diff --git a/test/core/end2end/fixtures/http_proxy.c b/test/core/end2end/fixtures/http_proxy.c index c92f869be16..47f50069e27 100644 --- a/test/core/end2end/fixtures/http_proxy.c +++ b/test/core/end2end/fixtures/http_proxy.c @@ -90,8 +90,10 @@ typedef struct proxy_connection { grpc_closure on_server_write_done; gpr_slice_buffer client_read_buffer; + gpr_slice_buffer client_deferred_write_buffer; gpr_slice_buffer client_write_buffer; gpr_slice_buffer server_read_buffer; + gpr_slice_buffer server_deferred_write_buffer; gpr_slice_buffer server_write_buffer; grpc_http_parser http_parser; @@ -107,8 +109,10 @@ static void proxy_connection_unref(grpc_exec_ctx* exec_ctx, grpc_endpoint_destroy(exec_ctx, conn->server_endpoint); grpc_pollset_set_destroy(conn->pollset_set); gpr_slice_buffer_destroy(&conn->client_read_buffer); + gpr_slice_buffer_destroy(&conn->client_deferred_write_buffer); gpr_slice_buffer_destroy(&conn->client_write_buffer); gpr_slice_buffer_destroy(&conn->server_read_buffer); + gpr_slice_buffer_destroy(&conn->server_deferred_write_buffer); gpr_slice_buffer_destroy(&conn->server_write_buffer); grpc_http_parser_destroy(&conn->http_parser); grpc_http_request_destroy(&conn->http_request); @@ -130,10 +134,6 @@ static void proxy_connection_failed(grpc_exec_ctx* exec_ctx, proxy_connection_unref(exec_ctx, conn); } -// Forward declarations. -static void do_client_read(grpc_exec_ctx* exec_ctx, proxy_connection* conn); -static void do_server_read(grpc_exec_ctx* exec_ctx, proxy_connection* conn); - // Callback for writing proxy data to the client. static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { @@ -143,10 +143,20 @@ static void on_client_write_done(grpc_exec_ctx* exec_ctx, void* arg, "HTTP proxy client write", error); return; } - // Clear write buffer. + // Clear write buffer (the data we just wrote). gpr_slice_buffer_reset_and_unref(&conn->client_write_buffer); - // Unref the connection. - proxy_connection_unref(exec_ctx, conn); + // If more data was read from the server since we started this write, + // write that data now. + if (conn->client_deferred_write_buffer.length > 0) { + gpr_slice_buffer_move_into(&conn->client_deferred_write_buffer, + &conn->client_write_buffer); + grpc_endpoint_write(exec_ctx, conn->client_endpoint, + &conn->client_write_buffer, + &conn->on_client_write_done); + } else { + // No more writes. Unref the connection. + proxy_connection_unref(exec_ctx, conn); + } } // Callback for writing proxy data to the backend server. @@ -158,10 +168,20 @@ static void on_server_write_done(grpc_exec_ctx* exec_ctx, void* arg, "HTTP proxy server write", error); return; } - // Clear write buffer. + // Clear write buffer (the data we just wrote). gpr_slice_buffer_reset_and_unref(&conn->server_write_buffer); - // Unref the connection. - proxy_connection_unref(exec_ctx, conn); + // If more data was read from the client since we started this write, + // write that data now. + if (conn->server_deferred_write_buffer.length > 0) { + gpr_slice_buffer_move_into(&conn->server_deferred_write_buffer, + &conn->server_write_buffer); + grpc_endpoint_write(exec_ctx, conn->server_endpoint, + &conn->server_write_buffer, + &conn->on_server_write_done); + } else { + // No more writes. Unref the connection. + proxy_connection_unref(exec_ctx, conn); + } } // Callback for reading data from the client, which will be proxied to @@ -174,14 +194,26 @@ static void on_client_read_done(grpc_exec_ctx* exec_ctx, void* arg, "HTTP proxy client read", error); return; } - // Move read data into write buffer and write it. - // The write operation inherits our reference to conn. - gpr_slice_buffer_move_into(&conn->client_read_buffer, - &conn->server_write_buffer); - grpc_endpoint_write(exec_ctx, conn->server_endpoint, - &conn->server_write_buffer, &conn->on_server_write_done); + // If there is already a pending write (i.e., server_write_buffer is + // not empty), then move the read data into server_deferred_write_buffer, + // and the next write will be requested in on_server_write_done(), when + // the current write is finished. + // + // Otherwise, move the read data into the write buffer and write it. + if (conn->client_write_buffer.length > 0) { + gpr_slice_buffer_move_into(&conn->client_read_buffer, + &conn->server_deferred_write_buffer); + } else { + gpr_slice_buffer_move_into(&conn->client_read_buffer, + &conn->server_write_buffer); + gpr_ref(&conn->refcount); + grpc_endpoint_write(exec_ctx, conn->server_endpoint, + &conn->server_write_buffer, + &conn->on_server_write_done); + } // Read more data. - do_client_read(exec_ctx, conn); + grpc_endpoint_read(exec_ctx, conn->client_endpoint, &conn->client_read_buffer, + &conn->on_client_read_done); } // Callback for reading data from the backend server, which will be @@ -194,14 +226,26 @@ static void on_server_read_done(grpc_exec_ctx* exec_ctx, void* arg, "HTTP proxy server read", error); return; } - // Move read data into write buffer and write it. - // The write operation inherits our reference to conn. - gpr_slice_buffer_move_into(&conn->server_read_buffer, - &conn->client_write_buffer); - grpc_endpoint_write(exec_ctx, conn->client_endpoint, - &conn->client_write_buffer, &conn->on_client_write_done); + // If there is already a pending write (i.e., client_write_buffer is + // not empty), then move the read data into client_deferred_write_buffer, + // and the next write will be requested in on_client_write_done(), when + // the current write is finished. + // + // Otherwise, move the read data into the write buffer and write it. + if (conn->client_write_buffer.length > 0) { + gpr_slice_buffer_move_into(&conn->server_read_buffer, + &conn->client_deferred_write_buffer); + } else { + gpr_slice_buffer_move_into(&conn->server_read_buffer, + &conn->client_write_buffer); + gpr_ref(&conn->refcount); + grpc_endpoint_write(exec_ctx, conn->client_endpoint, + &conn->client_write_buffer, + &conn->on_client_write_done); + } // Read more data. - do_server_read(exec_ctx, conn); + grpc_endpoint_read(exec_ctx, conn->server_endpoint, &conn->server_read_buffer, + &conn->on_server_read_done); } // Callback to write the HTTP response for the CONNECT request. @@ -213,24 +257,14 @@ static void on_write_response_done(grpc_exec_ctx* exec_ctx, void* arg, "HTTP proxy write response", error); return; } - gpr_unref(&conn->refcount); // Clear write buffer. gpr_slice_buffer_reset_and_unref(&conn->client_write_buffer); - // Start reading from both client and server. - do_client_read(exec_ctx, conn); - do_server_read(exec_ctx, conn); -} - -// Start a read from the client. -static void do_client_read(grpc_exec_ctx* exec_ctx, proxy_connection* conn) { + // Start reading from both client and server. One of the read + // requests inherits our ref to conn, but we need to take a new ref + // for the other one. gpr_ref(&conn->refcount); grpc_endpoint_read(exec_ctx, conn->client_endpoint, &conn->client_read_buffer, &conn->on_client_read_done); -} - -// Start a read from the server. -static void do_server_read(grpc_exec_ctx* exec_ctx, proxy_connection* conn) { - gpr_ref(&conn->refcount); grpc_endpoint_read(exec_ctx, conn->server_endpoint, &conn->server_read_buffer, &conn->on_server_read_done); } @@ -350,8 +384,10 @@ static void on_accept(grpc_exec_ctx* exec_ctx, void* arg, grpc_closure_init(&conn->on_server_read_done, on_server_read_done, conn); grpc_closure_init(&conn->on_server_write_done, on_server_write_done, conn); gpr_slice_buffer_init(&conn->client_read_buffer); + gpr_slice_buffer_init(&conn->client_deferred_write_buffer); gpr_slice_buffer_init(&conn->client_write_buffer); gpr_slice_buffer_init(&conn->server_read_buffer); + gpr_slice_buffer_init(&conn->server_deferred_write_buffer); gpr_slice_buffer_init(&conn->server_write_buffer); grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST, &conn->http_request);