|
|
|
@ -32,13 +32,13 @@ |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#import <XCTest/XCTest.h> |
|
|
|
|
#import <sys/socket.h> |
|
|
|
|
#import <netinet/in.h> |
|
|
|
|
#import <sys/socket.h> |
|
|
|
|
|
|
|
|
|
#import <Cronet/Cronet.h> |
|
|
|
|
#import <grpc/support/host_port.h> |
|
|
|
|
#import <grpc/grpc_cronet.h> |
|
|
|
|
#import <grpc/grpc.h> |
|
|
|
|
#import <grpc/grpc_cronet.h> |
|
|
|
|
#import <grpc/support/host_port.h> |
|
|
|
|
#import "test/core/end2end/cq_verifier.h" |
|
|
|
|
#import "test/core/util/port.h" |
|
|
|
|
|
|
|
|
@ -49,19 +49,19 @@ |
|
|
|
|
#import "src/core/lib/support/env.h" |
|
|
|
|
#import "src/core/lib/support/string.h" |
|
|
|
|
#import "src/core/lib/support/tmpfile.h" |
|
|
|
|
#import "test/core/util/test_config.h" |
|
|
|
|
#import "test/core/end2end/data/ssl_test_data.h" |
|
|
|
|
#import "test/core/util/test_config.h" |
|
|
|
|
|
|
|
|
|
#import <BoringSSL/openssl/ssl.h> |
|
|
|
|
|
|
|
|
|
static void drain_cq(grpc_completion_queue *cq) { |
|
|
|
|
grpc_event ev; |
|
|
|
|
do { |
|
|
|
|
ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5), NULL); |
|
|
|
|
ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5), |
|
|
|
|
NULL); |
|
|
|
|
} while (ev.type != GRPC_QUEUE_SHUTDOWN); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@interface CronetUnitTests : XCTestCase |
|
|
|
|
|
|
|
|
|
@end |
|
|
|
@ -71,8 +71,8 @@ static void drain_cq(grpc_completion_queue *cq) { |
|
|
|
|
+ (void)setUp { |
|
|
|
|
[super setUp]; |
|
|
|
|
|
|
|
|
|
/*** FILE *roots_file; |
|
|
|
|
size_t roots_size = strlen(test_root_cert);*/ |
|
|
|
|
/*** FILE *roots_file; |
|
|
|
|
size_t roots_size = strlen(test_root_cert);*/ |
|
|
|
|
|
|
|
|
|
char *argv[] = {"CoreCronetEnd2EndTests"}; |
|
|
|
|
grpc_test_init(1, argv); |
|
|
|
@ -83,8 +83,8 @@ static void drain_cq(grpc_completion_queue *cq) { |
|
|
|
|
[Cronet setSslKeyLogFileName:@"Documents/key"]; |
|
|
|
|
[Cronet enableTestCertVerifierForTesting]; |
|
|
|
|
NSURL *url = [[[NSFileManager defaultManager] |
|
|
|
|
URLsForDirectory:NSDocumentDirectory |
|
|
|
|
inDomains:NSUserDomainMask] lastObject]; |
|
|
|
|
URLsForDirectory:NSDocumentDirectory |
|
|
|
|
inDomains:NSUserDomainMask] lastObject]; |
|
|
|
|
NSLog(@"Documents directory: %@", url); |
|
|
|
|
[Cronet start]; |
|
|
|
|
[Cronet startNetLogToFile:@"Documents/cronet_netlog.json" logBytes:YES]; |
|
|
|
@ -104,28 +104,28 @@ void init_ssl(void) { |
|
|
|
|
OpenSSL_add_ssl_algorithms(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void cleanup_ssl(void) { |
|
|
|
|
EVP_cleanup(); |
|
|
|
|
} |
|
|
|
|
void cleanup_ssl(void) { EVP_cleanup(); } |
|
|
|
|
|
|
|
|
|
int alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, |
|
|
|
|
const unsigned char *in, unsigned int inlen, void *arg) { |
|
|
|
|
// Always select "h2" as the ALPN protocol to be used |
|
|
|
|
*out = (const unsigned char*)"h2"; |
|
|
|
|
*out = (const unsigned char *)"h2"; |
|
|
|
|
*outlen = 2; |
|
|
|
|
return SSL_TLSEXT_ERR_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void init_ctx(SSL_CTX *ctx) { |
|
|
|
|
// Install server certificate |
|
|
|
|
BIO *pem = BIO_new_mem_buf((void*)test_server1_cert, (int)strlen(test_server1_cert)); |
|
|
|
|
BIO *pem = BIO_new_mem_buf((void *)test_server1_cert, |
|
|
|
|
(int)strlen(test_server1_cert)); |
|
|
|
|
X509 *cert = PEM_read_bio_X509_AUX(pem, NULL, NULL, ""); |
|
|
|
|
SSL_CTX_use_certificate(ctx, cert); |
|
|
|
|
X509_free(cert); |
|
|
|
|
BIO_free(pem); |
|
|
|
|
|
|
|
|
|
// Install server private key |
|
|
|
|
pem = BIO_new_mem_buf((void *)test_server1_key, (int)strlen(test_server1_key)); |
|
|
|
|
pem = |
|
|
|
|
BIO_new_mem_buf((void *)test_server1_key, (int)strlen(test_server1_key)); |
|
|
|
|
EVP_PKEY *key = PEM_read_bio_PrivateKey(pem, NULL, NULL, ""); |
|
|
|
|
SSL_CTX_use_PrivateKey(ctx, key); |
|
|
|
|
EVP_PKEY_free(key); |
|
|
|
@ -139,29 +139,34 @@ void init_ctx(SSL_CTX *ctx) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
unsigned int parse_h2_length(const char *field) { |
|
|
|
|
return ((unsigned int)(unsigned char)(field[0])) * 65536 + |
|
|
|
|
((unsigned int)(unsigned char)(field[1])) * 256 + |
|
|
|
|
((unsigned int)(unsigned char)(field[2])); |
|
|
|
|
return ((unsigned int)(unsigned char)(field[0])) * 65536 + |
|
|
|
|
((unsigned int)(unsigned char)(field[1])) * 256 + |
|
|
|
|
((unsigned int)(unsigned char)(field[2])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
- (void)testInternalError { |
|
|
|
|
grpc_call *c; |
|
|
|
|
grpc_slice request_payload_slice = |
|
|
|
|
grpc_slice_from_copied_string("hello world"); |
|
|
|
|
grpc_slice_from_copied_string("hello world"); |
|
|
|
|
grpc_byte_buffer *request_payload = |
|
|
|
|
grpc_raw_byte_buffer_create(&request_payload_slice, 1); |
|
|
|
|
grpc_raw_byte_buffer_create(&request_payload_slice, 1); |
|
|
|
|
gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); |
|
|
|
|
grpc_metadata meta_c[2] = { |
|
|
|
|
{"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}}, |
|
|
|
|
{"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}}; |
|
|
|
|
grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), |
|
|
|
|
grpc_slice_from_static_string("val1"), |
|
|
|
|
0, |
|
|
|
|
{{NULL, NULL, NULL, NULL}}}, |
|
|
|
|
{grpc_slice_from_static_string("key2"), |
|
|
|
|
grpc_slice_from_static_string("val2"), |
|
|
|
|
0, |
|
|
|
|
{{NULL, NULL, NULL, NULL}}}}; |
|
|
|
|
|
|
|
|
|
int port = grpc_pick_unused_port_or_die(); |
|
|
|
|
char *addr; |
|
|
|
|
gpr_join_host_port(&addr, "127.0.0.1", port); |
|
|
|
|
grpc_completion_queue *cq = grpc_completion_queue_create(NULL); |
|
|
|
|
stream_engine *cronetEngine = [Cronet getGlobalEngine]; |
|
|
|
|
grpc_channel *client = grpc_cronet_secure_channel_create(cronetEngine, addr, |
|
|
|
|
NULL, NULL); |
|
|
|
|
grpc_channel *client = |
|
|
|
|
grpc_cronet_secure_channel_create(cronetEngine, addr, NULL, NULL); |
|
|
|
|
|
|
|
|
|
cq_verifier *cqv = cq_verifier_create(cq); |
|
|
|
|
grpc_op ops[6]; |
|
|
|
@ -173,12 +178,11 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
grpc_call_details call_details; |
|
|
|
|
grpc_status_code status; |
|
|
|
|
grpc_call_error error; |
|
|
|
|
char *details = NULL; |
|
|
|
|
size_t details_capacity = 0; |
|
|
|
|
grpc_slice details; |
|
|
|
|
|
|
|
|
|
c = grpc_channel_create_call( |
|
|
|
|
client, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/foo", |
|
|
|
|
NULL, deadline, NULL); |
|
|
|
|
c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, cq, |
|
|
|
|
grpc_slice_from_static_string("/foo"), NULL, |
|
|
|
|
deadline, NULL); |
|
|
|
|
GPR_ASSERT(c); |
|
|
|
|
|
|
|
|
|
grpc_metadata_array_init(&initial_metadata_recv); |
|
|
|
@ -217,40 +221,40 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
|
|
|
|
op->data.recv_status_on_client.status = &status; |
|
|
|
|
op->data.recv_status_on_client.status_details = &details; |
|
|
|
|
op->data.recv_status_on_client.status_details_capacity = &details_capacity; |
|
|
|
|
op->flags = 0; |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
|
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), (void*)1, NULL); |
|
|
|
|
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), (void *)1, NULL); |
|
|
|
|
GPR_ASSERT(GRPC_CALL_OK == error); |
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
|
|
|
|
int sl = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
GPR_ASSERT(sl >= 0); |
|
|
|
|
|
|
|
|
|
// Make and TCP endpoint to accept the connection |
|
|
|
|
struct sockaddr_in s_addr; |
|
|
|
|
memset(&s_addr, 0, sizeof(s_addr)); |
|
|
|
|
s_addr.sin_family = AF_INET; |
|
|
|
|
s_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
|
s_addr.sin_port = htons(port); |
|
|
|
|
GPR_ASSERT(0 == bind(sl, (struct sockaddr*)&s_addr, sizeof(s_addr))); |
|
|
|
|
GPR_ASSERT(0 == listen(sl, 5)); |
|
|
|
|
int s = accept(sl, NULL, NULL); |
|
|
|
|
GPR_ASSERT(s >= 0); |
|
|
|
|
|
|
|
|
|
// Close the connection after 1 second to trigger Cronet's on_failed() |
|
|
|
|
sleep(1); |
|
|
|
|
close(s); |
|
|
|
|
close(sl); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
CQ_EXPECT_COMPLETION(cqv, (void*)1, 1); |
|
|
|
|
dispatch_async( |
|
|
|
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
|
|
|
|
int sl = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
GPR_ASSERT(sl >= 0); |
|
|
|
|
|
|
|
|
|
// Make and TCP endpoint to accept the connection |
|
|
|
|
struct sockaddr_in s_addr; |
|
|
|
|
memset(&s_addr, 0, sizeof(s_addr)); |
|
|
|
|
s_addr.sin_family = AF_INET; |
|
|
|
|
s_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
|
s_addr.sin_port = htons(port); |
|
|
|
|
GPR_ASSERT(0 == bind(sl, (struct sockaddr *)&s_addr, sizeof(s_addr))); |
|
|
|
|
GPR_ASSERT(0 == listen(sl, 5)); |
|
|
|
|
int s = accept(sl, NULL, NULL); |
|
|
|
|
GPR_ASSERT(s >= 0); |
|
|
|
|
|
|
|
|
|
// Close the connection after 1 second to trigger Cronet's on_failed() |
|
|
|
|
sleep(1); |
|
|
|
|
close(s); |
|
|
|
|
close(sl); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
CQ_EXPECT_COMPLETION(cqv, (void *)1, 1); |
|
|
|
|
cq_verify(cqv); |
|
|
|
|
|
|
|
|
|
GPR_ASSERT(status == GRPC_STATUS_UNAVAILABLE); |
|
|
|
|
|
|
|
|
|
gpr_free(details); |
|
|
|
|
grpc_slice_unref(details); |
|
|
|
|
grpc_metadata_array_destroy(&initial_metadata_recv); |
|
|
|
|
grpc_metadata_array_destroy(&trailing_metadata_recv); |
|
|
|
|
grpc_metadata_array_destroy(&request_metadata_recv); |
|
|
|
@ -262,7 +266,7 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
|
|
|
|
|
grpc_byte_buffer_destroy(request_payload); |
|
|
|
|
grpc_byte_buffer_destroy(response_payload_recv); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grpc_channel_destroy(client); |
|
|
|
|
grpc_completion_queue_shutdown(cq); |
|
|
|
|
drain_cq(cq); |
|
|
|
@ -273,17 +277,22 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
grpc_arg arg; |
|
|
|
|
arg.key = GRPC_ARG_USE_CRONET_PACKET_COALESCING; |
|
|
|
|
arg.type = GRPC_ARG_INTEGER; |
|
|
|
|
arg.value.integer = use_coalescing ? 1:0; |
|
|
|
|
arg.value.integer = use_coalescing ? 1 : 0; |
|
|
|
|
grpc_channel_args *args = grpc_channel_args_copy_and_add(NULL, &arg, 1); |
|
|
|
|
grpc_call *c; |
|
|
|
|
grpc_slice request_payload_slice = |
|
|
|
|
grpc_slice_from_copied_string("hello world"); |
|
|
|
|
grpc_slice_from_copied_string("hello world"); |
|
|
|
|
grpc_byte_buffer *request_payload = |
|
|
|
|
grpc_raw_byte_buffer_create(&request_payload_slice, 1); |
|
|
|
|
gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5); |
|
|
|
|
grpc_metadata meta_c[2] = { |
|
|
|
|
{"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}}, |
|
|
|
|
{"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}}; |
|
|
|
|
grpc_raw_byte_buffer_create(&request_payload_slice, 1); |
|
|
|
|
gpr_timespec deadline = grpc_timeout_seconds_to_deadline(5); |
|
|
|
|
grpc_metadata meta_c[2] = {{grpc_slice_from_static_string("key1"), |
|
|
|
|
grpc_slice_from_static_string("val1"), |
|
|
|
|
0, |
|
|
|
|
{{NULL, NULL, NULL, NULL}}}, |
|
|
|
|
{grpc_slice_from_static_string("key2"), |
|
|
|
|
grpc_slice_from_static_string("val2"), |
|
|
|
|
0, |
|
|
|
|
{{NULL, NULL, NULL, NULL}}}}; |
|
|
|
|
|
|
|
|
|
int port = grpc_pick_unused_port_or_die(); |
|
|
|
|
char *addr; |
|
|
|
@ -303,12 +312,11 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
grpc_call_details call_details; |
|
|
|
|
grpc_status_code status; |
|
|
|
|
grpc_call_error error; |
|
|
|
|
char *details = NULL; |
|
|
|
|
size_t details_capacity = 0; |
|
|
|
|
grpc_slice details; |
|
|
|
|
|
|
|
|
|
c = grpc_channel_create_call( |
|
|
|
|
client, NULL, GRPC_PROPAGATE_DEFAULTS, cq, "/foo", |
|
|
|
|
NULL, deadline, NULL); |
|
|
|
|
c = grpc_channel_create_call(client, NULL, GRPC_PROPAGATE_DEFAULTS, cq, |
|
|
|
|
grpc_slice_from_static_string("/foo"), NULL, |
|
|
|
|
deadline, NULL); |
|
|
|
|
GPR_ASSERT(c); |
|
|
|
|
|
|
|
|
|
grpc_metadata_array_init(&initial_metadata_recv); |
|
|
|
@ -325,7 +333,7 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
|
op->op = GRPC_OP_SEND_MESSAGE; |
|
|
|
|
op->data.send_message = request_payload; |
|
|
|
|
op->data.send_message.send_message = request_payload; |
|
|
|
|
op->flags = 0; |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
@ -334,12 +342,12 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
|
op->op = GRPC_OP_RECV_INITIAL_METADATA; |
|
|
|
|
op->data.recv_initial_metadata = &initial_metadata_recv; |
|
|
|
|
op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv; |
|
|
|
|
op->flags = 0; |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
|
op->op = GRPC_OP_RECV_MESSAGE; |
|
|
|
|
op->data.recv_message = &response_payload_recv; |
|
|
|
|
op->data.recv_message.recv_message = &response_payload_recv; |
|
|
|
|
op->flags = 0; |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
@ -347,79 +355,80 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; |
|
|
|
|
op->data.recv_status_on_client.status = &status; |
|
|
|
|
op->data.recv_status_on_client.status_details = &details; |
|
|
|
|
op->data.recv_status_on_client.status_details_capacity = &details_capacity; |
|
|
|
|
op->flags = 0; |
|
|
|
|
op->reserved = NULL; |
|
|
|
|
op++; |
|
|
|
|
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), (void*)1, NULL); |
|
|
|
|
error = grpc_call_start_batch(c, ops, (size_t)(op - ops), (void *)1, NULL); |
|
|
|
|
GPR_ASSERT(GRPC_CALL_OK == error); |
|
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
|
|
|
|
int sl = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
GPR_ASSERT(sl >= 0); |
|
|
|
|
struct sockaddr_in s_addr; |
|
|
|
|
memset(&s_addr, 0, sizeof(s_addr)); |
|
|
|
|
s_addr.sin_family = AF_INET; |
|
|
|
|
s_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
|
s_addr.sin_port = htons(port); |
|
|
|
|
GPR_ASSERT(0 == bind(sl, (struct sockaddr*)&s_addr, sizeof(s_addr))); |
|
|
|
|
GPR_ASSERT(0 == listen(sl, 5)); |
|
|
|
|
int s = accept(sl, NULL, NULL); |
|
|
|
|
GPR_ASSERT(s >= 0); |
|
|
|
|
struct timeval tv; |
|
|
|
|
tv.tv_sec = 2; |
|
|
|
|
tv.tv_usec = 0; |
|
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
|
|
|
|
|
|
|
|
|
// Make an TLS endpoint to receive Cronet's transmission |
|
|
|
|
SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_server_method()); |
|
|
|
|
init_ctx(ctx); |
|
|
|
|
SSL *ssl = SSL_new(ctx); |
|
|
|
|
SSL_set_fd(ssl, s); |
|
|
|
|
SSL_accept(ssl); |
|
|
|
|
|
|
|
|
|
const char magic[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; |
|
|
|
|
|
|
|
|
|
char buf[4096]; |
|
|
|
|
long len; |
|
|
|
|
bool coalesced = false; |
|
|
|
|
while ((len = SSL_read(ssl, buf, sizeof(buf))) > 0) { |
|
|
|
|
gpr_log(GPR_DEBUG, "Read len: %ld", len); |
|
|
|
|
|
|
|
|
|
// Analyze the HTTP/2 frames in the same TLS PDU to identify if |
|
|
|
|
// coalescing is successful |
|
|
|
|
unsigned int p = 0; |
|
|
|
|
while (p < len) { |
|
|
|
|
if (len - p >= 24 && 0 == memcmp(&buf[p], magic, 24)) { |
|
|
|
|
p += 24; |
|
|
|
|
continue; |
|
|
|
|
dispatch_async( |
|
|
|
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
|
|
|
|
int sl = socket(AF_INET, SOCK_STREAM, 0); |
|
|
|
|
GPR_ASSERT(sl >= 0); |
|
|
|
|
struct sockaddr_in s_addr; |
|
|
|
|
memset(&s_addr, 0, sizeof(s_addr)); |
|
|
|
|
s_addr.sin_family = AF_INET; |
|
|
|
|
s_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
|
|
s_addr.sin_port = htons(port); |
|
|
|
|
GPR_ASSERT(0 == bind(sl, (struct sockaddr *)&s_addr, sizeof(s_addr))); |
|
|
|
|
GPR_ASSERT(0 == listen(sl, 5)); |
|
|
|
|
int s = accept(sl, NULL, NULL); |
|
|
|
|
GPR_ASSERT(s >= 0); |
|
|
|
|
struct timeval tv; |
|
|
|
|
tv.tv_sec = 2; |
|
|
|
|
tv.tv_usec = 0; |
|
|
|
|
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
|
|
|
|
|
|
|
|
|
// Make an TLS endpoint to receive Cronet's transmission |
|
|
|
|
SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_server_method()); |
|
|
|
|
init_ctx(ctx); |
|
|
|
|
SSL *ssl = SSL_new(ctx); |
|
|
|
|
SSL_set_fd(ssl, s); |
|
|
|
|
SSL_accept(ssl); |
|
|
|
|
|
|
|
|
|
const char magic[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; |
|
|
|
|
|
|
|
|
|
char buf[4096]; |
|
|
|
|
long len; |
|
|
|
|
bool coalesced = false; |
|
|
|
|
while ((len = SSL_read(ssl, buf, sizeof(buf))) > 0) { |
|
|
|
|
gpr_log(GPR_DEBUG, "Read len: %ld", len); |
|
|
|
|
|
|
|
|
|
// Analyze the HTTP/2 frames in the same TLS PDU to identify if |
|
|
|
|
// coalescing is successful |
|
|
|
|
unsigned int p = 0; |
|
|
|
|
while (p < len) { |
|
|
|
|
if (len - p >= 24 && 0 == memcmp(&buf[p], magic, 24)) { |
|
|
|
|
p += 24; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (buf[p + 3] == 0 && // Type is DATA |
|
|
|
|
parse_h2_length(&buf[p]) == 0x10 && // Length is correct |
|
|
|
|
(buf[p + 4] & 1) != 0 && // EOS bit is set |
|
|
|
|
0 == memcmp("hello world", &buf[p + 14], |
|
|
|
|
11)) { // Message is correct |
|
|
|
|
coalesced = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
p += (parse_h2_length(&buf[p]) + 9); |
|
|
|
|
} |
|
|
|
|
if (coalesced) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (buf[p+3] == 0 && // Type is DATA |
|
|
|
|
parse_h2_length(&buf[p]) == 0x10 && // Length is correct |
|
|
|
|
(buf[p+4] & 1) != 0 && // EOS bit is set |
|
|
|
|
0 == memcmp("hello world", &buf[p+14], 11)) { // Message is correct |
|
|
|
|
coalesced = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
p += (parse_h2_length(&buf[p]) + 9); |
|
|
|
|
} |
|
|
|
|
if (coalesced) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
XCTAssert(coalesced == use_coalescing); |
|
|
|
|
SSL_free(ssl); |
|
|
|
|
SSL_CTX_free(ctx); |
|
|
|
|
close(s); |
|
|
|
|
close(sl); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
CQ_EXPECT_COMPLETION(cqv, (void*)1, 1); |
|
|
|
|
XCTAssert(coalesced == use_coalescing); |
|
|
|
|
SSL_free(ssl); |
|
|
|
|
SSL_CTX_free(ctx); |
|
|
|
|
close(s); |
|
|
|
|
close(sl); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
CQ_EXPECT_COMPLETION(cqv, (void *)1, 1); |
|
|
|
|
cq_verify(cqv); |
|
|
|
|
|
|
|
|
|
gpr_free(details); |
|
|
|
|
grpc_slice_unref(details); |
|
|
|
|
grpc_metadata_array_destroy(&initial_metadata_recv); |
|
|
|
|
grpc_metadata_array_destroy(&trailing_metadata_recv); |
|
|
|
|
grpc_metadata_array_destroy(&request_metadata_recv); |
|
|
|
@ -431,7 +440,7 @@ unsigned int parse_h2_length(const char *field) { |
|
|
|
|
|
|
|
|
|
grpc_byte_buffer_destroy(request_payload); |
|
|
|
|
grpc_byte_buffer_destroy(response_payload_recv); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grpc_channel_destroy(client); |
|
|
|
|
grpc_completion_queue_shutdown(cq); |
|
|
|
|
drain_cq(cq); |
|
|
|
|