serf-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhuij...@apache.org
Subject svn commit: r1708898 - in /serf/trunk: outgoing.c serf.h serf_private.h test/MockHTTPinC/MockHTTP.h test/MockHTTPinC/MockHTTP_private.h test/MockHTTPinC/MockHTTP_server.c test/serf_get.c test/test_ssl.c
Date Thu, 15 Oct 2015 21:52:17 GMT
Author: rhuijben
Date: Thu Oct 15 21:52:17 2015
New Revision: 1708898

URL: http://svn.apache.org/viewvc?rev=1708898&view=rev
Log:
Make the standard serf connections capable of delaying the initial request
writing until the ALPN negotiation is complete, when explicitly requested.

This will allow developing http/2 support within the same connections.
Currently this support is still completely disabled outside the test code.

* outgoing.c
  (request_or_data_pending): Don't assume requests will be written if ther
    is no framing type.
  (serf__conn_update_pollset): Declare always interested in incoming data
    if not in http/1 mode.
  (reset_connection): Reset write_now.
  (read_from_connection): When write_now, do write now to handle switching
    to http/1.1.
  (serf_connection_create): Extend initialization.
  (serf_connection_set_framing_type): New function.

* serf.h
  (serf_connection_framing_type_t): New enum.
  (serf_connection_set_framing_type): New function.

* serf_private.h
  (serf_connection_t): Add two variables.

* test/MockHTTPinC/MockHTTP.h
  (WithProtocol): New define.
  (mhSetServerProtocol): New function.

* test/MockHTTPinC/MockHTTP_private.h
  (mhServCtx_t): Add variable.

* test/MockHTTPinC/MockHTTP_server.c
  (set_server_protocol,
   mhSetServerProtocol): New functions.
  (alpn_select_callback): New function.
  (initSSLCtx): With new openssl, init alpn.

* test/serf_get.c
  (app_baton_t): Add variable.
  (conn_baton_t): Add connection.
  (conn_set_protocol): New function.
  (conn_setup): Initialize negotiation if requested.
  (HTTP2FLAG): New define.
  (options): Add new option. For now #if 0-ed
  (main): Parse new argument. Store connection in baton.

* test/test_ssl.c
  (openssl/applink.c): Wrap in a #pragma to suppress a few dozen warnings.
  (http11_select_protocol,
   http11_alpn_setup): New functions.
  (test_ssl_alpn_negotiate): New test.
  (test_ssl): Add test_ssl_alpn_negotiate.

Modified:
    serf/trunk/outgoing.c
    serf/trunk/serf.h
    serf/trunk/serf_private.h
    serf/trunk/test/MockHTTPinC/MockHTTP.h
    serf/trunk/test/MockHTTPinC/MockHTTP_private.h
    serf/trunk/test/MockHTTPinC/MockHTTP_server.c
    serf/trunk/test/serf_get.c
    serf/trunk/test/test_ssl.c

Modified: serf/trunk/outgoing.c
URL: http://svn.apache.org/viewvc/serf/trunk/outgoing.c?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/outgoing.c (original)
+++ serf/trunk/outgoing.c Thu Oct 15 21:52:17 2015
@@ -106,7 +106,8 @@ request_or_data_pending(serf_request_t *
     reqs_in_progress = conn->completed_requests - conn->completed_responses;
 
     /* Prepare the next request */
-    if (conn->pipelining || (!conn->pipelining && reqs_in_progress == 0))
+    if (conn->framing_type != SERF_CONNECTION_FRAMING_TYPE_NONE
+        && (conn->pipelining || (!conn->pipelining && reqs_in_progress
== 0)))
     {
         /* Skip all requests that have been written completely but we're still
          waiting for a response. */
@@ -174,7 +175,7 @@ apr_status_t serf__conn_update_pollset(s
         desc.reqevents |= APR_POLLIN;
 
         /* Don't write if OpenSSL told us that it needs to read data first. */
-        if (conn->stop_writing != 1) {
+        if (! conn->stop_writing) {
 
             /* If the connection is not closing down and
              *   has unwritten data or
@@ -201,7 +202,9 @@ apr_status_t serf__conn_update_pollset(s
     }
 
     /* If we can have async responses, always look for something to read. */
-    if (conn->async_responses) {
+    if (conn->framing_type != SERF_CONNECTION_FRAMING_TYPE_HTTP1
+        || conn->async_responses)
+    {
         desc.reqevents |= APR_POLLIN;
     }
 
@@ -719,6 +722,7 @@ static apr_status_t reset_connection(ser
     conn->connect_time = 0;
     conn->latency = -1;
     conn->stop_writing = 0;
+    conn->write_now = 0;
     /* conn->pipelining */
 
     conn->status = APR_SUCCESS;
@@ -1274,8 +1278,17 @@ static apr_status_t read_from_connection
             }
 
             /* Unexpected response from the server */
+            if (conn->write_now) {
+                status = write_to_connection(conn);
+
+                if (!SERF_BUCKET_READ_ERROR(status))
+                    status = APR_SUCCESS;
+            }
         }
 
+        if (conn->framing_type == SERF_CONNECTION_FRAMING_TYPE_NONE)
+            break;
+
         /* If the request doesn't have a response bucket, then call the
          * acceptor to get one created.
          */
@@ -1552,7 +1565,10 @@ serf_connection_t *serf_connection_creat
     conn->hit_eof = 0;
     conn->state = SERF_CONN_INIT;
     conn->latency = -1; /* unknown */
+    conn->stop_writing = 0;
+    conn->write_now = 0;
     conn->pipelining = 1;
+    conn->framing_type = SERF_CONNECTION_FRAMING_TYPE_HTTP1;
 
     /* Create a subpool for our connection. */
     apr_pool_create(&conn->skt_pool, conn->pool);
@@ -1736,6 +1752,21 @@ void serf_connection_set_async_responses
     conn->async_handler_baton = handler_baton;
 }
 
+void serf_connection_set_framing_type(
+    serf_connection_t *conn,
+    serf_connection_framing_type_t framing_type)
+{
+    conn->framing_type = framing_type;
+
+    if (conn->skt) {
+        conn->dirty_conn = 1;
+        conn->ctx->dirty_pollset = 1;
+        conn->stop_writing = 0;
+        conn->write_now = 1;
+        serf__conn_update_pollset(conn);
+    }
+}
+
 static serf_request_t *
 create_request(serf_connection_t *conn,
                serf_request_setup_t setup,

Modified: serf/trunk/serf.h
URL: http://svn.apache.org/viewvc/serf/trunk/serf.h?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/serf.h (original)
+++ serf/trunk/serf.h Thu Oct 15 21:52:17 2015
@@ -539,6 +539,24 @@ void serf_connection_set_async_responses
     serf_response_handler_t handler,
     void *handler_baton);
 
+typedef enum serf_connection_framing_type_t {
+  SERF_CONNECTION_FRAMING_TYPE_NONE = 0,
+  SERF_CONNECTION_FRAMING_TYPE_HTTP1,
+  SERF_CONNECTION_FRAMING_TYPE_HTTP2
+} serf_connection_framing_type_t;
+
+/**
+* Sets the connection framing on the connection to the specified type. The
+* NONE type specifies that the framing type is undetermined yet and no
+* requests should be written to the connection until the framing type is
+* set. Connections default to HTTP1 framing.
+*
+* @since New in 1.4.
+*/
+void serf_connection_set_framing_type(
+  serf_connection_t *conn,
+  serf_connection_framing_type_t framing_type);
+
 /**
  * Setup the @a request for delivery on its connection.
  *

Modified: serf/trunk/serf_private.h
URL: http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Thu Oct 15 21:52:17 2015
@@ -377,6 +377,9 @@ struct serf_connection_t {
     /* Max. number of outstanding requests. */
     unsigned int max_outstanding_requests;
 
+    /* Framing type to use on the connection */
+    serf_connection_framing_type_t framing_type;
+
     /* Flag to enable or disable HTTP pipelining. This flag is used internally
        only. */
     int pipelining;
@@ -402,6 +405,9 @@ struct serf_connection_t {
     /* Needs to read first before we can write again. */
     int stop_writing;
 
+    /* Write out information now */
+    int write_now;
+
     /* Configuration shared with buckets and authn plugins */
     serf_config_t *config;
 };

Modified: serf/trunk/test/MockHTTPinC/MockHTTP.h
URL: http://svn.apache.org/viewvc/serf/trunk/test/MockHTTPinC/MockHTTP.h?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/test/MockHTTPinC/MockHTTP.h (original)
+++ serf/trunk/test/MockHTTPinC/MockHTTP.h Thu Oct 15 21:52:17 2015
@@ -152,6 +152,10 @@ typedef struct mhResponseBldr_t mhRespon
 #define     WithPort(port)\
                 mhSetServerPort(__servctx, port)
 
+/* Specify which protocol the server should choose */
+#define     WithProtocol(protocol)\
+                mhSetServerProtocol(__servctx, protocol)
+
 /* Set the maximum number of requests per connection. Default is unlimited */
 #define     WithMaxKeepAliveRequests(maxRequests)\
                 mhSetServerMaxRequestsPerConn(__servctx, maxRequests)
@@ -602,6 +606,7 @@ void mhStartServer(mhServCtx_t *ctx);
 void mhStopServer(mhServCtx_t *ctx);
 mhServerSetupBldr_t *mhSetServerID(mhServCtx_t *ctx, const char *serverID);
 mhServerSetupBldr_t *mhSetServerPort(mhServCtx_t *ctx, unsigned int port);
+mhServerSetupBldr_t *mhSetServerProtocol(mhServCtx_t *ctx, const char *protocols);
 mhServerSetupBldr_t *mhSetServerType(mhServCtx_t *ctx, mhServerType_t type);
 mhServerSetupBldr_t *mhSetServerThreading(mhServCtx_t *ctx,
                                           mhThreading_t threading);

Modified: serf/trunk/test/MockHTTPinC/MockHTTP_private.h
URL: http://svn.apache.org/viewvc/serf/trunk/test/MockHTTPinC/MockHTTP_private.h?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/test/MockHTTPinC/MockHTTP_private.h (original)
+++ serf/trunk/test/MockHTTPinC/MockHTTP_private.h Thu Oct 15 21:52:17 2015
@@ -113,6 +113,7 @@ struct mhServCtx_t {
     const char *hostname;
     const char *serverID;      /* unique id for this server */
     apr_port_t port;
+    const char *alpn;
     apr_pollset_t *pollset;
     apr_socket_t *skt;         /* Server listening socket */
     mhServerType_t type;

Modified: serf/trunk/test/MockHTTPinC/MockHTTP_server.c
URL: http://svn.apache.org/viewvc/serf/trunk/test/MockHTTPinC/MockHTTP_server.c?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/test/MockHTTPinC/MockHTTP_server.c (original)
+++ serf/trunk/test/MockHTTPinC/MockHTTP_server.c Thu Oct 15 21:52:17 2015
@@ -1867,6 +1867,21 @@ mhServerSetupBldr_t *mhSetServerPort(mhS
     return ssb;
 }
 
+static bool set_server_protocol(const mhServerSetupBldr_t *ssb, mhServCtx_t *ctx)
+{
+  ctx->alpn = ssb->ibaton;
+  return YES;
+}
+
+mhServerSetupBldr_t *mhSetServerProtocol(mhServCtx_t *ctx, const char *protocols)
+{
+  apr_pool_t *pool = ctx->pool;
+  mhServerSetupBldr_t *ssb = createServerSetupBldr(pool);
+  ssb->ibaton = apr_pstrdup(pool, protocols);
+  ssb->serversetup = set_server_protocol;
+  return ssb;
+}
+
 /**
  * Builder callback, sets the server type on server CTX.
  */
@@ -2499,6 +2514,21 @@ static apr_status_t initSSL(_mhClientCtx
     return APR_SUCCESS;
 }
 
+static int alpn_select_callback(SSL *ssl,
+                                const unsigned char **out,
+                                unsigned char *outlen,
+                                const unsigned char *in,
+                                unsigned int inlen,
+                                void *arg)
+{
+  const char *select = arg;
+
+  *out = select;
+  *outlen = strlen(select);
+
+  return SSL_TLSEXT_ERR_OK;
+}
+
 /**
  * Inits the OpenSSL context.
  */
@@ -2543,6 +2573,14 @@ static apr_status_t initSSLCtx(_mhClient
             SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_TLSv1_2);
 #endif
 
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L /* >= 1.0.2 */
+        if (cctx->serv_ctx->alpn) {
+            SSL_CTX_set_alpn_select_cb(ssl_ctx->ctx,
+                                       alpn_select_callback,
+                                       cctx->serv_ctx->alpn);
+        }
+#endif
+
         if (cctx->protocols == mhProtoSSLv2) {
             /* In recent versions of OpenSSL, SSLv2 has been disabled by removing
                all SSLv2 ciphers from the cipher string. 

Modified: serf/trunk/test/serf_get.c
URL: http://svn.apache.org/viewvc/serf/trunk/test/serf_get.c?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/test/serf_get.c (original)
+++ serf/trunk/test/serf_get.c Thu Oct 15 21:52:17 2015
@@ -37,6 +37,7 @@ typedef struct app_baton_t {
     const char *hostinfo;
     int using_ssl;
     int head_request;
+    int negotiate_http2;
     const char *pem_path;
     const char *pem_pwd;
     serf_bucket_alloc_t *bkt_alloc;
@@ -46,6 +47,7 @@ typedef struct app_baton_t {
 typedef struct conn_baton_t {
     app_baton_t *app;
     serf_ssl_context_t *ssl_ctx;
+    serf_connection_t *conn;
 } conn_baton_t;
 
 static void closed_connection(serf_connection_t *conn,
@@ -181,11 +183,30 @@ static apr_status_t print_certs(void *da
     return APR_SUCCESS;
 }
 
+/* Implements serf_ssl_protocol_result_cb_t for conn_setup */
+static apr_status_t conn_set_protocol(void *baton,
+                                      const char *protocol)
+{
+    conn_baton_t *conn_ctx = baton;
+
+    if (!strcmp(protocol, "h2")) {
+        serf_connection_set_framing_type(
+                  conn_ctx->conn,
+                  SERF_CONNECTION_FRAMING_TYPE_HTTP2);
+    } else /* "http/1.1" or "" */ {
+        serf_connection_set_framing_type(
+                  conn_ctx->conn,
+                  SERF_CONNECTION_FRAMING_TYPE_HTTP1);
+    }
+
+    return APR_SUCCESS;
+}
+
 static apr_status_t conn_setup(apr_socket_t *skt,
-                                serf_bucket_t **input_bkt,
-                                serf_bucket_t **output_bkt,
-                                void *setup_baton,
-                                apr_pool_t *pool)
+                               serf_bucket_t **input_bkt,
+                               serf_bucket_t **output_bkt,
+                               void *setup_baton,
+                               apr_pool_t *pool)
 {
     serf_bucket_t *c;
     conn_baton_t *conn_ctx = setup_baton;
@@ -219,6 +240,20 @@ static apr_status_t conn_setup(apr_socke
                                               ctx,
                                               pool);
         }
+
+        if (ctx->negotiate_http2) {
+            if (!serf_ssl_negotiate_protocol(conn_ctx->ssl_ctx,
+                                             "h2,http/1.1",
+                                             conn_set_protocol, conn_ctx))
+            {
+                serf_bucket_t *bkt;
+
+                /* Disable sending initial data until negotiate is done */
+                serf_connection_set_framing_type(
+                              conn_ctx->conn,
+                              SERF_CONNECTION_FRAMING_TYPE_NONE);
+            }
+        }
     }
 
     *input_bkt = c;
@@ -449,6 +484,7 @@ credentials_callback(char **username,
 /* Value for 'no short code' should be > 255 */
 #define CERTFILE 256
 #define CERTPWD  257
+#define HTTP2FLAG 258
 
 static const apr_getopt_option_t options[] =
 {
@@ -468,6 +504,9 @@ static const apr_getopt_option_t options
     {"certpwd", CERTPWD, 1, "<password> Password for the SSL client certificate"},
     {NULL,      'r', 1, "<header:value> Use <header:value> as request header"},
     {"debug",   'd', 0, "Enable debugging"},
+#if 0
+    {"http2",   HTTP2FLAG, 0, "Enable http2 (https only)"}
+#endif
 };
 
 static void print_usage(apr_pool_t *pool)
@@ -510,7 +549,7 @@ int main(int argc, const char **argv)
     const char *raw_url, *method, *req_body_path = NULL;
     int count, inflight, conn_count;
     int i;
-    int print_headers, debug;
+    int print_headers, debug, negotiate_http2;
     const char *username = NULL;
     const char *password = "";
     const char *pem_path = NULL, *pem_pwd = NULL;
@@ -535,8 +574,8 @@ int main(int argc, const char **argv)
     print_headers = 0;
     /* Do not debug by default. */
     debug = 0;
+    negotiate_http2 = 0;
 
-    
     apr_getopt_init(&opt, pool, argc, argv);
     while ((status = apr_getopt_long(opt, options, &opt_c, &opt_arg)) ==
            APR_SUCCESS) {
@@ -628,6 +667,9 @@ int main(int argc, const char **argv)
         case CERTPWD:
             pem_pwd = opt_arg;
             break;
+        case HTTP2FLAG:
+            negotiate_http2 = 1;
+            break;
         case 'v':
             puts("Serf version: " SERF_VERSION_STRING);
             exit(0);
@@ -653,9 +695,12 @@ int main(int argc, const char **argv)
 
     if (strcasecmp(url.scheme, "https") == 0) {
         app_ctx.using_ssl = 1;
+
+        app_ctx.negotiate_http2 = negotiate_http2;
     }
     else {
         app_ctx.using_ssl = 0;
+        app_ctx.negotiate_http2 = FALSE;
     }
 
     if (strcasecmp(method, "HEAD") == 0) {
@@ -763,6 +808,8 @@ int main(int argc, const char **argv)
             exit(1);
         }
 
+        conn_ctx->conn = connections[i];
+
         serf_connection_set_max_outstanding_requests(connections[i], inflight);
     }
 

Modified: serf/trunk/test/test_ssl.c
URL: http://svn.apache.org/viewvc/serf/trunk/test/test_ssl.c?rev=1708898&r1=1708897&r2=1708898&view=diff
==============================================================================
--- serf/trunk/test/test_ssl.c (original)
+++ serf/trunk/test/test_ssl.c Thu Oct 15 21:52:17 2015
@@ -31,7 +31,14 @@
 #if defined(WIN32) && defined(_DEBUG)
 /* Include this file to allow running a Debug build of serf with a Release
    build of OpenSSL. */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#pragma warning(push)
+#pragma warning(disable: 4152)
+#endif
 #include <openssl/applink.c>
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+#pragma warning(pop)
+#endif
 #endif
 
 /* Test setting up the openssl library. */
@@ -2110,6 +2117,95 @@ static void test_ssl_server_cert_with_sa
     CuAssertTrue(tc, tb->result_flags & TEST_RESULT_SERVERCERTCB_CALLED);
 }
 
+static apr_status_t http11_select_protocol(void *baton,
+                                           const char *protocol)
+{
+  test_baton_t *tb = baton;
+
+  if (! strcmp(protocol, "http/1.1"))
+      serf_connection_set_framing_type(tb->connection,
+                                       SERF_CONNECTION_FRAMING_TYPE_HTTP1);
+  else
+      return APR_EGENERAL; /* Failure */
+
+  return APR_SUCCESS;
+}
+
+static apr_status_t http11_alpn_setup(apr_socket_t *skt,
+                                      serf_bucket_t **input_bkt,
+                                      serf_bucket_t **output_bkt,
+                                      void *setup_baton,
+                                      apr_pool_t *pool)
+{
+  test_baton_t *tb = setup_baton;
+  apr_status_t status;
+
+  status = default_https_conn_setup(skt, input_bkt, output_bkt,
+                                    setup_baton, pool);
+  if (status)
+    return status;
+
+  status = serf_ssl_negotiate_protocol(tb->ssl_context, "h2,http/1.1",
+                                       http11_select_protocol, tb);
+
+  if (!status) {
+      /* Delay writing out the protocol type until we know how */
+      serf_connection_set_framing_type(tb->connection,
+                                       SERF_CONNECTION_FRAMING_TYPE_NONE);
+  }
+
+  return APR_SUCCESS;
+}
+
+
+static void test_ssl_alpn_negotiate(CuTest *tc)
+{
+    test_baton_t *tb = tc->testBaton;
+    handler_baton_t handler_ctx[1];
+    const int num_requests = sizeof(handler_ctx)/sizeof(handler_ctx[0]);
+    int expected_failures;
+    apr_status_t status;
+    static const char *server_cert[] = { "serfservercert.pem",
+                                         NULL };
+
+    /* Set up a test context and a https server */
+    tb->mh = mhInit();
+
+    InitMockServers(tb->mh)
+      SetupServer(WithHTTPS, WithID("server"), WithPort(30080),
+                  WithProtocol("http/1.1Q"),
+                  WithCertificateFilesPrefix(get_srcdir_file(tb->pool,
+                                                             "test/certs")),
+                  WithCertificateKeyFile(server_key),
+                  WithCertificateKeyPassPhrase("serftest"),
+                  WithCertificateFileArray(server_cert))
+    EndInit
+
+    tb->serv_port = mhServerPortNr(tb->mh);
+    tb->serv_host = apr_psprintf(tb->pool, "%s:%d", "localhost", tb->serv_port);
+    tb->serv_url = apr_psprintf(tb->pool, "https://%s", tb->serv_host);
+
+    status = setup_test_client_https_context(tb,
+                                             http11_alpn_setup,
+                                             ssl_server_cert_cb_expect_failures,
+                                             tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+    expected_failures = SERF_SSL_CERT_UNKNOWNCA;
+    tb->user_baton = &expected_failures;
+
+    Given(tb->mh)
+      GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"),
+                 HeaderEqualTo("Host", tb->serv_host))
+        Respond(WithCode(200), WithChunkedBody(""))
+    EndGiven
+
+    create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+
+    run_client_and_mock_servers_loops_expect_ok(tc, tb, num_requests,
+                                                handler_ctx, tb->pool);
+}
+
 CuSuite *test_ssl(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -2154,6 +2250,7 @@ CuSuite *test_ssl(void)
     SUITE_ADD_TEST(suite, test_ssl_server_cert_with_cnsan_nul_byte);
     SUITE_ADD_TEST(suite, test_ssl_server_cert_with_san_and_empty_cb);
     SUITE_ADD_TEST(suite, test_ssl_renegotiate);
+    SUITE_ADD_TEST(suite, test_ssl_alpn_negotiate);
 
     return suite;
 }



Mime
View raw message