From 942ef7d68cdefe6fa1365ccb61845c8820b00a67 Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Fri, 13 Jan 2017 15:11:06 -0800 Subject: [PATCH 1/2] Support HEAD request in transcoding --- WORKSPACE | 2 +- src/nginx/grpc_server_call.cc | 5 + src/nginx/t/BUILD | 1 + src/nginx/t/transcoding_head.t | 212 +++++++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/nginx/t/transcoding_head.t diff --git a/WORKSPACE b/WORKSPACE index 8c58161ab..9c5d68349 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -26,7 +26,7 @@ # # A Bazel (http://bazel.io) workspace for the Google Cloud Endpoints runtime. -ISTIO_PROXY = "8b3a4429965ed1e792704e795e5d22782eca505b" +ISTIO_PROXY = "ee3cdeeb33b1de0b87c80809802de5246cbb3e25" git_repository( name = "nginx", diff --git a/src/nginx/grpc_server_call.cc b/src/nginx/grpc_server_call.cc index cd6bbb9ff..8ad492370 100644 --- a/src/nginx/grpc_server_call.cc +++ b/src/nginx/grpc_server_call.cc @@ -78,6 +78,11 @@ void TrimFront(std::vector *slices, size_t count) { // returns NGX_AGAIN. ngx_int_t ngx_esp_write_output(ngx_http_request_t *r, ngx_chain_t *out, ngx_http_event_handler_pt write_event_handler) { + // Don't write response body if the request is HEAD + if (r->header_only) { + return NGX_OK; + } + ngx_int_t rc = ngx_http_output_filter(r, out); if (rc == NGX_OK) { diff --git a/src/nginx/t/BUILD b/src/nginx/t/BUILD index 1511cbe17..02b2e5926 100644 --- a/src/nginx/t/BUILD +++ b/src/nginx/t/BUILD @@ -280,6 +280,7 @@ nginx_suite( "transcoding_bindings.t", "transcoding_deep_struct.t", "transcoding_errors.t", + "transcoding_head.t", "transcoding_ignore_unknown_fields.t", "transcoding_large.t", "transcoding_metadata.t", diff --git a/src/nginx/t/transcoding_head.t b/src/nginx/t/transcoding_head.t new file mode 100644 index 000000000..8f3a92dec --- /dev/null +++ b/src/nginx/t/transcoding_head.t @@ -0,0 +1,212 @@ +# Copyright (C) Extensible Service Proxy Authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +################################################################################ +# +use strict; +use warnings; + +################################################################################ + +use src::nginx::t::ApiManager; # Must be first (sets up import path to the Nginx test module) +use src::nginx::t::HttpServer; +use src::nginx::t::ServiceControl; +use Test::Nginx; # Imports Nginx's test module +use Test::More; # And the test framework +use JSON::PP; + +################################################################################ + +# Port assignments +my $NginxPort = ApiManager::pick_port(); +my $ServiceControlPort = ApiManager::pick_port(); +my $GrpcServerPort = ApiManager::pick_port(); + +my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(13); + +$t->write_file('service.pb.txt', + ApiManager::get_transcoding_test_service_config( + 'endpoints-transcoding-test.cloudendpointsapis.com', + "http://127.0.0.1:${ServiceControlPort}") . + ApiManager::read_test_file('testdata/logs_metrics.pb.txt')); + +$t->write_file('server_config.pb.txt', ApiManager::disable_service_control_cache); + +$t->write_file_expand('nginx.conf', <run_daemon(\&service_control, $t, $ServiceControlPort, 'servicecontrol.log', $report_done); +ApiManager::run_transcoding_test_server($t, 'server.log', "127.0.0.1:${GrpcServerPort}"); + +is($t->waitforsocket("127.0.0.1:${ServiceControlPort}"), 1, "Service control socket ready."); +is($t->waitforsocket("127.0.0.1:${GrpcServerPort}"), 1, "GRPC test server socket ready."); +$t->run(); +is($t->waitforsocket("127.0.0.1:${NginxPort}"), 1, "Nginx socket ready."); + +################################################################################ + +my $shelves_response = ApiManager::http($NginxPort, <waitforfile("$t->{_testdir}/${report_done}"), 1, 'Report body file ready.'); +$t->stop_daemons(); + +# Check the requests that the backend has received +my $shelves_request_expected = {}; + +my $server_output = $t->read_file('server.log'); +my @server_requests = split /\r\n\r\n/, $server_output; + +ok(ApiManager::compare_json($server_requests[0], $shelves_request_expected), 'Server received request'); + +# Check responses +my $shelves_response_expected = { + 'shelves' => [ + {'id' => '1', 'theme' => 'Fiction'}, + {'id' => '2', 'theme' => 'Fantasy'}, + ] +}; + +my ($headers, $actual_body) = split /\r\n\r\n/, $shelves_response, 2; +like($headers, qr/HTTP\/1\.1 200 OK/, 'Returned HTTP 200.'); +like($headers, qr/Content-Type: application\/json/, 'Returned Content-Type'); +is('', $actual_body, 'Response body is empty'); + +# Expect 2 service control calls for 1 requests. +my @servicecontrol_requests = ApiManager::read_http_stream($t, 'servicecontrol.log'); +is(scalar @servicecontrol_requests, 2, 'Service control was called 2 times'); + +while (my ($i, $r) = each @servicecontrol_requests) { + if ($i % 2 == 0) { + like($r->{uri}, qr/:check$/, "Check has correct uri for ${i}"); + } else { + like($r->{uri}, qr/:report$/, "Report has correct uri for ${i}"); + } +} + +# :check +my $r = shift @servicecontrol_requests; +my $check_body = ServiceControl::convert_proto($r->{body}, 'check_request', 'json'); +my $expected_check_body = { + 'serviceName' => 'endpoints-transcoding-test.cloudendpointsapis.com', + 'serviceConfigId' => '2016-08-25r1', + 'operation' => { + 'consumerId' => 'api_key:api-key', + 'operationName' => 'endpoints.examples.bookstore.Bookstore.ListShelves', + 'labels' => { + 'servicecontrol.googleapis.com/caller_ip' => '127.0.0.1', + 'servicecontrol.googleapis.com/service_agent' => ServiceControl::service_agent(), + 'servicecontrol.googleapis.com/user_agent' => 'ESP', + } + } +}; +ok(ServiceControl::compare_json($check_body, $expected_check_body), 'Check body is received.'); + +# :report +$r = shift @servicecontrol_requests; +my $report_body = ServiceControl::convert_proto($r->{body}, 'report_request', 'json'); +my $expected_report_body = ServiceControl::gen_report_body({ + 'serviceName' => 'endpoints-transcoding-test.cloudendpointsapis.com', + 'api_method' => 'endpoints.examples.bookstore.Bookstore.ListShelves', + 'serviceConfigId' => '2016-08-25r1', + 'url' => '/shelves?key=api-key', + 'api_key' => 'api-key', + 'producer_project_id' => 'endpoints-transcoding-test', + 'location' => 'us-central1', + 'api_name' => 'endpoints.examples.bookstore.Bookstore', + 'api_version' => 'v1', + 'http_method' => 'HEAD', + 'log_message' => 'Method: endpoints.examples.bookstore.Bookstore.ListShelves', + 'response_code' => '200', + 'request_size' => 52, + 'response_size' => 122, + 'request_bytes' => 52, + 'response_bytes' => 122, + 'streaming_request_message_counts' => 1, + 'streaming_response_message_counts' => 1, + }); +ok(ServiceControl::compare_json($report_body, $expected_report_body), 'Report body is received.'); + +################################################################################ + +sub service_control { + my ($t, $port, $file, $done) = @_; + + my $server = HttpServer->new($port, $t->testdir() . '/' . $file) + or die "Can't create test server socket: $!\n"; + + local $SIG{PIPE} = 'IGNORE'; + + $server->on_sub('POST', '/v1/services/endpoints-transcoding-test.cloudendpointsapis.com:check', sub { + my ($headers, $body, $client) = @_; + print $client <on_sub('POST', '/v1/services/endpoints-transcoding-test.cloudendpointsapis.com:report', sub { + my ($headers, $body, $client) = @_; + print $client <write_file($done, ":report done"); + }); + + $server->run(); +} + +################################################################################ From 292697e859ee809a2cb582ef0591226ca774e1d4 Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Fri, 13 Jan 2017 16:52:24 -0800 Subject: [PATCH 2/2] Removed unused variables --- src/nginx/t/transcoding_head.t | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/nginx/t/transcoding_head.t b/src/nginx/t/transcoding_head.t index 8f3a92dec..cace262e9 100644 --- a/src/nginx/t/transcoding_head.t +++ b/src/nginx/t/transcoding_head.t @@ -107,14 +107,6 @@ my @server_requests = split /\r\n\r\n/, $server_output; ok(ApiManager::compare_json($server_requests[0], $shelves_request_expected), 'Server received request'); -# Check responses -my $shelves_response_expected = { - 'shelves' => [ - {'id' => '1', 'theme' => 'Fiction'}, - {'id' => '2', 'theme' => 'Fantasy'}, - ] -}; - my ($headers, $actual_body) = split /\r\n\r\n/, $shelves_response, 2; like($headers, qr/HTTP\/1\.1 200 OK/, 'Returned HTTP 200.'); like($headers, qr/Content-Type: application\/json/, 'Returned Content-Type');