Skip to content

Commit ccb7537

Browse files
icingptitSeb
authored andcommitted
tests: TLS session sharing test
- test TLS session sharing with special test client - expect failure with wolfSSL - disable flaky wolfSSL test_02_07b Closes curl#11675
1 parent e5ffa84 commit ccb7537

File tree

4 files changed

+325
-2
lines changed

4 files changed

+325
-2
lines changed

tests/http/clients/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ h2-serverpush
66
h2-download
77
ws-data
88
ws-pingpong
9-
h2-upgrade-extreme
9+
h2-upgrade-extreme
10+
tls-session-reuse

tests/http/clients/Makefile.inc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ check_PROGRAMS = \
2828
h2-download \
2929
ws-data \
3030
ws-pingpong \
31-
h2-upgrade-extreme
31+
h2-upgrade-extreme \
32+
tls-session-reuse
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
/***************************************************************************
2+
* _ _ ____ _
3+
* Project ___| | | | _ \| |
4+
* / __| | | | |_) | |
5+
* | (__| |_| | _ <| |___
6+
* \___|\___/|_| \_\_____|
7+
*
8+
* Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9+
*
10+
* This software is licensed as described in the file COPYING, which
11+
* you should have received as part of this distribution. The terms
12+
* are also available at https://curl.se/docs/copyright.html.
13+
*
14+
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
15+
* copies of the Software, and permit persons to whom the Software is
16+
* furnished to do so, under the terms of the COPYING file.
17+
*
18+
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19+
* KIND, either express or implied.
20+
*
21+
* SPDX-License-Identifier: curl
22+
*
23+
***************************************************************************/
24+
/* <DESC>
25+
* TLS session reuse
26+
* </DESC>
27+
*/
28+
#include <stdio.h>
29+
#include <stdlib.h>
30+
#include <stdint.h>
31+
#include <string.h>
32+
#include <inttypes.h>
33+
/* #include <error.h> */
34+
#include <errno.h>
35+
#include <curl/curl.h>
36+
#include <curl/mprintf.h>
37+
38+
39+
static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
40+
{
41+
/*
42+
* This is the trace look that is similar to what libcurl makes on its
43+
* own.
44+
*/
45+
static const char * const s_infotype[] = {
46+
"* ", "< ", "> ", "{ ", "} ", "{ ", "} "
47+
};
48+
if(idsbuf && *idsbuf)
49+
fprintf(log, "%s%s", idsbuf, s_infotype[type]);
50+
else
51+
fputs(s_infotype[type], log);
52+
}
53+
54+
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
55+
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
56+
CURL_FORMAT_CURL_OFF_T "] "
57+
/*
58+
** callback for CURLOPT_DEBUGFUNCTION
59+
*/
60+
static int debug_cb(CURL *handle, curl_infotype type,
61+
char *data, size_t size,
62+
void *userdata)
63+
{
64+
FILE *output = stderr;
65+
static int newl = 0;
66+
static int traced_data = 0;
67+
char idsbuf[60];
68+
curl_off_t xfer_id, conn_id;
69+
70+
(void)handle; /* not used */
71+
(void)userdata;
72+
73+
if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
74+
if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
75+
conn_id >= 0) {
76+
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2,
77+
xfer_id, conn_id);
78+
}
79+
else {
80+
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
81+
}
82+
}
83+
else
84+
idsbuf[0] = 0;
85+
86+
switch(type) {
87+
case CURLINFO_HEADER_OUT:
88+
if(size > 0) {
89+
size_t st = 0;
90+
size_t i;
91+
for(i = 0; i < size - 1; i++) {
92+
if(data[i] == '\n') { /* LF */
93+
if(!newl) {
94+
log_line_start(output, idsbuf, type);
95+
}
96+
(void)fwrite(data + st, i - st + 1, 1, output);
97+
st = i + 1;
98+
newl = 0;
99+
}
100+
}
101+
if(!newl)
102+
log_line_start(output, idsbuf, type);
103+
(void)fwrite(data + st, i - st + 1, 1, output);
104+
}
105+
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
106+
traced_data = 0;
107+
break;
108+
case CURLINFO_TEXT:
109+
case CURLINFO_HEADER_IN:
110+
if(!newl)
111+
log_line_start(output, idsbuf, type);
112+
(void)fwrite(data, size, 1, output);
113+
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
114+
traced_data = 0;
115+
break;
116+
case CURLINFO_DATA_OUT:
117+
case CURLINFO_DATA_IN:
118+
case CURLINFO_SSL_DATA_IN:
119+
case CURLINFO_SSL_DATA_OUT:
120+
if(!traced_data) {
121+
if(!newl)
122+
log_line_start(output, idsbuf, type);
123+
fprintf(output, "[%ld bytes data]\n", (long)size);
124+
newl = 0;
125+
traced_data = 1;
126+
}
127+
break;
128+
default: /* nada */
129+
newl = 0;
130+
traced_data = 1;
131+
break;
132+
}
133+
134+
return 0;
135+
}
136+
137+
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
138+
{
139+
(void)ptr;
140+
(void)opaque;
141+
return size * nmemb;
142+
}
143+
144+
static void add_transfer(CURLM *multi, CURLSH *share,
145+
struct curl_slist *resolve, const char *url)
146+
{
147+
CURL *easy;
148+
CURLMcode mc;
149+
150+
easy = curl_easy_init();
151+
if(!easy) {
152+
fprintf(stderr, "curl_easy_init failed\n");
153+
exit(1);
154+
}
155+
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
156+
curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
157+
curl_easy_setopt(easy, CURLOPT_URL, url);
158+
curl_easy_setopt(easy, CURLOPT_SHARE, share);
159+
curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
160+
curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
161+
curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
162+
curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
163+
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
164+
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
165+
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
166+
curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
167+
if(resolve)
168+
curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
169+
170+
171+
mc = curl_multi_add_handle(multi, easy);
172+
if(mc != CURLM_OK) {
173+
fprintf(stderr, "curl_multi_add_handle: %s\n",
174+
curl_multi_strerror(mc));
175+
exit(1);
176+
}
177+
}
178+
179+
int main(int argc, char *argv[])
180+
{
181+
const char *url;
182+
CURLM *multi;
183+
CURLMcode mc;
184+
int running_handles = 0, numfds;
185+
CURLMsg *msg;
186+
CURLSH *share;
187+
CURLU *cu;
188+
struct curl_slist resolve;
189+
char resolve_buf[1024];
190+
int msgs_in_queue;
191+
int add_more, waits, ongoing = 0;
192+
char *host, *port;
193+
194+
if(argc != 2) {
195+
fprintf(stderr, "%s URL\n", argv[0]);
196+
exit(2);
197+
}
198+
199+
url = argv[1];
200+
cu = curl_url();
201+
if(!cu) {
202+
fprintf(stderr, "out of memory\n");
203+
exit(1);
204+
}
205+
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
206+
fprintf(stderr, "not a URL: '%s'\n", url);
207+
exit(1);
208+
}
209+
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
210+
fprintf(stderr, "could not get host of '%s'\n", url);
211+
exit(1);
212+
}
213+
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
214+
fprintf(stderr, "could not get port of '%s'\n", url);
215+
exit(1);
216+
}
217+
218+
memset(&resolve, 0, sizeof(resolve));
219+
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1,
220+
"%s:%s:127.0.0.1", host, port);
221+
curl_slist_append(&resolve, resolve_buf);
222+
223+
multi = curl_multi_init();
224+
if(!multi) {
225+
fprintf(stderr, "curl_multi_init failed\n");
226+
exit(1);
227+
}
228+
229+
share = curl_share_init();
230+
if(!share) {
231+
fprintf(stderr, "curl_share_init failed\n");
232+
exit(1);
233+
}
234+
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
235+
236+
237+
add_transfer(multi, share, &resolve, url);
238+
++ongoing;
239+
add_more = 6;
240+
waits = 3;
241+
do {
242+
mc = curl_multi_perform(multi, &running_handles);
243+
if(mc != CURLM_OK) {
244+
fprintf(stderr, "curl_multi_perform: %s\n",
245+
curl_multi_strerror(mc));
246+
exit(1);
247+
}
248+
249+
if(running_handles) {
250+
mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
251+
if(mc != CURLM_OK) {
252+
fprintf(stderr, "curl_multi_poll: %s\n",
253+
curl_multi_strerror(mc));
254+
exit(1);
255+
}
256+
}
257+
258+
if(waits) {
259+
--waits;
260+
}
261+
else {
262+
while(add_more) {
263+
add_transfer(multi, share, &resolve, url);
264+
++ongoing;
265+
--add_more;
266+
}
267+
}
268+
269+
/* Check for finished handles and remove. */
270+
while((msg = curl_multi_info_read(multi, &msgs_in_queue))) {
271+
if(msg->msg == CURLMSG_DONE) {
272+
long status = 0;
273+
curl_off_t xfer_id;
274+
curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
275+
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
276+
if(msg->data.result == CURLE_SEND_ERROR ||
277+
msg->data.result == CURLE_RECV_ERROR) {
278+
/* We get these if the server had a GOAWAY in transit on
279+
* re-using a connection */
280+
}
281+
else if(msg->data.result) {
282+
fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
283+
": failed with %d\n", xfer_id, msg->data.result);
284+
exit(1);
285+
}
286+
else if(status != 200) {
287+
fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
288+
": wrong http status %ld (expected 200)\n", xfer_id, status);
289+
exit(1);
290+
}
291+
curl_multi_remove_handle(multi, msg->easy_handle);
292+
curl_easy_cleanup(msg->easy_handle);
293+
--ongoing;
294+
fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
295+
"(%d now running)\n", xfer_id, running_handles);
296+
}
297+
}
298+
299+
fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
300+
running_handles, add_more);
301+
302+
} while(ongoing || add_more);
303+
304+
fprintf(stderr, "exiting\n");
305+
exit(EXIT_SUCCESS);
306+
}

tests/http/test_02_download.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ def test_02_07_download_reuse(self, env: Env,
163163
@pytest.mark.parametrize("proto", ['http/1.1'])
164164
def test_02_07b_download_reuse(self, env: Env,
165165
httpd, nghttpx, repeat, proto):
166+
if env.curl_uses_lib('wolfssl'):
167+
pytest.skip("wolfssl session reuse borked")
166168
count = 6
167169
curl = CurlClient(env=env)
168170
urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]'
@@ -366,6 +368,19 @@ def test_02_25_h2_upgrade_x(self, env: Env, httpd, repeat):
366368
r = client.run(args=[url])
367369
assert r.exit_code == 0, f'{client.dump_logs()}'
368370

371+
# Special client that tests TLS session reuse in parallel transfers
372+
def test_02_26_session_shared_reuse(self, env: Env, httpd, repeat):
373+
curl = CurlClient(env=env)
374+
url = f'https://{env.domain1}:{env.https_port}/data-100k'
375+
client = LocalClient(name='tls-session-reuse', env=env)
376+
if not client.exists():
377+
pytest.skip(f'example client not built: {client.name}')
378+
r = client.run(args=[url])
379+
if env.curl_uses_lib('wolfssl'):
380+
assert r.exit_code != 0, f'unexpected success for wolfSSL session share'
381+
else:
382+
r.check_exit_code(0)
383+
369384
def check_downloads(self, client, srcfile: str, count: int,
370385
complete: bool = True):
371386
for i in range(count):

0 commit comments

Comments
 (0)