]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tests: TLS session sharing test
authorStefan Eissing <stefan@eissing.org>
Tue, 15 Aug 2023 11:24:56 +0000 (13:24 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 17 Aug 2023 11:44:55 +0000 (13:44 +0200)
- test TLS session sharing with special test client
- expect failure with wolfSSL
- disable flaky wolfSSL test_02_07b

Closes #11675

tests/http/clients/.gitignore
tests/http/clients/Makefile.inc
tests/http/clients/tls-session-reuse.c [new file with mode: 0644]
tests/http/test_02_download.py

index 2f2d6a7095d9c7ec08c2c6190c59114e90440ef5..9c08b202a973c17fb33a339d32a091fde1a0d0cf 100644 (file)
@@ -6,4 +6,5 @@ h2-serverpush
 h2-download
 ws-data
 ws-pingpong
-h2-upgrade-extreme
\ No newline at end of file
+h2-upgrade-extreme
+tls-session-reuse
\ No newline at end of file
index 1ce98d5b7bd40d81a33b8f440b77ccb0fc485c9f..dace3a933c62e30f6362bf58d80237827f6255e0 100644 (file)
@@ -28,4 +28,5 @@ check_PROGRAMS = \
   h2-download \
   ws-data \
   ws-pingpong \
-  h2-upgrade-extreme
+  h2-upgrade-extreme \
+  tls-session-reuse
diff --git a/tests/http/clients/tls-session-reuse.c b/tests/http/clients/tls-session-reuse.c
new file mode 100644 (file)
index 0000000..0309276
--- /dev/null
@@ -0,0 +1,306 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+/* <DESC>
+ * TLS session reuse
+ * </DESC>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+/* #include <error.h> */
+#include <errno.h>
+#include <curl/curl.h>
+#include <curl/mprintf.h>
+
+
+static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
+{
+  /*
+   * This is the trace look that is similar to what libcurl makes on its
+   * own.
+   */
+  static const char * const s_infotype[] = {
+    "* ", "< ", "> ", "{ ", "} ", "{ ", "} "
+  };
+  if(idsbuf && *idsbuf)
+    fprintf(log, "%s%s", idsbuf, s_infotype[type]);
+  else
+    fputs(s_infotype[type], log);
+}
+
+#define TRC_IDS_FORMAT_IDS_1  "[%" CURL_FORMAT_CURL_OFF_T "-x] "
+#define TRC_IDS_FORMAT_IDS_2  "[%" CURL_FORMAT_CURL_OFF_T "-%" \
+                                   CURL_FORMAT_CURL_OFF_T "] "
+/*
+** callback for CURLOPT_DEBUGFUNCTION
+*/
+static int debug_cb(CURL *handle, curl_infotype type,
+                    char *data, size_t size,
+                    void *userdata)
+{
+  FILE *output = stderr;
+  static int newl = 0;
+  static int traced_data = 0;
+  char idsbuf[60];
+  curl_off_t xfer_id, conn_id;
+
+  (void)handle; /* not used */
+  (void)userdata;
+
+  if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
+    if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
+        conn_id >= 0) {
+      curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2,
+                     xfer_id, conn_id);
+    }
+    else {
+      curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
+    }
+  }
+  else
+    idsbuf[0] = 0;
+
+  switch(type) {
+  case CURLINFO_HEADER_OUT:
+    if(size > 0) {
+      size_t st = 0;
+      size_t i;
+      for(i = 0; i < size - 1; i++) {
+        if(data[i] == '\n') { /* LF */
+          if(!newl) {
+            log_line_start(output, idsbuf, type);
+          }
+          (void)fwrite(data + st, i - st + 1, 1, output);
+          st = i + 1;
+          newl = 0;
+        }
+      }
+      if(!newl)
+        log_line_start(output, idsbuf, type);
+      (void)fwrite(data + st, i - st + 1, 1, output);
+    }
+    newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
+    traced_data = 0;
+    break;
+  case CURLINFO_TEXT:
+  case CURLINFO_HEADER_IN:
+    if(!newl)
+      log_line_start(output, idsbuf, type);
+    (void)fwrite(data, size, 1, output);
+    newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
+    traced_data = 0;
+    break;
+  case CURLINFO_DATA_OUT:
+  case CURLINFO_DATA_IN:
+  case CURLINFO_SSL_DATA_IN:
+  case CURLINFO_SSL_DATA_OUT:
+    if(!traced_data) {
+      if(!newl)
+        log_line_start(output, idsbuf, type);
+      fprintf(output, "[%ld bytes data]\n", (long)size);
+      newl = 0;
+      traced_data = 1;
+    }
+    break;
+  default: /* nada */
+    newl = 0;
+    traced_data = 1;
+    break;
+  }
+
+  return 0;
+}
+
+static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
+{
+  (void)ptr;
+  (void)opaque;
+  return size * nmemb;
+}
+
+static void add_transfer(CURLM *multi, CURLSH *share,
+                         struct curl_slist *resolve, const char *url)
+{
+  CURL *easy;
+  CURLMcode mc;
+
+  easy = curl_easy_init();
+  if(!easy) {
+    fprintf(stderr, "curl_easy_init failed\n");
+    exit(1);
+  }
+  curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
+  curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
+  curl_easy_setopt(easy, CURLOPT_URL, url);
+  curl_easy_setopt(easy, CURLOPT_SHARE, share);
+  curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
+  curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
+  curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
+  curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
+  curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
+  curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
+  curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
+  if(resolve)
+    curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
+
+
+  mc = curl_multi_add_handle(multi, easy);
+  if(mc != CURLM_OK) {
+    fprintf(stderr, "curl_multi_add_handle: %s\n",
+           curl_multi_strerror(mc));
+    exit(1);
+  }
+}
+
+int main(int argc, char *argv[])
+{
+  const char *url;
+  CURLM *multi;
+  CURLMcode mc;
+  int running_handles = 0, numfds;
+  CURLMsg *msg;
+  CURLSH *share;
+  CURLU *cu;
+  struct curl_slist resolve;
+  char resolve_buf[1024];
+  int msgs_in_queue;
+  int add_more, waits, ongoing = 0;
+  char *host, *port;
+
+  if(argc != 2) {
+    fprintf(stderr, "%s URL\n", argv[0]);
+    exit(2);
+  }
+
+  url = argv[1];
+  cu = curl_url();
+  if(!cu) {
+    fprintf(stderr, "out of memory\n");
+    exit(1);
+  }
+  if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
+    fprintf(stderr, "not a URL: '%s'\n", url);
+    exit(1);
+  }
+  if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
+    fprintf(stderr, "could not get host of '%s'\n", url);
+    exit(1);
+  }
+  if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
+    fprintf(stderr, "could not get port of '%s'\n", url);
+    exit(1);
+  }
+
+   memset(&resolve, 0, sizeof(resolve));
+   curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1,
+                  "%s:%s:127.0.0.1", host, port);
+  curl_slist_append(&resolve, resolve_buf);
+
+  multi = curl_multi_init();
+  if(!multi) {
+    fprintf(stderr, "curl_multi_init failed\n");
+    exit(1);
+  }
+
+  share = curl_share_init();
+  if(!share) {
+    fprintf(stderr, "curl_share_init failed\n");
+    exit(1);
+  }
+  curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+
+
+  add_transfer(multi, share, &resolve, url);
+  ++ongoing;
+  add_more = 6;
+  waits = 3;
+  do {
+    mc = curl_multi_perform(multi, &running_handles);
+    if(mc != CURLM_OK) {
+      fprintf(stderr, "curl_multi_perform: %s\n",
+             curl_multi_strerror(mc));
+      exit(1);
+    }
+
+    if(running_handles) {
+      mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
+      if(mc != CURLM_OK) {
+        fprintf(stderr, "curl_multi_poll: %s\n",
+               curl_multi_strerror(mc));
+        exit(1);
+      }
+    }
+
+    if(waits) {
+      --waits;
+    }
+    else {
+      while(add_more) {
+        add_transfer(multi, share, &resolve, url);
+        ++ongoing;
+        --add_more;
+      }
+    }
+
+    /* Check for finished handles and remove. */
+    while((msg = curl_multi_info_read(multi, &msgs_in_queue))) {
+      if(msg->msg == CURLMSG_DONE) {
+        long status = 0;
+        curl_off_t xfer_id;
+        curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
+        curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
+        if(msg->data.result == CURLE_SEND_ERROR ||
+            msg->data.result == CURLE_RECV_ERROR) {
+          /* We get these if the server had a GOAWAY in transit on
+           * re-using a connection */
+        }
+        else if(msg->data.result) {
+          fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
+                  ": failed with %d\n", xfer_id, msg->data.result);
+          exit(1);
+        }
+        else if(status != 200) {
+          fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
+                  ": wrong http status %ld (expected 200)\n", xfer_id, status);
+          exit(1);
+        }
+        curl_multi_remove_handle(multi, msg->easy_handle);
+        curl_easy_cleanup(msg->easy_handle);
+        --ongoing;
+        fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
+                "(%d now running)\n", xfer_id, running_handles);
+      }
+    }
+
+    fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
+            running_handles, add_more);
+
+  } while(ongoing || add_more);
+
+  fprintf(stderr, "exiting\n");
+  exit(EXIT_SUCCESS);
+}
index c6ea590aac5807c8c04ebe58014111cfcca59ba6..42854f8bd82cd0a40ca24081389fcb2727612ddc 100644 (file)
@@ -163,6 +163,8 @@ class TestDownload:
     @pytest.mark.parametrize("proto", ['http/1.1'])
     def test_02_07b_download_reuse(self, env: Env,
                                    httpd, nghttpx, repeat, proto):
+        if env.curl_uses_lib('wolfssl'):
+            pytest.skip("wolfssl session reuse borked")
         count = 6
         curl = CurlClient(env=env)
         urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]'
@@ -366,6 +368,19 @@ class TestDownload:
         r = client.run(args=[url])
         assert r.exit_code == 0, f'{client.dump_logs()}'
 
+    # Special client that tests TLS session reuse in parallel transfers
+    def test_02_26_session_shared_reuse(self, env: Env, httpd, repeat):
+        curl = CurlClient(env=env)
+        url = f'https://{env.domain1}:{env.https_port}/data-100k'
+        client = LocalClient(name='tls-session-reuse', env=env)
+        if not client.exists():
+            pytest.skip(f'example client not built: {client.name}')
+        r = client.run(args=[url])
+        if env.curl_uses_lib('wolfssl'):
+            assert r.exit_code != 0, f'unexpected success for wolfSSL session share'
+        else:
+            r.check_exit_code(0)
+
     def check_downloads(self, client, srcfile: str, count: int,
                         complete: bool = True):
         for i in range(count):