LCOV - code coverage report
Current view: top level - test/core/util - port_posix.c (source / functions) Hit Total Coverage
Test: tmp.zDYK9MVh93 Lines: 107 174 61.5 %
Date: 2015-10-10 Functions: 9 11 81.8 %

          Line data    Source code
       1             : /*
       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_POSIX_SOCKET) && defined(GRPC_TEST_PICK_PORT)
      37             : 
      38             : #include "test/core/util/port.h"
      39             : 
      40             : #include <netinet/in.h>
      41             : #include <sys/socket.h>
      42             : #include <stdio.h>
      43             : #include <errno.h>
      44             : #include <string.h>
      45             : #include <unistd.h>
      46             : 
      47             : #include <grpc/grpc.h>
      48             : #include <grpc/support/alloc.h>
      49             : #include <grpc/support/log.h>
      50             : #include <grpc/support/string_util.h>
      51             : 
      52             : #include "src/core/httpcli/httpcli.h"
      53             : #include "src/core/support/env.h"
      54             : 
      55             : #define NUM_RANDOM_PORTS_TO_PICK 100
      56             : 
      57             : static int *chosen_ports = NULL;
      58             : static size_t num_chosen_ports = 0;
      59             : 
      60           0 : static int has_port_been_chosen(int port) {
      61             :   size_t i;
      62           0 :   for (i = 0; i < num_chosen_ports; i++) {
      63           0 :     if (chosen_ports[i] == port) {
      64           0 :       return 1;
      65             :     }
      66             :   }
      67           0 :   return 0;
      68             : }
      69             : 
      70             : typedef struct freereq {
      71             :   grpc_pollset pollset;
      72             :   int done;
      73             : } freereq;
      74             : 
      75        2902 : static void destroy_pollset_and_shutdown(grpc_exec_ctx *exec_ctx, void *p,
      76             :                                          int success) {
      77        2902 :   grpc_pollset_destroy(p);
      78        2902 :   grpc_shutdown();
      79        2902 : }
      80             : 
      81        1451 : static void freed_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
      82             :                                    const grpc_httpcli_response *response) {
      83        1451 :   freereq *pr = arg;
      84        1451 :   gpr_mu_lock(GRPC_POLLSET_MU(&pr->pollset));
      85        1451 :   pr->done = 1;
      86        1451 :   grpc_pollset_kick(&pr->pollset, NULL);
      87        1451 :   gpr_mu_unlock(GRPC_POLLSET_MU(&pr->pollset));
      88        1451 : }
      89             : 
      90        1451 : static void free_port_using_server(char *server, int port) {
      91             :   grpc_httpcli_context context;
      92             :   grpc_httpcli_request req;
      93             :   freereq pr;
      94             :   char *path;
      95        1451 :   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
      96             :   grpc_closure shutdown_closure;
      97             : 
      98        1451 :   grpc_init();
      99             : 
     100        1451 :   memset(&pr, 0, sizeof(pr));
     101        1451 :   memset(&req, 0, sizeof(req));
     102        1451 :   grpc_pollset_init(&pr.pollset);
     103        1451 :   grpc_closure_init(&shutdown_closure, destroy_pollset_and_shutdown,
     104             :                     &pr.pollset);
     105             : 
     106        1451 :   req.host = server;
     107        1451 :   gpr_asprintf(&path, "/drop/%d", port);
     108        1451 :   req.path = path;
     109             : 
     110        1451 :   grpc_httpcli_context_init(&context);
     111        1451 :   grpc_httpcli_get(&exec_ctx, &context, &pr.pollset, &req,
     112        1451 :                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), freed_port_from_server,
     113             :                    &pr);
     114        1451 :   gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
     115       18466 :   while (!pr.done) {
     116             :     grpc_pollset_worker worker;
     117       15564 :     grpc_pollset_work(&exec_ctx, &pr.pollset, &worker,
     118             :                       gpr_now(GPR_CLOCK_MONOTONIC),
     119       15564 :                       GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
     120             :   }
     121        1451 :   gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
     122             : 
     123        1451 :   grpc_httpcli_context_destroy(&context);
     124        1451 :   grpc_exec_ctx_finish(&exec_ctx);
     125        1451 :   grpc_pollset_shutdown(&exec_ctx, &pr.pollset, &shutdown_closure);
     126        1451 :   grpc_exec_ctx_finish(&exec_ctx);
     127        1451 :   gpr_free(path);
     128        1451 : }
     129             : 
     130         405 : static void free_chosen_ports() {
     131         405 :   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
     132         405 :   if (env != NULL) {
     133             :     size_t i;
     134        1856 :     for (i = 0; i < num_chosen_ports; i++) {
     135        1451 :       free_port_using_server(env, chosen_ports[i]);
     136             :     }
     137         405 :     gpr_free(env);
     138             :   }
     139             : 
     140         405 :   gpr_free(chosen_ports);
     141         405 : }
     142             : 
     143        1451 : static void chose_port(int port) {
     144        1451 :   if (chosen_ports == NULL) {
     145         405 :     atexit(free_chosen_ports);
     146             :   }
     147        1451 :   num_chosen_ports++;
     148        1451 :   chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
     149        1451 :   chosen_ports[num_chosen_ports - 1] = port;
     150        1451 : }
     151             : 
     152           0 : static int is_port_available(int *port, int is_tcp) {
     153           0 :   const int proto = is_tcp ? IPPROTO_TCP : 0;
     154           0 :   const int fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto);
     155           0 :   int one = 1;
     156             :   struct sockaddr_in addr;
     157           0 :   socklen_t alen = sizeof(addr);
     158             :   int actual_port;
     159             : 
     160           0 :   GPR_ASSERT(*port >= 0);
     161           0 :   GPR_ASSERT(*port <= 65535);
     162           0 :   if (fd < 0) {
     163           0 :     gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno));
     164           0 :     return 0;
     165             :   }
     166             : 
     167             :   /* Reuseaddr lets us start up a server immediately after it exits */
     168           0 :   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
     169           0 :     gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno));
     170           0 :     close(fd);
     171           0 :     return 0;
     172             :   }
     173             : 
     174             :   /* Try binding to port */
     175           0 :   addr.sin_family = AF_INET;
     176           0 :   addr.sin_addr.s_addr = INADDR_ANY;
     177           0 :   addr.sin_port = htons((gpr_uint16)*port);
     178           0 :   if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
     179           0 :     gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno));
     180           0 :     close(fd);
     181           0 :     return 0;
     182             :   }
     183             : 
     184             :   /* Get the bound port number */
     185           0 :   if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) {
     186           0 :     gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno));
     187           0 :     close(fd);
     188           0 :     return 0;
     189             :   }
     190           0 :   GPR_ASSERT(alen <= sizeof(addr));
     191           0 :   actual_port = ntohs(addr.sin_port);
     192           0 :   GPR_ASSERT(actual_port > 0);
     193           0 :   if (*port == 0) {
     194           0 :     *port = actual_port;
     195             :   } else {
     196           0 :     GPR_ASSERT(*port == actual_port);
     197             :   }
     198             : 
     199           0 :   close(fd);
     200           0 :   return 1;
     201             : }
     202             : 
     203             : typedef struct portreq {
     204             :   grpc_pollset pollset;
     205             :   int port;
     206             :   int retries;
     207             :   char *server;
     208             :   grpc_httpcli_context *ctx;
     209             : } portreq;
     210             : 
     211        1451 : static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
     212             :                                  const grpc_httpcli_response *response) {
     213             :   size_t i;
     214        1451 :   int port = 0;
     215        1451 :   portreq *pr = arg;
     216        1451 :   int failed = 0;
     217             : 
     218        1451 :   if (!response) {
     219           0 :     failed = 1;
     220           0 :     gpr_log(GPR_DEBUG,
     221             :             "failed port pick from server: retrying [response=NULL]");
     222        1451 :   } else if (response->status != 200) {
     223           0 :     failed = 1;
     224           0 :     gpr_log(GPR_DEBUG, "failed port pick from server: status=%d",
     225             :             response->status);
     226             :   }
     227             : 
     228        1451 :   if (failed) {
     229             :     grpc_httpcli_request req;
     230           0 :     memset(&req, 0, sizeof(req));
     231           0 :     GPR_ASSERT(pr->retries < 10);
     232           0 :     pr->retries++;
     233           0 :     req.host = pr->server;
     234           0 :     req.path = "/get";
     235           0 :     sleep(1);
     236           0 :     grpc_httpcli_get(exec_ctx, pr->ctx, &pr->pollset, &req,
     237           0 :                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
     238             :                      pr);
     239        1451 :     return;
     240             :   }
     241        1451 :   GPR_ASSERT(response);
     242        1451 :   GPR_ASSERT(response->status == 200);
     243        7255 :   for (i = 0; i < response->body_length; i++) {
     244        5804 :     GPR_ASSERT(response->body[i] >= '0' && response->body[i] <= '9');
     245        5804 :     port = port * 10 + response->body[i] - '0';
     246             :   }
     247        1451 :   GPR_ASSERT(port > 1024);
     248        1451 :   gpr_mu_lock(GRPC_POLLSET_MU(&pr->pollset));
     249        1451 :   pr->port = port;
     250        1451 :   grpc_pollset_kick(&pr->pollset, NULL);
     251        1451 :   gpr_mu_unlock(GRPC_POLLSET_MU(&pr->pollset));
     252             : }
     253             : 
     254        1451 : static int pick_port_using_server(char *server) {
     255             :   grpc_httpcli_context context;
     256             :   grpc_httpcli_request req;
     257             :   portreq pr;
     258        1451 :   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     259             :   grpc_closure shutdown_closure;
     260             : 
     261        1451 :   grpc_init();
     262             : 
     263        1451 :   memset(&pr, 0, sizeof(pr));
     264        1451 :   memset(&req, 0, sizeof(req));
     265        1451 :   grpc_pollset_init(&pr.pollset);
     266        1451 :   grpc_closure_init(&shutdown_closure, destroy_pollset_and_shutdown,
     267             :                     &pr.pollset);
     268        1451 :   pr.port = -1;
     269        1451 :   pr.server = server;
     270        1451 :   pr.ctx = &context;
     271             : 
     272        1451 :   req.host = server;
     273        1451 :   req.path = "/get";
     274             : 
     275        1451 :   grpc_httpcli_context_init(&context);
     276        1451 :   grpc_httpcli_get(&exec_ctx, &context, &pr.pollset, &req,
     277        1451 :                    GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
     278             :                    &pr);
     279        1451 :   grpc_exec_ctx_finish(&exec_ctx);
     280        1451 :   gpr_mu_lock(GRPC_POLLSET_MU(&pr.pollset));
     281       18969 :   while (pr.port == -1) {
     282             :     grpc_pollset_worker worker;
     283       16067 :     grpc_pollset_work(&exec_ctx, &pr.pollset, &worker,
     284             :                       gpr_now(GPR_CLOCK_MONOTONIC),
     285       16067 :                       GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
     286             :   }
     287        1451 :   gpr_mu_unlock(GRPC_POLLSET_MU(&pr.pollset));
     288             : 
     289        1451 :   grpc_httpcli_context_destroy(&context);
     290        1451 :   grpc_pollset_shutdown(&exec_ctx, &pr.pollset, &shutdown_closure);
     291        1451 :   grpc_exec_ctx_finish(&exec_ctx);
     292             : 
     293        1451 :   return pr.port;
     294             : }
     295             : 
     296        1451 : int grpc_pick_unused_port(void) {
     297             :   /* We repeatedly pick a port and then see whether or not it is
     298             :      available for use both as a TCP socket and a UDP socket.  First, we
     299             :      pick a random large port number.  For subsequent
     300             :      iterations, we bind to an anonymous port and let the OS pick the
     301             :      port number.  The random port picking reduces the probability of
     302             :      races with other processes on kernels that want to reuse the same
     303             :      port numbers over and over. */
     304             : 
     305             :   /* In alternating iterations we trial UDP ports before TCP ports UDP
     306             :      ports -- it could be the case that this machine has been using up
     307             :      UDP ports and they are scarcer. */
     308             : 
     309             :   /* Type of port to first pick in next iteration */
     310        1451 :   int is_tcp = 1;
     311        1451 :   int trial = 0;
     312             : 
     313        1451 :   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
     314        1451 :   if (env) {
     315        1451 :     int port = pick_port_using_server(env);
     316        1451 :     gpr_free(env);
     317        1451 :     if (port != 0) {
     318        1451 :       chose_port(port);
     319             :     }
     320        1451 :     return port;
     321             :   }
     322             : 
     323             :   for (;;) {
     324             :     int port;
     325           0 :     trial++;
     326           0 :     if (trial == 1) {
     327           0 :       port = getpid() % (65536 - 30000) + 30000;
     328           0 :     } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) {
     329           0 :       port = rand() % (65536 - 30000) + 30000;
     330             :     } else {
     331           0 :       port = 0;
     332             :     }
     333             : 
     334           0 :     if (has_port_been_chosen(port)) {
     335           0 :       continue;
     336             :     }
     337             : 
     338           0 :     if (!is_port_available(&port, is_tcp)) {
     339           0 :       continue;
     340             :     }
     341             : 
     342           0 :     GPR_ASSERT(port > 0);
     343             :     /* Check that the port # is free for the other type of socket also */
     344           0 :     if (!is_port_available(&port, !is_tcp)) {
     345             :       /* In the next iteration trial to bind to the other type first
     346             :          because perhaps it is more rare. */
     347           0 :       is_tcp = !is_tcp;
     348           0 :       continue;
     349             :     }
     350             : 
     351           0 :     chose_port(port);
     352           0 :     return port;
     353           0 :   }
     354             : 
     355             :   /* The port iterator reached the end without finding a suitable port. */
     356             :   return 0;
     357             : }
     358             : 
     359        1451 : int grpc_pick_unused_port_or_die(void) {
     360        1451 :   int port = grpc_pick_unused_port();
     361        1451 :   GPR_ASSERT(port > 0);
     362        1451 :   return port;
     363             : }
     364             : 
     365             : #endif /* GPR_POSIX_SOCKET && GRPC_TEST_PICK_PORT */

Generated by: LCOV version 1.10