diff --git a/Makefile b/Makefile index aec16ee83ec35ae2847034d4e0eaba82d9979db6..ed75b4cea7c26c592b0e6f712a7c800895adefc4 100644 --- a/Makefile +++ b/Makefile @@ -833,6 +833,7 @@ hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test httpcli_format_request_test: $(BINDIR)/$(CONFIG)/httpcli_format_request_test httpcli_parser_test: $(BINDIR)/$(CONFIG)/httpcli_parser_test httpcli_test: $(BINDIR)/$(CONFIG)/httpcli_test +httpscli_test: $(BINDIR)/$(CONFIG)/httpscli_test init_test: $(BINDIR)/$(CONFIG)/init_test invalid_call_argument_test: $(BINDIR)/$(CONFIG)/invalid_call_argument_test json_rewrite: $(BINDIR)/$(CONFIG)/json_rewrite @@ -1933,6 +1934,7 @@ buildtests_c: privatelibs_c \ $(BINDIR)/$(CONFIG)/httpcli_format_request_test \ $(BINDIR)/$(CONFIG)/httpcli_parser_test \ $(BINDIR)/$(CONFIG)/httpcli_test \ + $(BINDIR)/$(CONFIG)/httpscli_test \ $(BINDIR)/$(CONFIG)/init_test \ $(BINDIR)/$(CONFIG)/invalid_call_argument_test \ $(BINDIR)/$(CONFIG)/json_rewrite \ @@ -2964,6 +2966,8 @@ test_c: buildtests_c $(Q) $(BINDIR)/$(CONFIG)/httpcli_parser_test || ( echo test httpcli_parser_test failed ; exit 1 ) $(E) "[RUN] Testing httpcli_test" $(Q) $(BINDIR)/$(CONFIG)/httpcli_test || ( echo test httpcli_test failed ; exit 1 ) + $(E) "[RUN] Testing httpscli_test" + $(Q) $(BINDIR)/$(CONFIG)/httpscli_test || ( echo test httpscli_test failed ; exit 1 ) $(E) "[RUN] Testing init_test" $(Q) $(BINDIR)/$(CONFIG)/init_test || ( echo test init_test failed ; exit 1 ) $(E) "[RUN] Testing invalid_call_argument_test" @@ -10830,6 +10834,35 @@ endif endif +HTTPSCLI_TEST_SRC = \ + test/core/httpcli/httpscli_test.c \ + +HTTPSCLI_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTPSCLI_TEST_SRC)))) +ifeq ($(NO_SECURE),true) + +# You can't build secure targets if you don't have OpenSSL. + +$(BINDIR)/$(CONFIG)/httpscli_test: openssl_dep_error + +else + +$(BINDIR)/$(CONFIG)/httpscli_test: $(HTTPSCLI_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a + $(E) "[LD] Linking $@" + $(Q) mkdir -p `dirname $@` + $(Q) $(LD) $(LDFLAGS) $(HTTPSCLI_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/httpscli_test + +endif + +$(OBJDIR)/$(CONFIG)/test/core/httpcli/httpscli_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a +deps_httpscli_test: $(HTTPSCLI_TEST_OBJS:.o=.dep) + +ifneq ($(NO_SECURE),true) +ifneq ($(NO_DEPS),true) +-include $(HTTPSCLI_TEST_OBJS:.o=.dep) +endif +endif + + INIT_TEST_SRC = \ test/core/surface/init_test.c \ diff --git a/build.yaml b/build.yaml index 2891b7e19e123be1d49017b795ada180f301822d..3b8b8be94f577a8b27d959e566104b716ea04dcf 100644 --- a/build.yaml +++ b/build.yaml @@ -1357,6 +1357,18 @@ targets: - mac - linux - posix +- name: httpscli_test + build: test + language: c + src: + - test/core/httpcli/httpscli_test.c + deps: + - grpc_test_util + - grpc + - gpr_test_util + - gpr + platforms: + - linux - name: init_test build: test language: c diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index a87f1aa87bcba0005f0827f7ca688266b45a8950..b5cd8d8d2acc88dacb92ec3a101c3c7bae205942 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -53,6 +53,7 @@ typedef struct { size_t next_address; grpc_endpoint *ep; char *host; + char *ssl_host_override; gpr_timespec deadline; int have_read_byte; const grpc_httpcli_handshaker *handshaker; @@ -106,6 +107,7 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, } gpr_slice_unref(req->request_text); gpr_free(req->host); + gpr_free(req->ssl_host_override); grpc_iomgr_unregister_object(&req->iomgr_obj); gpr_slice_buffer_destroy(&req->incoming); gpr_slice_buffer_destroy(&req->outgoing); @@ -180,8 +182,10 @@ static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { next_address(exec_ctx, req); return; } - req->handshaker->handshake(exec_ctx, req, req->ep, req->host, - on_handshake_done); + req->handshaker->handshake( + exec_ctx, req, req->ep, + req->ssl_host_override ? req->ssl_host_override : req->host, + on_handshake_done); } static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { @@ -231,6 +235,7 @@ static void internal_request_begin( gpr_slice_buffer_init(&req->outgoing); grpc_iomgr_register_object(&req->iomgr_obj, name); req->host = gpr_strdup(request->host); + req->ssl_host_override = gpr_strdup(request->ssl_host_override); grpc_pollset_set_add_pollset(exec_ctx, &req->context->pollset_set, req->pollset); diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h index 6469c2f03e7e9d7c677b15d1d805b98c66fd2c70..30875d71f13b7767f325e1d25ea39d0e7593a266 100644 --- a/src/core/httpcli/httpcli.h +++ b/src/core/httpcli/httpcli.h @@ -74,6 +74,8 @@ extern const grpc_httpcli_handshaker grpc_httpcli_ssl; typedef struct grpc_httpcli_request { /* The host name to connect to */ char *host; + /* The host to verify in the SSL handshake (or NULL) */ + char *ssl_host_override; /* The path of the resource to fetch */ char *path; /* Additional headers: count and key/values; the following are supplied diff --git a/test/core/httpcli/httpcli_test.c b/test/core/httpcli/httpcli_test.c index 4012f995c7b0c8bc47d619e32c315d0428d82384..d47774251a9086a8ecb72617be04a86112e61a4d 100644 --- a/test/core/httpcli/httpcli_test.c +++ b/test/core/httpcli/httpcli_test.c @@ -69,13 +69,13 @@ static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); } -static void test_get(int use_ssl, int port) { +static void test_get(int port) { grpc_httpcli_request req; char *host; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; g_done = 0; - gpr_log(GPR_INFO, "running %s with use_ssl=%d.", "test_get", use_ssl); + gpr_log(GPR_INFO, "test_get"); gpr_asprintf(&host, "localhost:%d", port); gpr_log(GPR_INFO, "requesting from %s", host); @@ -83,7 +83,7 @@ static void test_get(int use_ssl, int port) { memset(&req, 0, sizeof(req)); req.host = host; req.path = "/get"; - req.handshaker = use_ssl ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext; + req.handshaker = &grpc_httpcli_plaintext; grpc_httpcli_get(&exec_ctx, &g_context, &g_pollset, &req, n_seconds_time(15), on_finish, (void *)42); @@ -100,13 +100,13 @@ static void test_get(int use_ssl, int port) { gpr_free(host); } -static void test_post(int use_ssl, int port) { +static void test_post(int port) { grpc_httpcli_request req; char *host; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; g_done = 0; - gpr_log(GPR_INFO, "running %s with use_ssl=%d.", "test_post", (int)use_ssl); + gpr_log(GPR_INFO, "test_post"); gpr_asprintf(&host, "localhost:%d", port); gpr_log(GPR_INFO, "posting to %s", host); @@ -114,7 +114,7 @@ static void test_post(int use_ssl, int port) { memset(&req, 0, sizeof(req)); req.host = host; req.path = "/post"; - req.handshaker = use_ssl ? &grpc_httpcli_ssl : &grpc_httpcli_plaintext; + req.handshaker = &grpc_httpcli_plaintext; grpc_httpcli_post(&exec_ctx, &g_context, &g_pollset, &req, "hello", 5, n_seconds_time(15), on_finish, (void *)42); @@ -170,8 +170,8 @@ int main(int argc, char **argv) { grpc_httpcli_context_init(&g_context); grpc_pollset_init(&g_pollset); - test_get(0, port); - test_post(0, port); + test_get(port); + test_post(port); grpc_httpcli_context_destroy(&g_context); grpc_closure_init(&destroyed, destroy_pollset, &g_pollset); diff --git a/test/core/httpcli/httpscli_test.c b/test/core/httpcli/httpscli_test.c new file mode 100644 index 0000000000000000000000000000000000000000..b1c1913cae0d6c8393d59a45529c206b9d4f806a --- /dev/null +++ b/test/core/httpcli/httpscli_test.c @@ -0,0 +1,188 @@ +/* + * + * 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/httpcli/httpcli.h" + +#include <string.h> + +#include <grpc/grpc.h> +#include "src/core/iomgr/iomgr.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/subprocess.h> +#include <grpc/support/sync.h> +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +static int g_done = 0; +static grpc_httpcli_context g_context; +static grpc_pollset g_pollset; + +static gpr_timespec n_seconds_time(int seconds) { + return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(seconds); +} + +static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, + const grpc_httpcli_response *response) { + const char *expect = + "<html><head><title>Hello world!</title></head>" + "<body><p>This is a test</p></body></html>"; + GPR_ASSERT(arg == (void *)42); + GPR_ASSERT(response); + GPR_ASSERT(response->status == 200); + GPR_ASSERT(response->body_length == strlen(expect)); + GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length)); + gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset)); + g_done = 1; + grpc_pollset_kick(&g_pollset, NULL); + gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); +} + +static void test_get(int port) { + grpc_httpcli_request req; + char *host; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + g_done = 0; + gpr_log(GPR_INFO, "test_get"); + + gpr_asprintf(&host, "localhost:%d", port); + gpr_log(GPR_INFO, "requesting from %s", host); + + memset(&req, 0, sizeof(req)); + req.host = host; + req.ssl_host_override = "foo.test.google.fr"; + req.path = "/get"; + req.handshaker = &grpc_httpcli_ssl; + + grpc_httpcli_get(&exec_ctx, &g_context, &g_pollset, &req, n_seconds_time(15), + on_finish, (void *)42); + gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset)); + while (!g_done) { + grpc_pollset_worker worker; + grpc_pollset_work(&exec_ctx, &g_pollset, &worker, + gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20)); + gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); + grpc_exec_ctx_finish(&exec_ctx); + gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset)); + } + gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); + gpr_free(host); +} + +static void test_post(int port) { + grpc_httpcli_request req; + char *host; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + g_done = 0; + gpr_log(GPR_INFO, "test_post"); + + gpr_asprintf(&host, "localhost:%d", port); + gpr_log(GPR_INFO, "posting to %s", host); + + memset(&req, 0, sizeof(req)); + req.host = host; + req.ssl_host_override = "foo.test.google.fr"; + req.path = "/post"; + req.handshaker = &grpc_httpcli_ssl; + + grpc_httpcli_post(&exec_ctx, &g_context, &g_pollset, &req, "hello", 5, + n_seconds_time(15), on_finish, (void *)42); + gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset)); + while (!g_done) { + grpc_pollset_worker worker; + grpc_pollset_work(&exec_ctx, &g_pollset, &worker, + gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20)); + gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); + grpc_exec_ctx_finish(&exec_ctx); + gpr_mu_lock(GRPC_POLLSET_MU(&g_pollset)); + } + gpr_mu_unlock(GRPC_POLLSET_MU(&g_pollset)); + gpr_free(host); +} + +static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, int success) { + grpc_pollset_destroy(p); +} + +int main(int argc, char **argv) { + grpc_closure destroyed; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_subprocess *server; + char *me = argv[0]; + char *lslash = strrchr(me, '/'); + char *args[5]; + char root[1024]; + int port = grpc_pick_unused_port_or_die(); + + /* figure out where we are */ + if (lslash) { + memcpy(root, me, (size_t)(lslash - me)); + root[lslash - me] = 0; + } else { + strcpy(root, "."); + } + + /* start the server */ + gpr_asprintf(&args[0], "%s/../../test/core/httpcli/test_server.py", root); + args[1] = "--port"; + gpr_asprintf(&args[2], "%d", port); + args[3] = "--ssl"; + server = gpr_subprocess_create(4, (const char **)args); + GPR_ASSERT(server); + gpr_free(args[0]); + gpr_free(args[2]); + + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_seconds(5, GPR_TIMESPAN))); + + grpc_test_init(argc, argv); + grpc_init(); + grpc_httpcli_context_init(&g_context); + grpc_pollset_init(&g_pollset); + + test_get(port); + test_post(port); + + grpc_httpcli_context_destroy(&g_context); + grpc_closure_init(&destroyed, destroy_pollset, &g_pollset); + grpc_pollset_shutdown(&exec_ctx, &g_pollset, &destroyed); + grpc_exec_ctx_finish(&exec_ctx); + grpc_shutdown(); + + gpr_subprocess_destroy(server); + + return 0; +} diff --git a/test/core/httpcli/test_server.py b/test/core/httpcli/test_server.py index 4aaf5e30f8dbfbc9e61edff003284045e65d2279..225c2a6b0fcb0183e50f47a26f273721f8ef48f3 100755 --- a/test/core/httpcli/test_server.py +++ b/test/core/httpcli/test_server.py @@ -4,9 +4,18 @@ import argparse import BaseHTTPServer +import os +import ssl +import sys + +_PEM = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../../..', 'src/core/tsi/test_creds/server1.pem')) +_KEY = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../../..', 'src/core/tsi/test_creds/server1.key')) +print _PEM +open(_PEM).close() argp = argparse.ArgumentParser(description='Server for httpcli_test') argp.add_argument('-p', '--port', default=10080, type=int) +argp.add_argument('-s', '--ssl', default=False, action='store_true') args = argp.parse_args() print 'server running on port %d' % args.port @@ -28,4 +37,7 @@ class Handler(BaseHTTPServer.BaseHTTPRequestHandler): if self.path == '/post' and content == 'hello': self.good() -BaseHTTPServer.HTTPServer(('', args.port), Handler).serve_forever() +httpd = BaseHTTPServer.HTTPServer(('localhost', args.port), Handler) +if args.ssl: + httpd.socket = ssl.wrap_socket(httpd.socket, certfile=_PEM, keyfile=_KEY, server_side=True) +httpd.serve_forever() diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index e3df912480389b105eaaef8e4c53f25e0125df6c..006f4bcdf1f4bd2d3872165dd48ef6a60afc3add 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -152,7 +152,10 @@ class CLanguage(object): else: binary = 'bins/%s/%s' % (config.build_config, target['name']) if os.path.isfile(binary): - out.append(config.job_spec([binary], [binary])) + out.append(config.job_spec([binary], [binary], + environ={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH': + os.path.abspath(os.path.dirname( + sys.argv[0]) + '/../../src/core/tsi/test_creds/ca.pem')})) elif args.regex == '.*' or platform_string() == 'windows': print '\nWARNING: binary not found, skipping', binary return sorted(out) @@ -192,6 +195,7 @@ class CLanguage(object): def __str__(self): return self.make_target + class NodeLanguage(object): def test_specs(self, config, args): diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json index af94a1a38d25fca27753dae0c7f4f01d945f6b91..8822126099e4ef7697ccf2f8e3f1b570cbda8ae3 100644 --- a/tools/run_tests/sources_and_headers.json +++ b/tools/run_tests/sources_and_headers.json @@ -742,6 +742,20 @@ "test/core/httpcli/httpcli_test.c" ] }, + { + "deps": [ + "gpr", + "gpr_test_util", + "grpc", + "grpc_test_util" + ], + "headers": [], + "language": "c", + "name": "httpscli_test", + "src": [ + "test/core/httpcli/httpscli_test.c" + ] + }, { "deps": [ "gpr", diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json index 6ec1955cff3f6faee60c07c7ef7260e8ba86353b..94be72b4a26209982df1710c7231aec62afb65e4 100644 --- a/tools/run_tests/tests.json +++ b/tools/run_tests/tests.json @@ -851,6 +851,18 @@ "posix" ] }, + { + "ci_platforms": [ + "linux" + ], + "exclude_configs": [], + "flaky": false, + "language": "c", + "name": "httpscli_test", + "platforms": [ + "linux" + ] + }, { "ci_platforms": [ "linux",