Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 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 "src/core/ext/transport/chttp2/server/chttp2_server.h" |
| 35 | |
| 36 | #include <grpc/grpc.h> |
| 37 | |
| 38 | #include <string.h> |
| 39 | |
| 40 | #include <grpc/support/alloc.h> |
| 41 | #include <grpc/support/log.h> |
| 42 | #include <grpc/support/string_util.h> |
| 43 | #include <grpc/support/sync.h> |
| 44 | #include <grpc/support/useful.h> |
| 45 | |
| 46 | #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" |
| 47 | #include "src/core/lib/channel/channel_args.h" |
| 48 | #include "src/core/lib/channel/handshaker.h" |
| 49 | #include "src/core/lib/channel/http_server_filter.h" |
| 50 | #include "src/core/lib/iomgr/endpoint.h" |
| 51 | #include "src/core/lib/iomgr/resolve_address.h" |
| 52 | #include "src/core/lib/iomgr/tcp_server.h" |
| 53 | #include "src/core/lib/surface/api_trace.h" |
| 54 | #include "src/core/lib/surface/server.h" |
| 55 | |
Mark D. Roth | 65b79c8 | 2016-12-06 07:20:20 -0800 | [diff] [blame] | 56 | void grpc_chttp2_server_handshaker_factory_add_handshakers( |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 57 | grpc_exec_ctx *exec_ctx, |
| 58 | grpc_chttp2_server_handshaker_factory *handshaker_factory, |
| 59 | grpc_handshake_manager *handshake_mgr) { |
| 60 | if (handshaker_factory != NULL) { |
Mark D. Roth | 65b79c8 | 2016-12-06 07:20:20 -0800 | [diff] [blame] | 61 | handshaker_factory->vtable->add_handshakers(exec_ctx, handshaker_factory, |
| 62 | handshake_mgr); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | |
| 66 | void grpc_chttp2_server_handshaker_factory_destroy( |
| 67 | grpc_exec_ctx *exec_ctx, |
| 68 | grpc_chttp2_server_handshaker_factory *handshaker_factory) { |
| 69 | if (handshaker_factory != NULL) { |
| 70 | handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory); |
| 71 | } |
| 72 | } |
| 73 | |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 74 | typedef struct pending_handshake_manager_node { |
| 75 | grpc_handshake_manager *handshake_mgr; |
| 76 | struct pending_handshake_manager_node *next; |
| 77 | } pending_handshake_manager_node; |
| 78 | |
| 79 | typedef struct { |
| 80 | grpc_server *server; |
| 81 | grpc_tcp_server *tcp_server; |
| 82 | grpc_channel_args *args; |
| 83 | grpc_chttp2_server_handshaker_factory *handshaker_factory; |
| 84 | gpr_mu mu; |
| 85 | bool shutdown; |
| 86 | grpc_closure tcp_server_shutdown_complete; |
| 87 | grpc_closure *server_destroy_listener_done; |
| 88 | pending_handshake_manager_node *pending_handshake_mgrs; |
| 89 | } server_state; |
| 90 | |
| 91 | typedef struct { |
| 92 | server_state *server_state; |
| 93 | grpc_pollset *accepting_pollset; |
| 94 | grpc_tcp_server_acceptor *acceptor; |
| 95 | grpc_handshake_manager *handshake_mgr; |
| 96 | } server_connection_state; |
| 97 | |
| 98 | static void pending_handshake_manager_add_locked( |
| 99 | server_state *state, grpc_handshake_manager *handshake_mgr) { |
| 100 | pending_handshake_manager_node *node = gpr_malloc(sizeof(*node)); |
| 101 | node->handshake_mgr = handshake_mgr; |
| 102 | node->next = state->pending_handshake_mgrs; |
| 103 | state->pending_handshake_mgrs = node; |
| 104 | } |
| 105 | |
| 106 | static void pending_handshake_manager_remove_locked( |
| 107 | server_state *state, grpc_handshake_manager *handshake_mgr) { |
| 108 | pending_handshake_manager_node **prev_node = &state->pending_handshake_mgrs; |
| 109 | for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
| 110 | node != NULL; node = node->next) { |
| 111 | if (node->handshake_mgr == handshake_mgr) { |
| 112 | *prev_node = node->next; |
| 113 | gpr_free(node); |
| 114 | break; |
| 115 | } |
| 116 | prev_node = &node->next; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | static void pending_handshake_manager_shutdown_locked(grpc_exec_ctx *exec_ctx, |
| 121 | server_state *state) { |
| 122 | pending_handshake_manager_node *prev_node = NULL; |
| 123 | for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
| 124 | node != NULL; node = node->next) { |
| 125 | grpc_handshake_manager_shutdown(exec_ctx, node->handshake_mgr); |
| 126 | gpr_free(prev_node); |
| 127 | prev_node = node; |
| 128 | } |
| 129 | gpr_free(prev_node); |
| 130 | state->pending_handshake_mgrs = NULL; |
| 131 | } |
| 132 | |
| 133 | static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, |
| 134 | grpc_error *error) { |
| 135 | grpc_handshaker_args *args = arg; |
| 136 | server_connection_state *connection_state = args->user_data; |
| 137 | gpr_mu_lock(&connection_state->server_state->mu); |
| 138 | if (error != GRPC_ERROR_NONE || connection_state->server_state->shutdown) { |
| 139 | const char *error_str = grpc_error_string(error); |
| 140 | gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str); |
| 141 | grpc_error_free_string(error_str); |
| 142 | if (error == GRPC_ERROR_NONE) { |
| 143 | // We were shut down after handshaking completed successfully, so |
| 144 | // destroy the endpoint here. |
| 145 | // TODO(ctiller): It is currently necessary to shutdown endpoints |
| 146 | // before destroying them, even if we know that there are no |
| 147 | // pending read/write callbacks. This should be fixed, at which |
| 148 | // point this can be removed. |
| 149 | grpc_endpoint_shutdown(exec_ctx, args->endpoint); |
| 150 | grpc_endpoint_destroy(exec_ctx, args->endpoint); |
| 151 | grpc_channel_args_destroy(args->args); |
| 152 | grpc_slice_buffer_destroy(args->read_buffer); |
| 153 | gpr_free(args->read_buffer); |
| 154 | } |
| 155 | } else { |
| 156 | grpc_transport *transport = |
| 157 | grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0); |
| 158 | grpc_server_setup_transport( |
| 159 | exec_ctx, connection_state->server_state->server, transport, |
| 160 | connection_state->accepting_pollset, args->args); |
| 161 | grpc_chttp2_transport_start_reading(exec_ctx, transport, args->read_buffer); |
| 162 | grpc_channel_args_destroy(args->args); |
| 163 | } |
| 164 | pending_handshake_manager_remove_locked(connection_state->server_state, |
| 165 | connection_state->handshake_mgr); |
| 166 | gpr_mu_unlock(&connection_state->server_state->mu); |
| 167 | grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr); |
| 168 | grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp_server); |
Mark D. Roth | eed3815 | 2016-12-08 13:59:13 -0800 | [diff] [blame] | 169 | gpr_free(connection_state->acceptor); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 170 | gpr_free(connection_state); |
| 171 | } |
| 172 | |
| 173 | static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, |
| 174 | grpc_pollset *accepting_pollset, |
| 175 | grpc_tcp_server_acceptor *acceptor) { |
| 176 | server_state *state = arg; |
| 177 | gpr_mu_lock(&state->mu); |
| 178 | if (state->shutdown) { |
| 179 | gpr_mu_unlock(&state->mu); |
| 180 | grpc_endpoint_destroy(exec_ctx, tcp); |
Mark D. Roth | 96ba68d | 2016-12-09 17:21:26 +0000 | [diff] [blame^] | 181 | gpr_free(acceptor); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 182 | return; |
| 183 | } |
| 184 | grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create(); |
| 185 | pending_handshake_manager_add_locked(state, handshake_mgr); |
| 186 | gpr_mu_unlock(&state->mu); |
| 187 | grpc_tcp_server_ref(state->tcp_server); |
| 188 | server_connection_state *connection_state = |
| 189 | gpr_malloc(sizeof(*connection_state)); |
| 190 | connection_state->server_state = state; |
| 191 | connection_state->accepting_pollset = accepting_pollset; |
| 192 | connection_state->acceptor = acceptor; |
| 193 | connection_state->handshake_mgr = handshake_mgr; |
Mark D. Roth | 65b79c8 | 2016-12-06 07:20:20 -0800 | [diff] [blame] | 194 | grpc_chttp2_server_handshaker_factory_add_handshakers( |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 195 | exec_ctx, state->handshaker_factory, connection_state->handshake_mgr); |
| 196 | // TODO(roth): We should really get this timeout value from channel |
| 197 | // args instead of hard-coding it. |
| 198 | const gpr_timespec deadline = gpr_time_add( |
| 199 | gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN)); |
Craig Tiller | 73122ba | 2016-12-05 08:16:58 -0800 | [diff] [blame] | 200 | grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr, |
| 201 | tcp, state->args, deadline, acceptor, |
| 202 | on_handshake_done, connection_state); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | /* Server callback: start listening on our ports */ |
| 206 | static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server, |
| 207 | void *arg, grpc_pollset **pollsets, |
| 208 | size_t pollset_count) { |
| 209 | server_state *state = arg; |
| 210 | gpr_mu_lock(&state->mu); |
| 211 | state->shutdown = false; |
| 212 | gpr_mu_unlock(&state->mu); |
| 213 | grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count, |
| 214 | on_accept, state); |
| 215 | } |
| 216 | |
| 217 | static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg, |
| 218 | grpc_error *error) { |
| 219 | server_state *state = arg; |
| 220 | /* ensure all threads have unlocked */ |
| 221 | gpr_mu_lock(&state->mu); |
| 222 | grpc_closure *destroy_done = state->server_destroy_listener_done; |
| 223 | GPR_ASSERT(state->shutdown); |
| 224 | pending_handshake_manager_shutdown_locked(exec_ctx, state); |
| 225 | gpr_mu_unlock(&state->mu); |
| 226 | // Flush queued work before destroying handshaker factory, since that |
| 227 | // may do a synchronous unref. |
| 228 | grpc_exec_ctx_flush(exec_ctx); |
| 229 | grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, |
| 230 | state->handshaker_factory); |
| 231 | if (destroy_done != NULL) { |
| 232 | destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error)); |
| 233 | grpc_exec_ctx_flush(exec_ctx); |
| 234 | } |
| 235 | grpc_channel_args_destroy(state->args); |
| 236 | gpr_mu_destroy(&state->mu); |
| 237 | gpr_free(state); |
| 238 | } |
| 239 | |
| 240 | /* Server callback: destroy the tcp listener (so we don't generate further |
| 241 | callbacks) */ |
| 242 | static void server_destroy_listener(grpc_exec_ctx *exec_ctx, |
| 243 | grpc_server *server, void *arg, |
| 244 | grpc_closure *destroy_done) { |
| 245 | server_state *state = arg; |
| 246 | gpr_mu_lock(&state->mu); |
| 247 | state->shutdown = true; |
| 248 | state->server_destroy_listener_done = destroy_done; |
| 249 | grpc_tcp_server *tcp_server = state->tcp_server; |
| 250 | gpr_mu_unlock(&state->mu); |
| 251 | grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server); |
| 252 | grpc_tcp_server_unref(exec_ctx, tcp_server); |
| 253 | } |
| 254 | |
| 255 | grpc_error *grpc_chttp2_server_add_port( |
| 256 | grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, |
| 257 | grpc_channel_args *args, |
| 258 | grpc_chttp2_server_handshaker_factory *handshaker_factory, int *port_num) { |
| 259 | grpc_resolved_addresses *resolved = NULL; |
| 260 | grpc_tcp_server *tcp_server = NULL; |
| 261 | size_t i; |
| 262 | size_t count = 0; |
| 263 | int port_temp; |
| 264 | grpc_error *err = GRPC_ERROR_NONE; |
| 265 | server_state *state = NULL; |
Mark D. Roth | a0bcfbb | 2016-12-02 12:10:25 -0800 | [diff] [blame] | 266 | grpc_error **errors = NULL; |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 267 | |
| 268 | *port_num = -1; |
| 269 | |
| 270 | /* resolve address */ |
| 271 | err = grpc_blocking_resolve_address(addr, "https", &resolved); |
| 272 | if (err != GRPC_ERROR_NONE) { |
| 273 | goto error; |
| 274 | } |
| 275 | state = gpr_malloc(sizeof(*state)); |
| 276 | memset(state, 0, sizeof(*state)); |
| 277 | grpc_closure_init(&state->tcp_server_shutdown_complete, |
| 278 | tcp_server_shutdown_complete, state); |
Craig Tiller | 73122ba | 2016-12-05 08:16:58 -0800 | [diff] [blame] | 279 | err = grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete, |
| 280 | args, &tcp_server); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 281 | if (err != GRPC_ERROR_NONE) { |
| 282 | goto error; |
| 283 | } |
| 284 | |
| 285 | state->server = server; |
| 286 | state->tcp_server = tcp_server; |
| 287 | state->args = args; |
| 288 | state->handshaker_factory = handshaker_factory; |
| 289 | state->shutdown = true; |
| 290 | gpr_mu_init(&state->mu); |
| 291 | |
| 292 | const size_t naddrs = resolved->naddrs; |
Mark D. Roth | a0bcfbb | 2016-12-02 12:10:25 -0800 | [diff] [blame] | 293 | errors = gpr_malloc(sizeof(*errors) * naddrs); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 294 | for (i = 0; i < naddrs; i++) { |
| 295 | errors[i] = |
| 296 | grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); |
| 297 | if (errors[i] == GRPC_ERROR_NONE) { |
| 298 | if (*port_num == -1) { |
| 299 | *port_num = port_temp; |
| 300 | } else { |
| 301 | GPR_ASSERT(*port_num == port_temp); |
| 302 | } |
| 303 | count++; |
| 304 | } |
| 305 | } |
| 306 | if (count == 0) { |
| 307 | char *msg; |
| 308 | gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", |
| 309 | naddrs); |
| 310 | err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
| 311 | gpr_free(msg); |
| 312 | goto error; |
| 313 | } else if (count != naddrs) { |
| 314 | char *msg; |
| 315 | gpr_asprintf(&msg, "Only %" PRIuPTR |
| 316 | " addresses added out of total %" PRIuPTR " resolved", |
| 317 | count, naddrs); |
| 318 | err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
| 319 | gpr_free(msg); |
| 320 | |
| 321 | const char *warning_message = grpc_error_string(err); |
| 322 | gpr_log(GPR_INFO, "WARNING: %s", warning_message); |
| 323 | grpc_error_free_string(warning_message); |
| 324 | /* we managed to bind some addresses: continue */ |
| 325 | } |
| 326 | grpc_resolved_addresses_destroy(resolved); |
| 327 | |
| 328 | /* Register with the server only upon success */ |
| 329 | grpc_server_add_listener(exec_ctx, server, state, server_start_listener, |
| 330 | server_destroy_listener); |
| 331 | goto done; |
| 332 | |
| 333 | /* Error path: cleanup and return */ |
| 334 | error: |
| 335 | GPR_ASSERT(err != GRPC_ERROR_NONE); |
| 336 | if (resolved) { |
| 337 | grpc_resolved_addresses_destroy(resolved); |
| 338 | } |
| 339 | if (tcp_server) { |
| 340 | grpc_tcp_server_unref(exec_ctx, tcp_server); |
Mark D. Roth | 0f4bbba | 2016-12-02 22:16:03 +0000 | [diff] [blame] | 341 | } else { |
Mark D. Roth | a0bb374 | 2016-12-02 22:27:26 +0000 | [diff] [blame] | 342 | grpc_channel_args_destroy(args); |
| 343 | grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, handshaker_factory); |
Mark D. Roth | 0f4bbba | 2016-12-02 22:16:03 +0000 | [diff] [blame] | 344 | gpr_free(state); |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 345 | } |
Mark D. Roth | 7140382 | 2016-12-02 10:51:39 -0800 | [diff] [blame] | 346 | *port_num = 0; |
| 347 | |
| 348 | done: |
| 349 | if (errors != NULL) { |
| 350 | for (i = 0; i < naddrs; i++) { |
| 351 | GRPC_ERROR_UNREF(errors[i]); |
| 352 | } |
| 353 | gpr_free(errors); |
| 354 | } |
| 355 | return err; |
| 356 | } |