commit
c55236f49b
511 changed files with 14927 additions and 6072 deletions
@ -0,0 +1,194 @@ |
||||
Negative HTTP/2 Interop Test Case Descriptions |
||||
======================================= |
||||
|
||||
Client and server use |
||||
[test.proto](../src/proto/grpc/testing/test.proto). |
||||
|
||||
Server |
||||
------ |
||||
The code for the custom http2 server can be found |
||||
[here](https://github.com/grpc/grpc/tree/master/test/http2_test). |
||||
It is responsible for handling requests and sending responses, and also for |
||||
fulfilling the behavior of each particular test case. |
||||
|
||||
Server should accept these arguments: |
||||
* --port=PORT |
||||
* The port the server will run on. For example, "8080" |
||||
* --test_case=TESTCASE |
||||
* The name of the test case to execute. For example, "goaway" |
||||
|
||||
Client |
||||
------ |
||||
|
||||
Clients implement test cases that test certain functionality. Each client is |
||||
provided the test case it is expected to run as a command-line parameter. Names |
||||
should be lowercase and without spaces. |
||||
|
||||
Clients should accept these arguments: |
||||
* --server_host=HOSTNAME |
||||
* The server host to connect to. For example, "localhost" or "127.0.0.1" |
||||
* --server_port=PORT |
||||
* The server port to connect to. For example, "8080" |
||||
* --test_case=TESTCASE |
||||
* The name of the test case to execute. For example, "goaway" |
||||
|
||||
Note |
||||
----- |
||||
|
||||
Note that the server and client must be invoked with the same test case or else |
||||
the test will be meaningless. For convenience, we provide a shell script wrapper |
||||
that invokes both server and client at the same time, with the same test_case. |
||||
This is the preferred way to run these tests. |
||||
|
||||
## Test Cases |
||||
|
||||
### goaway |
||||
|
||||
This test verifies that the client correctly responds to a goaway sent by the |
||||
server. The client should handle the goaway by switching to a new stream without |
||||
the user application having to do a thing. |
||||
|
||||
Client Procedure: |
||||
1. Client sends two UnaryCall requests with: |
||||
|
||||
``` |
||||
{ |
||||
response_size: 314159 |
||||
payload:{ |
||||
body: 271828 bytes of zeros |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Client asserts: |
||||
* Call was successful. |
||||
* Response payload body is 314159 bytes in size. |
||||
|
||||
Server Procedure: |
||||
1. Server sends a GOAWAY after receiving the first UnaryCall. |
||||
|
||||
Server asserts: |
||||
* The second UnaryCall has a different stream_id than the first one. |
||||
|
||||
### rst_after_header |
||||
|
||||
This test verifies that the client fails correctly when the server sends a |
||||
RST_STREAM immediately after sending headers to the client. |
||||
|
||||
Procedure: |
||||
1. Client sends UnaryCall with: |
||||
|
||||
``` |
||||
{ |
||||
response_size: 314159 |
||||
payload:{ |
||||
body: 271828 bytes of zeros |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Client asserts: |
||||
* Call was not successful. |
||||
|
||||
Server Procedure: |
||||
1. Server sends a RST_STREAM with error code 0 after sending headers to the client. |
||||
|
||||
*At the moment the error code and message returned are not standardized throughout all |
||||
languages. Those checks will be added once all client languages behave the same way. [#9142](https://github.com/grpc/grpc/issues/9142) is in flight.* |
||||
|
||||
### rst_during_data |
||||
|
||||
This test verifies that the client fails "correctly" when the server sends a |
||||
RST_STREAM halfway through sending data to the client. |
||||
|
||||
Procedure: |
||||
1. Client sends UnaryCall with: |
||||
|
||||
``` |
||||
{ |
||||
response_size: 314159 |
||||
payload:{ |
||||
body: 271828 bytes of zeros |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Client asserts: |
||||
* Call was not successful. |
||||
|
||||
Server Procedure: |
||||
1. Server sends a RST_STREAM with error code 0 after sending half of |
||||
the requested data to the client. |
||||
|
||||
### rst_after_data |
||||
|
||||
This test verifies that the client fails "correctly" when the server sends a |
||||
RST_STREAM after sending all of the data to the client. |
||||
|
||||
Procedure: |
||||
1. Client sends UnaryCall with: |
||||
|
||||
``` |
||||
{ |
||||
response_size: 314159 |
||||
payload:{ |
||||
body: 271828 bytes of zeros |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Client asserts: |
||||
* Call was not successful. |
||||
|
||||
Server Procedure: |
||||
1. Server sends a RST_STREAM with error code 0 after sending all of the |
||||
data to the client. |
||||
|
||||
*Certain client languages allow the data to be accessed even though a RST_STREAM |
||||
was encountered. Once all client languages behave this way, checks will be added on |
||||
the incoming data.* |
||||
|
||||
### ping |
||||
|
||||
This test verifies that the client correctly acknowledges all pings it gets from the |
||||
server. |
||||
|
||||
Procedure: |
||||
1. Client sends UnaryCall with: |
||||
|
||||
``` |
||||
{ |
||||
response_size: 314159 |
||||
payload:{ |
||||
body: 271828 bytes of zeros |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Client asserts: |
||||
* call was successful. |
||||
* response payload body is 314159 bytes in size. |
||||
|
||||
Server Procedure: |
||||
1. Server tracks the number of outstanding pings (i.e. +1 when it sends a ping, and -1 |
||||
when it receives an ack from the client). |
||||
2. Server sends pings before and after sending headers, also before and after sending data. |
||||
|
||||
Server Asserts: |
||||
* Number of outstanding pings is 0 when the connection is lost. |
||||
|
||||
### max_streams |
||||
|
||||
This test verifies that the client observes the MAX_CONCURRENT_STREAMS limit set by the server. |
||||
|
||||
Client Procedure: |
||||
1. Client sends initial UnaryCall to allow the server to update its MAX_CONCURRENT_STREAMS settings. |
||||
2. Client concurrently sends 10 UnaryCalls. |
||||
|
||||
Client Asserts: |
||||
* All UnaryCalls were successful, and had the correct type and payload size. |
||||
|
||||
Server Procedure: |
||||
1. Sets MAX_CONCURRENT_STREAMS to one after the connection is made. |
||||
|
||||
*The assertion that the MAX_CONCURRENT_STREAMS limit is upheld occurs in the http2 library we used.* |
@ -0,0 +1,47 @@ |
||||
import grpc |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.interfaces.face import utilities as face_utilities |
||||
|
||||
import helloworld_pb2 as helloworld__pb2 |
||||
|
||||
|
||||
class GreeterStub(object): |
||||
"""The greeting service definition. |
||||
""" |
||||
|
||||
def __init__(self, channel): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
channel: A grpc.Channel. |
||||
""" |
||||
self.SayHello = channel.unary_unary( |
||||
'/helloworld.Greeter/SayHello', |
||||
request_serializer=helloworld__pb2.HelloRequest.SerializeToString, |
||||
response_deserializer=helloworld__pb2.HelloReply.FromString, |
||||
) |
||||
|
||||
|
||||
class GreeterServicer(object): |
||||
"""The greeting service definition. |
||||
""" |
||||
|
||||
def SayHello(self, request, context): |
||||
"""Sends a greeting |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
|
||||
def add_GreeterServicer_to_server(servicer, server): |
||||
rpc_method_handlers = { |
||||
'SayHello': grpc.unary_unary_rpc_method_handler( |
||||
servicer.SayHello, |
||||
request_deserializer=helloworld__pb2.HelloRequest.FromString, |
||||
response_serializer=helloworld__pb2.HelloReply.SerializeToString, |
||||
), |
||||
} |
||||
generic_handler = grpc.method_handlers_generic_handler( |
||||
'helloworld.Greeter', rpc_method_handlers) |
||||
server.add_generic_rpc_handlers((generic_handler,)) |
@ -0,0 +1,47 @@ |
||||
import grpc |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.interfaces.face import utilities as face_utilities |
||||
|
||||
import helloworld_pb2 as helloworld__pb2 |
||||
|
||||
|
||||
class GreeterStub(object): |
||||
"""The greeting service definition. |
||||
""" |
||||
|
||||
def __init__(self, channel): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
channel: A grpc.Channel. |
||||
""" |
||||
self.SayHello = channel.unary_unary( |
||||
'/helloworld.Greeter/SayHello', |
||||
request_serializer=helloworld__pb2.HelloRequest.SerializeToString, |
||||
response_deserializer=helloworld__pb2.HelloReply.FromString, |
||||
) |
||||
|
||||
|
||||
class GreeterServicer(object): |
||||
"""The greeting service definition. |
||||
""" |
||||
|
||||
def SayHello(self, request, context): |
||||
"""Sends a greeting |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
|
||||
def add_GreeterServicer_to_server(servicer, server): |
||||
rpc_method_handlers = { |
||||
'SayHello': grpc.unary_unary_rpc_method_handler( |
||||
servicer.SayHello, |
||||
request_deserializer=helloworld__pb2.HelloRequest.FromString, |
||||
response_serializer=helloworld__pb2.HelloReply.SerializeToString, |
||||
), |
||||
} |
||||
generic_handler = grpc.method_handlers_generic_handler( |
||||
'helloworld.Greeter', rpc_method_handlers) |
||||
server.add_generic_rpc_handlers((generic_handler,)) |
@ -0,0 +1,114 @@ |
||||
import grpc |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.interfaces.face import utilities as face_utilities |
||||
|
||||
import route_guide_pb2 as route__guide__pb2 |
||||
|
||||
|
||||
class RouteGuideStub(object): |
||||
"""Interface exported by the server. |
||||
""" |
||||
|
||||
def __init__(self, channel): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
channel: A grpc.Channel. |
||||
""" |
||||
self.GetFeature = channel.unary_unary( |
||||
'/routeguide.RouteGuide/GetFeature', |
||||
request_serializer=route__guide__pb2.Point.SerializeToString, |
||||
response_deserializer=route__guide__pb2.Feature.FromString, |
||||
) |
||||
self.ListFeatures = channel.unary_stream( |
||||
'/routeguide.RouteGuide/ListFeatures', |
||||
request_serializer=route__guide__pb2.Rectangle.SerializeToString, |
||||
response_deserializer=route__guide__pb2.Feature.FromString, |
||||
) |
||||
self.RecordRoute = channel.stream_unary( |
||||
'/routeguide.RouteGuide/RecordRoute', |
||||
request_serializer=route__guide__pb2.Point.SerializeToString, |
||||
response_deserializer=route__guide__pb2.RouteSummary.FromString, |
||||
) |
||||
self.RouteChat = channel.stream_stream( |
||||
'/routeguide.RouteGuide/RouteChat', |
||||
request_serializer=route__guide__pb2.RouteNote.SerializeToString, |
||||
response_deserializer=route__guide__pb2.RouteNote.FromString, |
||||
) |
||||
|
||||
|
||||
class RouteGuideServicer(object): |
||||
"""Interface exported by the server. |
||||
""" |
||||
|
||||
def GetFeature(self, request, context): |
||||
"""A simple RPC. |
||||
|
||||
Obtains the feature at a given position. |
||||
|
||||
A feature with an empty name is returned if there's no feature at the given |
||||
position. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def ListFeatures(self, request, context): |
||||
"""A server-to-client streaming RPC. |
||||
|
||||
Obtains the Features available within the given Rectangle. Results are |
||||
streamed rather than returned at once (e.g. in a response message with a |
||||
repeated field), as the rectangle may cover a large area and contain a |
||||
huge number of features. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def RecordRoute(self, request_iterator, context): |
||||
"""A client-to-server streaming RPC. |
||||
|
||||
Accepts a stream of Points on a route being traversed, returning a |
||||
RouteSummary when traversal is completed. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def RouteChat(self, request_iterator, context): |
||||
"""A Bidirectional streaming RPC. |
||||
|
||||
Accepts a stream of RouteNotes sent while a route is being traversed, |
||||
while receiving other RouteNotes (e.g. from other users). |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
|
||||
def add_RouteGuideServicer_to_server(servicer, server): |
||||
rpc_method_handlers = { |
||||
'GetFeature': grpc.unary_unary_rpc_method_handler( |
||||
servicer.GetFeature, |
||||
request_deserializer=route__guide__pb2.Point.FromString, |
||||
response_serializer=route__guide__pb2.Feature.SerializeToString, |
||||
), |
||||
'ListFeatures': grpc.unary_stream_rpc_method_handler( |
||||
servicer.ListFeatures, |
||||
request_deserializer=route__guide__pb2.Rectangle.FromString, |
||||
response_serializer=route__guide__pb2.Feature.SerializeToString, |
||||
), |
||||
'RecordRoute': grpc.stream_unary_rpc_method_handler( |
||||
servicer.RecordRoute, |
||||
request_deserializer=route__guide__pb2.Point.FromString, |
||||
response_serializer=route__guide__pb2.RouteSummary.SerializeToString, |
||||
), |
||||
'RouteChat': grpc.stream_stream_rpc_method_handler( |
||||
servicer.RouteChat, |
||||
request_deserializer=route__guide__pb2.RouteNote.FromString, |
||||
response_serializer=route__guide__pb2.RouteNote.SerializeToString, |
||||
), |
||||
} |
||||
generic_handler = grpc.method_handlers_generic_handler( |
||||
'routeguide.RouteGuide', rpc_method_handlers) |
||||
server.add_generic_rpc_handlers((generic_handler,)) |
@ -0,0 +1,114 @@ |
||||
import grpc |
||||
from grpc.framework.common import cardinality |
||||
from grpc.framework.interfaces.face import utilities as face_utilities |
||||
|
||||
import route_guide_pb2 as route__guide__pb2 |
||||
|
||||
|
||||
class RouteGuideStub(object): |
||||
"""Interface exported by the server. |
||||
""" |
||||
|
||||
def __init__(self, channel): |
||||
"""Constructor. |
||||
|
||||
Args: |
||||
channel: A grpc.Channel. |
||||
""" |
||||
self.GetFeature = channel.unary_unary( |
||||
'/routeguide.RouteGuide/GetFeature', |
||||
request_serializer=route__guide__pb2.Point.SerializeToString, |
||||
response_deserializer=route__guide__pb2.Feature.FromString, |
||||
) |
||||
self.ListFeatures = channel.unary_stream( |
||||
'/routeguide.RouteGuide/ListFeatures', |
||||
request_serializer=route__guide__pb2.Rectangle.SerializeToString, |
||||
response_deserializer=route__guide__pb2.Feature.FromString, |
||||
) |
||||
self.RecordRoute = channel.stream_unary( |
||||
'/routeguide.RouteGuide/RecordRoute', |
||||
request_serializer=route__guide__pb2.Point.SerializeToString, |
||||
response_deserializer=route__guide__pb2.RouteSummary.FromString, |
||||
) |
||||
self.RouteChat = channel.stream_stream( |
||||
'/routeguide.RouteGuide/RouteChat', |
||||
request_serializer=route__guide__pb2.RouteNote.SerializeToString, |
||||
response_deserializer=route__guide__pb2.RouteNote.FromString, |
||||
) |
||||
|
||||
|
||||
class RouteGuideServicer(object): |
||||
"""Interface exported by the server. |
||||
""" |
||||
|
||||
def GetFeature(self, request, context): |
||||
"""A simple RPC. |
||||
|
||||
Obtains the feature at a given position. |
||||
|
||||
A feature with an empty name is returned if there's no feature at the given |
||||
position. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def ListFeatures(self, request, context): |
||||
"""A server-to-client streaming RPC. |
||||
|
||||
Obtains the Features available within the given Rectangle. Results are |
||||
streamed rather than returned at once (e.g. in a response message with a |
||||
repeated field), as the rectangle may cover a large area and contain a |
||||
huge number of features. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def RecordRoute(self, request_iterator, context): |
||||
"""A client-to-server streaming RPC. |
||||
|
||||
Accepts a stream of Points on a route being traversed, returning a |
||||
RouteSummary when traversal is completed. |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
def RouteChat(self, request_iterator, context): |
||||
"""A Bidirectional streaming RPC. |
||||
|
||||
Accepts a stream of RouteNotes sent while a route is being traversed, |
||||
while receiving other RouteNotes (e.g. from other users). |
||||
""" |
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) |
||||
context.set_details('Method not implemented!') |
||||
raise NotImplementedError('Method not implemented!') |
||||
|
||||
|
||||
def add_RouteGuideServicer_to_server(servicer, server): |
||||
rpc_method_handlers = { |
||||
'GetFeature': grpc.unary_unary_rpc_method_handler( |
||||
servicer.GetFeature, |
||||
request_deserializer=route__guide__pb2.Point.FromString, |
||||
response_serializer=route__guide__pb2.Feature.SerializeToString, |
||||
), |
||||
'ListFeatures': grpc.unary_stream_rpc_method_handler( |
||||
servicer.ListFeatures, |
||||
request_deserializer=route__guide__pb2.Rectangle.FromString, |
||||
response_serializer=route__guide__pb2.Feature.SerializeToString, |
||||
), |
||||
'RecordRoute': grpc.stream_unary_rpc_method_handler( |
||||
servicer.RecordRoute, |
||||
request_deserializer=route__guide__pb2.Point.FromString, |
||||
response_serializer=route__guide__pb2.RouteSummary.SerializeToString, |
||||
), |
||||
'RouteChat': grpc.stream_stream_rpc_method_handler( |
||||
servicer.RouteChat, |
||||
request_deserializer=route__guide__pb2.RouteNote.FromString, |
||||
response_serializer=route__guide__pb2.RouteNote.SerializeToString, |
||||
), |
||||
} |
||||
generic_handler = grpc.method_handlers_generic_handler( |
||||
'routeguide.RouteGuide', rpc_method_handlers) |
||||
server.add_generic_rpc_handlers((generic_handler,)) |
@ -0,0 +1,264 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/slice_buffer.h> |
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/ext/client_channel/connector.h" |
||||
#include "src/core/ext/client_channel/http_connect_handshaker.h" |
||||
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/channel/handshaker.h" |
||||
#include "src/core/lib/iomgr/tcp_client.h" |
||||
#include "src/core/lib/security/transport/security_connector.h" |
||||
|
||||
typedef struct { |
||||
grpc_connector base; |
||||
|
||||
gpr_mu mu; |
||||
gpr_refcount refs; |
||||
|
||||
bool shutdown; |
||||
bool connecting; |
||||
|
||||
grpc_chttp2_add_handshakers_func add_handshakers; |
||||
void *add_handshakers_user_data; |
||||
|
||||
grpc_closure *notify; |
||||
grpc_connect_in_args args; |
||||
grpc_connect_out_args *result; |
||||
grpc_closure initial_string_sent; |
||||
grpc_slice_buffer initial_string_buffer; |
||||
|
||||
grpc_endpoint *endpoint; // Non-NULL until handshaking starts.
|
||||
|
||||
grpc_closure connected; |
||||
|
||||
grpc_handshake_manager *handshake_mgr; |
||||
} chttp2_connector; |
||||
|
||||
static void chttp2_connector_ref(grpc_connector *con) { |
||||
chttp2_connector *c = (chttp2_connector *)con; |
||||
gpr_ref(&c->refs); |
||||
} |
||||
|
||||
static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx, |
||||
grpc_connector *con) { |
||||
chttp2_connector *c = (chttp2_connector *)con; |
||||
if (gpr_unref(&c->refs)) { |
||||
/* c->initial_string_buffer does not need to be destroyed */ |
||||
gpr_mu_destroy(&c->mu); |
||||
// If handshaking is not yet in progress, destroy the endpoint.
|
||||
// Otherwise, the handshaker will do this for us.
|
||||
if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint); |
||||
gpr_free(c); |
||||
} |
||||
} |
||||
|
||||
static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx, |
||||
grpc_connector *con) { |
||||
chttp2_connector *c = (chttp2_connector *)con; |
||||
gpr_mu_lock(&c->mu); |
||||
c->shutdown = true; |
||||
if (c->handshake_mgr != NULL) { |
||||
grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr); |
||||
} |
||||
// If handshaking is not yet in progress, shutdown the endpoint.
|
||||
// Otherwise, the handshaker will do this for us.
|
||||
if (!c->connecting && c->endpoint != NULL) { |
||||
grpc_endpoint_shutdown(exec_ctx, c->endpoint); |
||||
} |
||||
gpr_mu_unlock(&c->mu); |
||||
} |
||||
|
||||
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_error *error) { |
||||
grpc_handshaker_args *args = arg; |
||||
chttp2_connector *c = args->user_data; |
||||
gpr_mu_lock(&c->mu); |
||||
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||
if (error == GRPC_ERROR_NONE) { |
||||
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||
// We were shut down after handshaking completed successfully, so
|
||||
// destroy the endpoint here.
|
||||
// TODO(ctiller): It is currently necessary to shutdown endpoints
|
||||
// before destroying them, even if we know that there are no
|
||||
// pending read/write callbacks. This should be fixed, at which
|
||||
// point this can be removed.
|
||||
grpc_endpoint_shutdown(exec_ctx, args->endpoint); |
||||
grpc_endpoint_destroy(exec_ctx, args->endpoint); |
||||
grpc_channel_args_destroy(args->args); |
||||
grpc_slice_buffer_destroy(args->read_buffer); |
||||
gpr_free(args->read_buffer); |
||||
} else { |
||||
error = GRPC_ERROR_REF(error); |
||||
} |
||||
memset(c->result, 0, sizeof(*c->result)); |
||||
} else { |
||||
c->result->transport = |
||||
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1); |
||||
GPR_ASSERT(c->result->transport); |
||||
grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, |
||||
args->read_buffer); |
||||
c->result->channel_args = args->args; |
||||
} |
||||
grpc_closure *notify = c->notify; |
||||
c->notify = NULL; |
||||
grpc_closure_sched(exec_ctx, notify, error); |
||||
grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); |
||||
c->handshake_mgr = NULL; |
||||
gpr_mu_unlock(&c->mu); |
||||
chttp2_connector_unref(exec_ctx, (grpc_connector *)c); |
||||
} |
||||
|
||||
static void start_handshake_locked(grpc_exec_ctx *exec_ctx, |
||||
chttp2_connector *c) { |
||||
c->handshake_mgr = grpc_handshake_manager_create(); |
||||
char *proxy_name = grpc_get_http_proxy_server(); |
||||
if (proxy_name != NULL) { |
||||
grpc_handshake_manager_add(c->handshake_mgr, |
||||
grpc_http_connect_handshaker_create(proxy_name)); |
||||
gpr_free(proxy_name); |
||||
} |
||||
if (c->add_handshakers != NULL) { |
||||
c->add_handshakers(exec_ctx, c->add_handshakers_user_data, |
||||
c->handshake_mgr); |
||||
} |
||||
grpc_handshake_manager_do_handshake( |
||||
exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args, |
||||
c->args.deadline, NULL /* acceptor */, on_handshake_done, c); |
||||
c->endpoint = NULL; // Endpoint handed off to handshake manager.
|
||||
} |
||||
|
||||
static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_error *error) { |
||||
chttp2_connector *c = arg; |
||||
gpr_mu_lock(&c->mu); |
||||
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||
if (error == GRPC_ERROR_NONE) { |
||||
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||
} else { |
||||
error = GRPC_ERROR_REF(error); |
||||
} |
||||
memset(c->result, 0, sizeof(*c->result)); |
||||
grpc_closure *notify = c->notify; |
||||
c->notify = NULL; |
||||
grpc_closure_sched(exec_ctx, notify, error); |
||||
gpr_mu_unlock(&c->mu); |
||||
chttp2_connector_unref(exec_ctx, arg); |
||||
} else { |
||||
start_handshake_locked(exec_ctx, c); |
||||
gpr_mu_unlock(&c->mu); |
||||
} |
||||
} |
||||
|
||||
static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
||||
chttp2_connector *c = arg; |
||||
gpr_mu_lock(&c->mu); |
||||
GPR_ASSERT(c->connecting); |
||||
c->connecting = false; |
||||
if (error != GRPC_ERROR_NONE || c->shutdown) { |
||||
if (error == GRPC_ERROR_NONE) { |
||||
error = GRPC_ERROR_CREATE("connector shutdown"); |
||||
} else { |
||||
error = GRPC_ERROR_REF(error); |
||||
} |
||||
memset(c->result, 0, sizeof(*c->result)); |
||||
grpc_closure *notify = c->notify; |
||||
c->notify = NULL; |
||||
grpc_closure_sched(exec_ctx, notify, error); |
||||
if (c->endpoint != NULL) grpc_endpoint_shutdown(exec_ctx, c->endpoint); |
||||
gpr_mu_unlock(&c->mu); |
||||
chttp2_connector_unref(exec_ctx, arg); |
||||
} else { |
||||
GPR_ASSERT(c->endpoint != NULL); |
||||
if (!GRPC_SLICE_IS_EMPTY(c->args.initial_connect_string)) { |
||||
grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent, |
||||
c, grpc_schedule_on_exec_ctx); |
||||
grpc_slice_buffer_init(&c->initial_string_buffer); |
||||
grpc_slice_buffer_add(&c->initial_string_buffer, |
||||
c->args.initial_connect_string); |
||||
grpc_endpoint_write(exec_ctx, c->endpoint, &c->initial_string_buffer, |
||||
&c->initial_string_sent); |
||||
} else { |
||||
start_handshake_locked(exec_ctx, c); |
||||
} |
||||
gpr_mu_unlock(&c->mu); |
||||
} |
||||
} |
||||
|
||||
static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx, |
||||
grpc_connector *con, |
||||
const grpc_connect_in_args *args, |
||||
grpc_connect_out_args *result, |
||||
grpc_closure *notify) { |
||||
chttp2_connector *c = (chttp2_connector *)con; |
||||
gpr_mu_lock(&c->mu); |
||||
GPR_ASSERT(c->notify == NULL); |
||||
c->notify = notify; |
||||
c->args = *args; |
||||
c->result = result; |
||||
GPR_ASSERT(c->endpoint == NULL); |
||||
chttp2_connector_ref(con); // Ref taken for callback.
|
||||
grpc_closure_init(&c->connected, connected, c, grpc_schedule_on_exec_ctx); |
||||
GPR_ASSERT(!c->connecting); |
||||
c->connecting = true; |
||||
grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint, |
||||
args->interested_parties, args->channel_args, |
||||
args->addr, args->deadline); |
||||
gpr_mu_unlock(&c->mu); |
||||
} |
||||
|
||||
static const grpc_connector_vtable chttp2_connector_vtable = { |
||||
chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown, |
||||
chttp2_connector_connect}; |
||||
|
||||
grpc_connector *grpc_chttp2_connector_create( |
||||
grpc_exec_ctx *exec_ctx, grpc_chttp2_add_handshakers_func add_handshakers, |
||||
void *add_handshakers_user_data) { |
||||
chttp2_connector *c = gpr_malloc(sizeof(*c)); |
||||
memset(c, 0, sizeof(*c)); |
||||
c->base.vtable = &chttp2_connector_vtable; |
||||
gpr_mu_init(&c->mu); |
||||
gpr_ref_init(&c->refs, 1); |
||||
c->add_handshakers = add_handshakers; |
||||
c->add_handshakers_user_data = add_handshakers_user_data; |
||||
return &c->base; |
||||
} |
@ -0,0 +1,51 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H |
||||
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H |
||||
|
||||
#include "src/core/ext/client_channel/connector.h" |
||||
#include "src/core/lib/channel/handshaker.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
|
||||
typedef void (*grpc_chttp2_add_handshakers_func)( |
||||
grpc_exec_ctx* exec_ctx, void* user_data, |
||||
grpc_handshake_manager* handshake_mgr); |
||||
|
||||
/// If \a add_handshakers is non-NULL, it will be called with
|
||||
/// \a add_handshakers_user_data to add handshakers.
|
||||
grpc_connector* grpc_chttp2_connector_create( |
||||
grpc_exec_ctx* exec_ctx, grpc_chttp2_add_handshakers_func add_handshakers, |
||||
void* add_handshakers_user_data); |
||||
|
||||
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H */ |
@ -0,0 +1,363 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2015, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/ext/transport/chttp2/server/chttp2_server.h" |
||||
|
||||
#include <grpc/grpc.h> |
||||
|
||||
#include <string.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
#include <grpc/support/sync.h> |
||||
#include <grpc/support/useful.h> |
||||
|
||||
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" |
||||
#include "src/core/lib/channel/channel_args.h" |
||||
#include "src/core/lib/channel/handshaker.h" |
||||
#include "src/core/lib/channel/http_server_filter.h" |
||||
#include "src/core/lib/iomgr/endpoint.h" |
||||
#include "src/core/lib/iomgr/resolve_address.h" |
||||
#include "src/core/lib/iomgr/tcp_server.h" |
||||
#include "src/core/lib/surface/api_trace.h" |
||||
#include "src/core/lib/surface/server.h" |
||||
|
||||
void grpc_chttp2_server_handshaker_factory_add_handshakers( |
||||
grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||
grpc_handshake_manager *handshake_mgr) { |
||||
if (handshaker_factory != NULL) { |
||||
handshaker_factory->vtable->add_handshakers(exec_ctx, handshaker_factory, |
||||
handshake_mgr); |
||||
} |
||||
} |
||||
|
||||
void grpc_chttp2_server_handshaker_factory_destroy( |
||||
grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory) { |
||||
if (handshaker_factory != NULL) { |
||||
handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory); |
||||
} |
||||
} |
||||
|
||||
typedef struct pending_handshake_manager_node { |
||||
grpc_handshake_manager *handshake_mgr; |
||||
struct pending_handshake_manager_node *next; |
||||
} pending_handshake_manager_node; |
||||
|
||||
typedef struct { |
||||
grpc_server *server; |
||||
grpc_tcp_server *tcp_server; |
||||
grpc_channel_args *args; |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory; |
||||
gpr_mu mu; |
||||
bool shutdown; |
||||
grpc_closure tcp_server_shutdown_complete; |
||||
grpc_closure *server_destroy_listener_done; |
||||
pending_handshake_manager_node *pending_handshake_mgrs; |
||||
} server_state; |
||||
|
||||
typedef struct { |
||||
server_state *server_state; |
||||
grpc_pollset *accepting_pollset; |
||||
grpc_tcp_server_acceptor *acceptor; |
||||
grpc_handshake_manager *handshake_mgr; |
||||
} server_connection_state; |
||||
|
||||
static void pending_handshake_manager_add_locked( |
||||
server_state *state, grpc_handshake_manager *handshake_mgr) { |
||||
pending_handshake_manager_node *node = gpr_malloc(sizeof(*node)); |
||||
node->handshake_mgr = handshake_mgr; |
||||
node->next = state->pending_handshake_mgrs; |
||||
state->pending_handshake_mgrs = node; |
||||
} |
||||
|
||||
static void pending_handshake_manager_remove_locked( |
||||
server_state *state, grpc_handshake_manager *handshake_mgr) { |
||||
pending_handshake_manager_node **prev_node = &state->pending_handshake_mgrs; |
||||
for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
||||
node != NULL; node = node->next) { |
||||
if (node->handshake_mgr == handshake_mgr) { |
||||
*prev_node = node->next; |
||||
gpr_free(node); |
||||
break; |
||||
} |
||||
prev_node = &node->next; |
||||
} |
||||
} |
||||
|
||||
static void pending_handshake_manager_shutdown_locked(grpc_exec_ctx *exec_ctx, |
||||
server_state *state) { |
||||
pending_handshake_manager_node *prev_node = NULL; |
||||
for (pending_handshake_manager_node *node = state->pending_handshake_mgrs; |
||||
node != NULL; node = node->next) { |
||||
grpc_handshake_manager_shutdown(exec_ctx, node->handshake_mgr); |
||||
gpr_free(prev_node); |
||||
prev_node = node; |
||||
} |
||||
gpr_free(prev_node); |
||||
state->pending_handshake_mgrs = NULL; |
||||
} |
||||
|
||||
static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_error *error) { |
||||
grpc_handshaker_args *args = arg; |
||||
server_connection_state *connection_state = args->user_data; |
||||
gpr_mu_lock(&connection_state->server_state->mu); |
||||
if (error != GRPC_ERROR_NONE || connection_state->server_state->shutdown) { |
||||
const char *error_str = grpc_error_string(error); |
||||
gpr_log(GPR_ERROR, "Handshaking failed: %s", error_str); |
||||
grpc_error_free_string(error_str); |
||||
if (error == GRPC_ERROR_NONE && args->endpoint != NULL) { |
||||
// We were shut down after handshaking completed successfully, so
|
||||
// destroy the endpoint here.
|
||||
// TODO(ctiller): It is currently necessary to shutdown endpoints
|
||||
// before destroying them, even if we know that there are no
|
||||
// pending read/write callbacks. This should be fixed, at which
|
||||
// point this can be removed.
|
||||
grpc_endpoint_shutdown(exec_ctx, args->endpoint); |
||||
grpc_endpoint_destroy(exec_ctx, args->endpoint); |
||||
grpc_channel_args_destroy(args->args); |
||||
grpc_slice_buffer_destroy(args->read_buffer); |
||||
gpr_free(args->read_buffer); |
||||
} |
||||
} else { |
||||
// If the handshaking succeeded but there is no endpoint, then the
|
||||
// handshaker may have handed off the connection to some external
|
||||
// code, so we can just clean up here without creating a transport.
|
||||
if (args->endpoint != NULL) { |
||||
grpc_transport *transport = |
||||
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0); |
||||
grpc_server_setup_transport( |
||||
exec_ctx, connection_state->server_state->server, transport, |
||||
connection_state->accepting_pollset, args->args); |
||||
grpc_chttp2_transport_start_reading(exec_ctx, transport, |
||||
args->read_buffer); |
||||
grpc_channel_args_destroy(args->args); |
||||
} |
||||
} |
||||
pending_handshake_manager_remove_locked(connection_state->server_state, |
||||
connection_state->handshake_mgr); |
||||
gpr_mu_unlock(&connection_state->server_state->mu); |
||||
grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr); |
||||
grpc_tcp_server_unref(exec_ctx, connection_state->server_state->tcp_server); |
||||
gpr_free(connection_state->acceptor); |
||||
gpr_free(connection_state); |
||||
} |
||||
|
||||
static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, |
||||
grpc_pollset *accepting_pollset, |
||||
grpc_tcp_server_acceptor *acceptor) { |
||||
server_state *state = arg; |
||||
gpr_mu_lock(&state->mu); |
||||
if (state->shutdown) { |
||||
gpr_mu_unlock(&state->mu); |
||||
grpc_endpoint_destroy(exec_ctx, tcp); |
||||
gpr_free(acceptor); |
||||
return; |
||||
} |
||||
grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create(); |
||||
pending_handshake_manager_add_locked(state, handshake_mgr); |
||||
gpr_mu_unlock(&state->mu); |
||||
grpc_tcp_server_ref(state->tcp_server); |
||||
server_connection_state *connection_state = |
||||
gpr_malloc(sizeof(*connection_state)); |
||||
connection_state->server_state = state; |
||||
connection_state->accepting_pollset = accepting_pollset; |
||||
connection_state->acceptor = acceptor; |
||||
connection_state->handshake_mgr = handshake_mgr; |
||||
grpc_chttp2_server_handshaker_factory_add_handshakers( |
||||
exec_ctx, state->handshaker_factory, connection_state->handshake_mgr); |
||||
// TODO(roth): We should really get this timeout value from channel
|
||||
// args instead of hard-coding it.
|
||||
const gpr_timespec deadline = gpr_time_add( |
||||
gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN)); |
||||
grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr, |
||||
tcp, state->args, deadline, acceptor, |
||||
on_handshake_done, connection_state); |
||||
} |
||||
|
||||
/* Server callback: start listening on our ports */ |
||||
static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server, |
||||
void *arg, grpc_pollset **pollsets, |
||||
size_t pollset_count) { |
||||
server_state *state = arg; |
||||
gpr_mu_lock(&state->mu); |
||||
state->shutdown = false; |
||||
gpr_mu_unlock(&state->mu); |
||||
grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count, |
||||
on_accept, state); |
||||
} |
||||
|
||||
static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg, |
||||
grpc_error *error) { |
||||
server_state *state = arg; |
||||
/* ensure all threads have unlocked */ |
||||
gpr_mu_lock(&state->mu); |
||||
grpc_closure *destroy_done = state->server_destroy_listener_done; |
||||
GPR_ASSERT(state->shutdown); |
||||
pending_handshake_manager_shutdown_locked(exec_ctx, state); |
||||
gpr_mu_unlock(&state->mu); |
||||
// Flush queued work before destroying handshaker factory, since that
|
||||
// may do a synchronous unref.
|
||||
grpc_exec_ctx_flush(exec_ctx); |
||||
grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, |
||||
state->handshaker_factory); |
||||
if (destroy_done != NULL) { |
||||
destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error)); |
||||
grpc_exec_ctx_flush(exec_ctx); |
||||
} |
||||
grpc_channel_args_destroy(state->args); |
||||
gpr_mu_destroy(&state->mu); |
||||
gpr_free(state); |
||||
} |
||||
|
||||
/* Server callback: destroy the tcp listener (so we don't generate further
|
||||
callbacks) */ |
||||
static void server_destroy_listener(grpc_exec_ctx *exec_ctx, |
||||
grpc_server *server, void *arg, |
||||
grpc_closure *destroy_done) { |
||||
server_state *state = arg; |
||||
gpr_mu_lock(&state->mu); |
||||
state->shutdown = true; |
||||
state->server_destroy_listener_done = destroy_done; |
||||
grpc_tcp_server *tcp_server = state->tcp_server; |
||||
gpr_mu_unlock(&state->mu); |
||||
grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server); |
||||
grpc_tcp_server_unref(exec_ctx, tcp_server); |
||||
} |
||||
|
||||
grpc_error *grpc_chttp2_server_add_port( |
||||
grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, |
||||
grpc_channel_args *args, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory, int *port_num) { |
||||
grpc_resolved_addresses *resolved = NULL; |
||||
grpc_tcp_server *tcp_server = NULL; |
||||
size_t i; |
||||
size_t count = 0; |
||||
int port_temp; |
||||
grpc_error *err = GRPC_ERROR_NONE; |
||||
server_state *state = NULL; |
||||
grpc_error **errors = NULL; |
||||
|
||||
*port_num = -1; |
||||
|
||||
/* resolve address */ |
||||
err = grpc_blocking_resolve_address(addr, "https", &resolved); |
||||
if (err != GRPC_ERROR_NONE) { |
||||
goto error; |
||||
} |
||||
state = gpr_malloc(sizeof(*state)); |
||||
memset(state, 0, sizeof(*state)); |
||||
grpc_closure_init(&state->tcp_server_shutdown_complete, |
||||
tcp_server_shutdown_complete, state, |
||||
grpc_schedule_on_exec_ctx); |
||||
err = grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete, |
||||
args, &tcp_server); |
||||
if (err != GRPC_ERROR_NONE) { |
||||
goto error; |
||||
} |
||||
|
||||
state->server = server; |
||||
state->tcp_server = tcp_server; |
||||
state->args = args; |
||||
state->handshaker_factory = handshaker_factory; |
||||
state->shutdown = true; |
||||
gpr_mu_init(&state->mu); |
||||
|
||||
const size_t naddrs = resolved->naddrs; |
||||
errors = gpr_malloc(sizeof(*errors) * naddrs); |
||||
for (i = 0; i < naddrs; i++) { |
||||
errors[i] = |
||||
grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); |
||||
if (errors[i] == GRPC_ERROR_NONE) { |
||||
if (*port_num == -1) { |
||||
*port_num = port_temp; |
||||
} else { |
||||
GPR_ASSERT(*port_num == port_temp); |
||||
} |
||||
count++; |
||||
} |
||||
} |
||||
if (count == 0) { |
||||
char *msg; |
||||
gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", |
||||
naddrs); |
||||
err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
||||
gpr_free(msg); |
||||
goto error; |
||||
} else if (count != naddrs) { |
||||
char *msg; |
||||
gpr_asprintf(&msg, "Only %" PRIuPTR |
||||
" addresses added out of total %" PRIuPTR " resolved", |
||||
count, naddrs); |
||||
err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs); |
||||
gpr_free(msg); |
||||
|
||||
const char *warning_message = grpc_error_string(err); |
||||
gpr_log(GPR_INFO, "WARNING: %s", warning_message); |
||||
grpc_error_free_string(warning_message); |
||||
/* we managed to bind some addresses: continue */ |
||||
} |
||||
grpc_resolved_addresses_destroy(resolved); |
||||
|
||||
/* Register with the server only upon success */ |
||||
grpc_server_add_listener(exec_ctx, server, state, server_start_listener, |
||||
server_destroy_listener); |
||||
goto done; |
||||
|
||||
/* Error path: cleanup and return */ |
||||
error: |
||||
GPR_ASSERT(err != GRPC_ERROR_NONE); |
||||
if (resolved) { |
||||
grpc_resolved_addresses_destroy(resolved); |
||||
} |
||||
if (tcp_server) { |
||||
grpc_tcp_server_unref(exec_ctx, tcp_server); |
||||
} else { |
||||
grpc_channel_args_destroy(args); |
||||
grpc_chttp2_server_handshaker_factory_destroy(exec_ctx, handshaker_factory); |
||||
gpr_free(state); |
||||
} |
||||
*port_num = 0; |
||||
|
||||
done: |
||||
if (errors != NULL) { |
||||
for (i = 0; i < naddrs; i++) { |
||||
GRPC_ERROR_UNREF(errors[i]); |
||||
} |
||||
gpr_free(errors); |
||||
} |
||||
return err; |
||||
} |
@ -0,0 +1,78 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2016, Google Inc. |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* * Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following disclaimer |
||||
* in the documentation and/or other materials provided with the |
||||
* distribution. |
||||
* * Neither the name of Google Inc. nor the names of its |
||||
* contributors may be used to endorse or promote products derived from |
||||
* this software without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H |
||||
#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H |
||||
|
||||
#include <grpc/impl/codegen/grpc_types.h> |
||||
|
||||
#include "src/core/lib/channel/handshaker.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
|
||||
/// A server handshaker factory is used to create handshakers for server
|
||||
/// connections.
|
||||
typedef struct grpc_chttp2_server_handshaker_factory |
||||
grpc_chttp2_server_handshaker_factory; |
||||
|
||||
typedef struct { |
||||
void (*add_handshakers)( |
||||
grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||
grpc_handshake_manager *handshake_mgr); |
||||
void (*destroy)(grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory); |
||||
} grpc_chttp2_server_handshaker_factory_vtable; |
||||
|
||||
struct grpc_chttp2_server_handshaker_factory { |
||||
const grpc_chttp2_server_handshaker_factory_vtable *vtable; |
||||
}; |
||||
|
||||
void grpc_chttp2_server_handshaker_factory_add_handshakers( |
||||
grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory, |
||||
grpc_handshake_manager *handshake_mgr); |
||||
|
||||
void grpc_chttp2_server_handshaker_factory_destroy( |
||||
grpc_exec_ctx *exec_ctx, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory); |
||||
|
||||
/// Adds a port to \a server. Sets \a port_num to the port number.
|
||||
/// If \a handshaker_factory is not NULL, it will be used to create
|
||||
/// handshakers for the port.
|
||||
/// Takes ownership of \a args and \a handshaker_factory.
|
||||
grpc_error *grpc_chttp2_server_add_port( |
||||
grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, |
||||
grpc_channel_args *args, |
||||
grpc_chttp2_server_handshaker_factory *handshaker_factory, int *port_num); |
||||
|
||||
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */ |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue