blob: 60ad9b4f2ae70f847bf509610a2683ef57a41f6c [file] [log] [blame]
Craig Tiller34cf2f32015-04-08 16:10:00 -07001/*
2 *
3 * Copyright 2015, Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <grpc/support/port_platform.h>
35#include "test/core/util/test_config.h"
36#if defined(GPR_WINSOCK_SOCKET) && defined(GRPC_TEST_PICK_PORT)
37
38#include "test/core/util/port.h"
39
Craig Tillerf7262052015-04-08 16:13:58 -070040#include <process.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070041#include <stdio.h>
42#include <errno.h>
43#include <string.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070044
Craig Tillerb0298592015-08-27 07:38:01 -070045#include <grpc/grpc.h>
46#include <grpc/support/alloc.h>
Craig Tiller34cf2f32015-04-08 16:10:00 -070047#include <grpc/support/log.h>
48
Craig Tillerb0298592015-08-27 07:38:01 -070049#include "src/core/support/env.h"
50#include "src/core/httpcli/httpcli.h"
51#include "src/core/iomgr/sockaddr_utils.h"
52
Craig Tiller34cf2f32015-04-08 16:10:00 -070053#define NUM_RANDOM_PORTS_TO_PICK 100
54
Craig Tiller4326f732015-09-02 12:36:25 -070055static int *chosen_ports = NULL;
56static size_t num_chosen_ports = 0;
57
Craig Tillera82950e2015-09-22 12:33:20 -070058static int has_port_been_chosen(int port) {
Craig Tiller4326f732015-09-02 12:36:25 -070059 size_t i;
Craig Tillera82950e2015-09-22 12:33:20 -070060 for (i = 0; i < num_chosen_ports; i++) {
61 if (chosen_ports[i] == port) {
62 return 1;
Craig Tiller4326f732015-09-02 12:36:25 -070063 }
Craig Tillera82950e2015-09-22 12:33:20 -070064 }
Craig Tiller4326f732015-09-02 12:36:25 -070065 return 0;
66}
67
Craig Tiller82f9bd82015-09-23 09:31:51 -070068static void free_chosen_ports(void) { gpr_free(chosen_ports); }
Craig Tiller4326f732015-09-02 12:36:25 -070069
Craig Tillera82950e2015-09-22 12:33:20 -070070static void chose_port(int port) {
71 if (chosen_ports == NULL) {
72 atexit(free_chosen_ports);
73 }
Craig Tiller4326f732015-09-02 12:36:25 -070074 num_chosen_ports++;
Craig Tillera82950e2015-09-22 12:33:20 -070075 chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
Craig Tiller4326f732015-09-02 12:36:25 -070076 chosen_ports[num_chosen_ports - 1] = port;
77}
78
Craig Tillera82950e2015-09-22 12:33:20 -070079static int is_port_available(int *port, int is_tcp) {
Craig Tiller34cf2f32015-04-08 16:10:00 -070080 const int proto = is_tcp ? IPPROTO_TCP : 0;
Craig Tillera82950e2015-09-22 12:33:20 -070081 const SOCKET fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
Craig Tiller34cf2f32015-04-08 16:10:00 -070082 int one = 1;
83 struct sockaddr_in addr;
Craig Tillera82950e2015-09-22 12:33:20 -070084 socklen_t alen = sizeof(addr);
Craig Tiller34cf2f32015-04-08 16:10:00 -070085 int actual_port;
86
Craig Tillera82950e2015-09-22 12:33:20 -070087 GPR_ASSERT(*port >= 0);
88 GPR_ASSERT(*port <= 65535);
89 if (INVALID_SOCKET == fd) {
90 gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
91 return 0;
92 }
Craig Tiller34cf2f32015-04-08 16:10:00 -070093
94 /* Reuseaddr lets us start up a server immediately after it exits */
Craig Tillera82950e2015-09-22 12:33:20 -070095 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
96 sizeof(one)) < 0) {
97 gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
98 closesocket(fd);
99 return 0;
100 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700101
102 /* Try binding to port */
103 addr.sin_family = AF_INET;
104 addr.sin_addr.s_addr = INADDR_ANY;
Craig Tillera82950e2015-09-22 12:33:20 -0700105 addr.sin_port = htons(*port);
106 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
107 gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
108 closesocket(fd);
109 return 0;
110 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700111
112 /* Get the bound port number */
Craig Tillera82950e2015-09-22 12:33:20 -0700113 if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
114 gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
115 closesocket(fd);
116 return 0;
117 }
118 GPR_ASSERT(alen <= sizeof(addr));
119 actual_port = ntohs(addr.sin_port);
120 GPR_ASSERT(actual_port > 0);
121 if (*port == 0) {
122 *port = actual_port;
123 } else {
124 GPR_ASSERT(*port == actual_port);
125 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700126
Craig Tillera82950e2015-09-22 12:33:20 -0700127 closesocket(fd);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700128 return 1;
129}
130
Craig Tillera82950e2015-09-22 12:33:20 -0700131typedef struct portreq {
Craig Tillerb0298592015-08-27 07:38:01 -0700132 grpc_pollset pollset;
133 int port;
134} portreq;
135
Craig Tiller82f9bd82015-09-23 09:31:51 -0700136static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
Craig Tillera82950e2015-09-22 12:33:20 -0700137 const grpc_httpcli_response *response) {
Craig Tillerb0298592015-08-27 07:38:01 -0700138 size_t i;
139 int port = 0;
140 portreq *pr = arg;
Craig Tillera82950e2015-09-22 12:33:20 -0700141 GPR_ASSERT(response);
142 GPR_ASSERT(response->status == 200);
143 for (i = 0; i < response->body_length; i++) {
144 GPR_ASSERT(response->body[i] >= '0' && response->body[i] <= '9');
145 port = port * 10 + response->body[i] - '0';
146 }
147 GPR_ASSERT(port > 1024);
148 gpr_mu_lock(GRPC_POLLSET_MU(&pr->pollset));
Craig Tillerb0298592015-08-27 07:38:01 -0700149 pr->port = port;
Craig Tillera82950e2015-09-22 12:33:20 -0700150 grpc_pollset_kick(&pr->pollset, NULL);
151 gpr_mu_unlock(GRPC_POLLSET_MU(&pr->pollset));
Craig Tillerb0298592015-08-27 07:38:01 -0700152}
153
Craig Tiller565b18b2015-09-23 10:09:42 -0700154static void destroy_pollset_and_shutdown(grpc_exec_ctx *exec_ctx, void *p,
155 int success) {
Craig Tillera82950e2015-09-22 12:33:20 -0700156 grpc_pollset_destroy(p);
157 grpc_shutdown();
Craig Tillerb0298592015-08-27 07:38:01 -0700158}
159
Craig Tillera82950e2015-09-22 12:33:20 -0700160static int pick_port_using_server(char *server) {
Craig Tillerb0298592015-08-27 07:38:01 -0700161 grpc_httpcli_context context;
162 grpc_httpcli_request req;
163 portreq pr;
Craig Tiller82f9bd82015-09-23 09:31:51 -0700164 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
165 grpc_closure destroy_pollset_closure;
Craig Tillerb0298592015-08-27 07:38:01 -0700166
Craig Tillera82950e2015-09-22 12:33:20 -0700167 grpc_init();
Craig Tillerb0298592015-08-27 07:38:01 -0700168
Craig Tillera82950e2015-09-22 12:33:20 -0700169 memset(&pr, 0, sizeof(pr));
170 memset(&req, 0, sizeof(req));
171 grpc_pollset_init(&pr.pollset);
Craig Tillerb0298592015-08-27 07:38:01 -0700172 pr.port = -1;
173
174 req.host = server;
175 req.path = "/get";
176
Craig Tillera82950e2015-09-22 12:33:20 -0700177 grpc_httpcli_context_init(&context);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700178 grpc_httpcli_get(&exec_ctx, &context, &pr.pollset, &req,
Craig Tillera82950e2015-09-22 12:33:20 -0700179 GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
180 &pr);
181 gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
182 while (pr.port == -1) {
183 grpc_pollset_worker worker;
Craig Tiller565b18b2015-09-23 10:09:42 -0700184 grpc_pollset_work(&exec_ctx, &pr.pollset, &worker,
185 gpr_now(GPR_CLOCK_MONOTONIC),
Craig Tillera82950e2015-09-22 12:33:20 -0700186 GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700187 gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
188 grpc_exec_ctx_flush(&exec_ctx);
189 gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
Craig Tillera82950e2015-09-22 12:33:20 -0700190 }
191 gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
Craig Tillerb0298592015-08-27 07:38:01 -0700192
Craig Tillera82950e2015-09-22 12:33:20 -0700193 grpc_httpcli_context_destroy(&context);
Craig Tiller565b18b2015-09-23 10:09:42 -0700194 grpc_closure_init(&destroy_pollset_closure, destroy_pollset_and_shutdown,
195 &pr.pollset);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700196 grpc_pollset_shutdown(&exec_ctx, &pr.pollset, &destroy_pollset_closure);
Craig Tillerb0298592015-08-27 07:38:01 -0700197
Craig Tiller82f9bd82015-09-23 09:31:51 -0700198 grpc_exec_ctx_finish(&exec_ctx);
Craig Tillerb0298592015-08-27 07:38:01 -0700199 return pr.port;
200}
201
Craig Tillera82950e2015-09-22 12:33:20 -0700202int grpc_pick_unused_port(void) {
Craig Tiller34cf2f32015-04-08 16:10:00 -0700203 /* We repeatedly pick a port and then see whether or not it is
204 available for use both as a TCP socket and a UDP socket. First, we
205 pick a random large port number. For subsequent
206 iterations, we bind to an anonymous port and let the OS pick the
207 port number. The random port picking reduces the probability of
208 races with other processes on kernels that want to reuse the same
209 port numbers over and over. */
210
Craig Tillerb0298592015-08-27 07:38:01 -0700211 /* In alternating iterations we trial UDP ports before TCP ports UDP
Craig Tiller34cf2f32015-04-08 16:10:00 -0700212 ports -- it could be the case that this machine has been using up
213 UDP ports and they are scarcer. */
214
215 /* Type of port to first pick in next iteration */
216 int is_tcp = 1;
Craig Tillerb0298592015-08-27 07:38:01 -0700217 int trial = 0;
218
Craig Tillera82950e2015-09-22 12:33:20 -0700219 char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
220 if (env) {
221 int port = pick_port_using_server(env);
222 gpr_free(env);
223 if (port != 0) {
Craig Tillerb0298592015-08-27 07:38:01 -0700224 return port;
225 }
Craig Tillera82950e2015-09-22 12:33:20 -0700226 }
227
228 for (;;) {
229 int port;
230 trial++;
231 if (trial == 1) {
232 port = _getpid() % (65536 - 30000) + 30000;
233 } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) {
234 port = rand() % (65536 - 30000) + 30000;
235 } else {
236 port = 0;
237 }
238
239 if (has_port_been_chosen(port)) {
240 continue;
241 }
242
243 if (!is_port_available(&port, is_tcp)) {
244 continue;
245 }
246
247 GPR_ASSERT(port > 0);
248 /* Check that the port # is free for the other type of socket also */
249 if (!is_port_available(&port, !is_tcp)) {
250 /* In the next iteration trial to bind to the other type first
251 because perhaps it is more rare. */
252 is_tcp = !is_tcp;
253 continue;
254 }
255
256 /* TODO(ctiller): consider caching this port in some structure, to avoid
257 handing it out again */
258
259 chose_port(port);
260 return port;
261 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700262
263 /* The port iterator reached the end without finding a suitable port. */
264 return 0;
265}
266
Craig Tillera82950e2015-09-22 12:33:20 -0700267int grpc_pick_unused_port_or_die(void) {
268 int port = grpc_pick_unused_port();
269 GPR_ASSERT(port > 0);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700270 return port;
271}
272
273#endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */