blob: 3d2567757a8b34e4e7eb06110ecb30ddd1134573 [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 Tiller82f9bd82015-09-23 09:31:51 -0700154static void destroy_pollset_and_shutdown(grpc_exec_ctx *exec_ctx, void *p, int success) {
Craig Tillera82950e2015-09-22 12:33:20 -0700155 grpc_pollset_destroy(p);
156 grpc_shutdown();
Craig Tillerb0298592015-08-27 07:38:01 -0700157}
158
Craig Tillera82950e2015-09-22 12:33:20 -0700159static int pick_port_using_server(char *server) {
Craig Tillerb0298592015-08-27 07:38:01 -0700160 grpc_httpcli_context context;
161 grpc_httpcli_request req;
162 portreq pr;
Craig Tiller82f9bd82015-09-23 09:31:51 -0700163 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
164 grpc_closure destroy_pollset_closure;
Craig Tillerb0298592015-08-27 07:38:01 -0700165
Craig Tillera82950e2015-09-22 12:33:20 -0700166 grpc_init();
Craig Tillerb0298592015-08-27 07:38:01 -0700167
Craig Tillera82950e2015-09-22 12:33:20 -0700168 memset(&pr, 0, sizeof(pr));
169 memset(&req, 0, sizeof(req));
170 grpc_pollset_init(&pr.pollset);
Craig Tillerb0298592015-08-27 07:38:01 -0700171 pr.port = -1;
172
173 req.host = server;
174 req.path = "/get";
175
Craig Tillera82950e2015-09-22 12:33:20 -0700176 grpc_httpcli_context_init(&context);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700177 grpc_httpcli_get(&exec_ctx, &context, &pr.pollset, &req,
Craig Tillera82950e2015-09-22 12:33:20 -0700178 GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
179 &pr);
180 gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
181 while (pr.port == -1) {
182 grpc_pollset_worker worker;
Craig Tiller82f9bd82015-09-23 09:31:51 -0700183 grpc_pollset_work(&exec_ctx, &pr.pollset, &worker, gpr_now(GPR_CLOCK_MONOTONIC),
Craig Tillera82950e2015-09-22 12:33:20 -0700184 GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
Craig Tiller82f9bd82015-09-23 09:31:51 -0700185 gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
186 grpc_exec_ctx_flush(&exec_ctx);
187 gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
Craig Tillera82950e2015-09-22 12:33:20 -0700188 }
189 gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
Craig Tillerb0298592015-08-27 07:38:01 -0700190
Craig Tillera82950e2015-09-22 12:33:20 -0700191 grpc_httpcli_context_destroy(&context);
Craig Tiller82f9bd82015-09-23 09:31:51 -0700192 grpc_closure_init(&destroy_pollset_closure, destroy_pollset_and_shutdown, &pr.pollset);
193 grpc_pollset_shutdown(&exec_ctx, &pr.pollset, &destroy_pollset_closure);
Craig Tillerb0298592015-08-27 07:38:01 -0700194
Craig Tiller82f9bd82015-09-23 09:31:51 -0700195 grpc_exec_ctx_finish(&exec_ctx);
Craig Tillerb0298592015-08-27 07:38:01 -0700196 return pr.port;
197}
198
Craig Tillera82950e2015-09-22 12:33:20 -0700199int grpc_pick_unused_port(void) {
Craig Tiller34cf2f32015-04-08 16:10:00 -0700200 /* We repeatedly pick a port and then see whether or not it is
201 available for use both as a TCP socket and a UDP socket. First, we
202 pick a random large port number. For subsequent
203 iterations, we bind to an anonymous port and let the OS pick the
204 port number. The random port picking reduces the probability of
205 races with other processes on kernels that want to reuse the same
206 port numbers over and over. */
207
Craig Tillerb0298592015-08-27 07:38:01 -0700208 /* In alternating iterations we trial UDP ports before TCP ports UDP
Craig Tiller34cf2f32015-04-08 16:10:00 -0700209 ports -- it could be the case that this machine has been using up
210 UDP ports and they are scarcer. */
211
212 /* Type of port to first pick in next iteration */
213 int is_tcp = 1;
Craig Tillerb0298592015-08-27 07:38:01 -0700214 int trial = 0;
215
Craig Tillera82950e2015-09-22 12:33:20 -0700216 char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
217 if (env) {
218 int port = pick_port_using_server(env);
219 gpr_free(env);
220 if (port != 0) {
Craig Tillerb0298592015-08-27 07:38:01 -0700221 return port;
222 }
Craig Tillera82950e2015-09-22 12:33:20 -0700223 }
224
225 for (;;) {
226 int port;
227 trial++;
228 if (trial == 1) {
229 port = _getpid() % (65536 - 30000) + 30000;
230 } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) {
231 port = rand() % (65536 - 30000) + 30000;
232 } else {
233 port = 0;
234 }
235
236 if (has_port_been_chosen(port)) {
237 continue;
238 }
239
240 if (!is_port_available(&port, is_tcp)) {
241 continue;
242 }
243
244 GPR_ASSERT(port > 0);
245 /* Check that the port # is free for the other type of socket also */
246 if (!is_port_available(&port, !is_tcp)) {
247 /* In the next iteration trial to bind to the other type first
248 because perhaps it is more rare. */
249 is_tcp = !is_tcp;
250 continue;
251 }
252
253 /* TODO(ctiller): consider caching this port in some structure, to avoid
254 handing it out again */
255
256 chose_port(port);
257 return port;
258 }
Craig Tiller34cf2f32015-04-08 16:10:00 -0700259
260 /* The port iterator reached the end without finding a suitable port. */
261 return 0;
262}
263
Craig Tillera82950e2015-09-22 12:33:20 -0700264int grpc_pick_unused_port_or_die(void) {
265 int port = grpc_pick_unused_port();
266 GPR_ASSERT(port > 0);
Craig Tiller34cf2f32015-04-08 16:10:00 -0700267 return port;
268}
269
270#endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */