xref: /freebsd/crypto/openssl/test/http_test.c (revision c4fde87a3236b500246d4fce76dbca2a3d64f73a)
1 /*
2  * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright Siemens AG 2020
4  *
5  * Licensed under the Apache License 2.0 (the "License").  You may not use
6  * this file except in compliance with the License.  You can obtain a copy
7  * in the file LICENSE in the source distribution or at
8  * https://www.openssl.org/source/license.html
9  */
10 
11 #include <openssl/http.h>
12 #include <openssl/httperr.h>
13 #include <openssl/pem.h>
14 #include <openssl/x509v3.h>
15 #include <openssl/err.h>
16 #include <string.h>
17 
18 #include "testutil.h"
19 
20 #define HTTP_STATUS_CODE_OK 200
21 #define HTTP_STATUS_CODES_FATAL_ERROR 399
22 #define HTTP_STATUS_CODES_NONFATAL_ERROR 400
23 
24 static const ASN1_ITEM *x509_it = NULL;
25 static X509 *x509 = NULL;
26 #define RPATH "/path"
27 
28 typedef struct {
29     BIO *out;
30     const char *content_type;
31     const char *txt;
32     char version;
33     int keep_alive;
34 } server_args;
35 
36 /*-
37  * Pretty trivial HTTP mock server:
38  * For POST, copy request headers+body from mem BIO |in| as response to |out|.
39  * For GET, redirect to RPATH unless already there, else use |content_type| and
40  * respond with |txt| if not NULL, else with |rsp| of ASN1 type |it|.
41  * Take the status code suggsted by the client via special prefix of the path.
42  * On fatal status, respond with empty content.
43  * Response hdr has HTTP version 1.|version| and |keep_alive| (unless implicit).
44  */
45 static int mock_http_server(BIO *in, BIO *out, char version, int keep_alive,
46     const char *content_type, const char *txt,
47     ASN1_VALUE *rsp, const ASN1_ITEM *it)
48 {
49     const char *req, *path;
50     long count = BIO_get_mem_data(in, (unsigned char **)&req);
51     const char *hdr = (char *)req, *suggested_status;
52     char status[4] = "200";
53     int len;
54     int is_get = count >= 4 && CHECK_AND_SKIP_PREFIX(hdr, "GET ");
55 
56     /* first line should contain "(GET|POST) (/<suggested status>)?/<path> HTTP/1.x" */
57     if (!is_get
58         && !(TEST_true(count >= 5 && CHECK_AND_SKIP_PREFIX(hdr, "POST "))))
59         return 0;
60 
61     /* get any status code string to be returned suggested by test client */
62     if (*hdr == '/') {
63         suggested_status = ++hdr;
64         while (*hdr >= '0' && *hdr <= '9')
65             hdr++;
66         if (hdr == suggested_status + sizeof(status) - 1)
67             strncpy(status, suggested_status, sizeof(status) - 1);
68         else
69             hdr = suggested_status - 1;
70     }
71 
72     path = hdr;
73     hdr = strchr(hdr, ' ');
74     if (hdr == NULL)
75         return 0;
76     len = strlen("HTTP/1.");
77     if (!TEST_strn_eq(++hdr, "HTTP/1.", len))
78         return 0;
79     hdr += len;
80     /* check for HTTP version 1.0 .. 1.1 */
81     if (!TEST_char_le('0', *hdr) || !TEST_char_le(*hdr++, '1'))
82         return 0;
83     if (!TEST_char_eq(*hdr++, '\r') || !TEST_char_eq(*hdr++, '\n'))
84         return 0;
85 
86     count -= (hdr - req);
87     if (count < 0 || out == NULL)
88         return 0;
89 
90     if (!HAS_PREFIX(path, RPATH)) {
91         if (!is_get)
92             return 0;
93         return BIO_printf(out, "HTTP/1.%c 301 Moved Permanently\r\n"
94                                "Location: %s\r\n\r\n",
95                    version, RPATH)
96             > 0; /* same server */
97     }
98     if (BIO_printf(out, "HTTP/1.%c %s %s\r\n", version, status,
99             /* mock some reason string: */
100             strcmp(status, "200") == 0 ? "OK" : strcmp(status, "400") >= 0 ? "error"
101                                                                            : "fatal")
102         <= 0)
103         return 0;
104     if ((version == '0') == keep_alive) /* otherwise, default */
105         if (BIO_printf(out, "Connection: %s\r\n",
106                 version == '0' ? "keep-alive" : "close")
107             <= 0)
108             return 0;
109 
110     if (strcmp(status, "399") == 0) /* HTTP_STATUS_CODES_FATAL_ERROR */
111         return BIO_puts(out, "\r\n") == 2; /* empty content */
112 
113     if (is_get) { /* construct new header and body */
114         if (txt != NULL)
115             len = strlen(txt);
116         else if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0)
117             return 0;
118         if (BIO_printf(out, "Content-Type: %s\r\n"
119                             "Content-Length: %d\r\n\r\n",
120                 content_type, len)
121             <= 0)
122             return 0;
123         if (txt != NULL)
124             return BIO_puts(out, txt);
125         return ASN1_item_i2d_bio(it, out, rsp);
126     } else { /* respond on POST request */
127         if (CHECK_AND_SKIP_PREFIX(hdr, "Connection: ")) {
128             /* skip req Connection header */
129             hdr = strstr(hdr, "\r\n");
130             if (hdr == NULL)
131                 return 0;
132             hdr += 2;
133         }
134         /* echo remaining request header and body */
135         return BIO_write(out, hdr, count) == count;
136     }
137 }
138 
139 static long http_bio_cb_ex(BIO *bio, int oper, const char *argp, size_t len,
140     int cmd, long argl, int ret, size_t *processed)
141 {
142     server_args *args = (server_args *)BIO_get_callback_arg(bio);
143 
144     if (oper == (BIO_CB_CTRL | BIO_CB_RETURN) && cmd == BIO_CTRL_FLUSH)
145         ret = mock_http_server(bio, args->out, args->version, args->keep_alive,
146             args->content_type, args->txt,
147             (ASN1_VALUE *)x509, x509_it);
148     return ret;
149 }
150 
151 #define text1 "test\n"
152 #define text2 "more\n"
153 #define REAL_SERVER_URL "http://httpbin.org/"
154 #define DOCTYPE_HTML "<!DOCTYPE html>\n"
155 
156 /* do_get > 1 used for testing redirection */
157 static int test_http_method(int do_get, int do_txt, int suggested_status)
158 {
159     BIO *wbio = BIO_new(BIO_s_mem());
160     BIO *rbio = BIO_new(BIO_s_mem());
161     server_args mock_args = { NULL, NULL, NULL, '0', 0 };
162     BIO *req, *rsp;
163     char path[80];
164     STACK_OF(CONF_VALUE) *headers = NULL;
165     const char *content_type;
166     int res = 0;
167     int real_server = do_txt && 0; /* remove "&& 0" for using real server */
168 
169     BIO_snprintf(path, sizeof(path), "/%d%s", suggested_status,
170         do_get > 1 ? "/will-be-redirected" : RPATH);
171     if (do_txt) {
172         content_type = "text/plain";
173         req = BIO_new(BIO_s_mem());
174         if (req == NULL
175             || BIO_puts(req, text1) != sizeof(text1) - 1
176             || BIO_puts(req, text2) != sizeof(text2) - 1) {
177             BIO_free(req);
178             req = NULL;
179         }
180         mock_args.txt = text1;
181     } else {
182         content_type = "application/x-x509-ca-cert";
183         req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509);
184         mock_args.txt = NULL;
185     }
186     if (wbio == NULL || rbio == NULL || req == NULL)
187         goto err;
188 
189     mock_args.out = rbio;
190     mock_args.content_type = content_type;
191     BIO_set_callback_ex(wbio, http_bio_cb_ex);
192     BIO_set_callback_arg(wbio, (char *)&mock_args);
193 
194     rsp = do_get ? OSSL_HTTP_get(real_server ? REAL_SERVER_URL : path,
195                        NULL /* proxy */, NULL /* no_proxy */,
196                        real_server ? NULL : wbio,
197                        real_server ? NULL : rbio,
198                        NULL /* bio_update_fn */, NULL /* arg */,
199                        0 /* buf_size */, headers,
200                        real_server ? "text/html; charset=utf-8" : content_type,
201                        !do_txt /* expect_asn1 */,
202                        OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */)
203                  : OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, path,
204                        0 /* use_ssl */, NULL /* proxy */, NULL /* no_pr */,
205                        wbio, rbio, NULL /* bio_fn */, NULL /* arg */,
206                        0 /* buf_size */, headers, content_type,
207                        req, content_type, !do_txt /* expect_asn1 */,
208                        OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */,
209                        0 /* keep_alive */);
210     if (!TEST_int_eq(suggested_status == HTTP_STATUS_CODES_FATAL_ERROR, rsp == NULL))
211         goto err;
212     if (suggested_status == HTTP_STATUS_CODES_FATAL_ERROR)
213         res = 1;
214     if (rsp != NULL) {
215         if (do_get && real_server) {
216             char rtext[sizeof(DOCTYPE_HTML)];
217 
218             res = TEST_int_eq(BIO_gets(rsp, rtext, sizeof(rtext)),
219                       sizeof(DOCTYPE_HTML) - 1)
220                 && TEST_str_eq(rtext, DOCTYPE_HTML);
221         } else if (do_txt) {
222             char rtext[sizeof(text1) + 1 /* more space than needed */];
223 
224             res = TEST_int_eq(BIO_gets(rsp, rtext, sizeof(rtext)),
225                       sizeof(text1) - 1)
226                 && TEST_str_eq(rtext, text1);
227         } else {
228             X509 *rcert = d2i_X509_bio(rsp, NULL);
229 
230             res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0);
231             X509_free(rcert);
232         }
233         BIO_free(rsp);
234     }
235 
236 err:
237     BIO_free(req);
238     BIO_free(wbio);
239     BIO_free(rbio);
240     sk_CONF_VALUE_pop_free(headers, X509V3_conf_free);
241     return res;
242 }
243 
244 static const struct {
245     const char *url;
246     const char *redirects[4];
247     int success;
248 } redirect_tests[] = {
249     { "https://server/start", { "http://server/end" }, 0 },
250     { "https://server/start", { "/relative", "http://server/end" }, 0 },
251     { "http://server/start",
252         { "https://server/secure", "/relative", "http://server/end" }, 0 },
253     { "http://server/start", { "/relative", "http://server/end" }, 1 },
254     { "https://server/start", { "/relative", "https://server/end" }, 1 },
255 };
256 
257 /* Replace each flushed request with the next response, using a single mem BIO. */
258 static long http_redirect_cb(BIO *bio, int oper, const char *argp, size_t len,
259     int cmd, long argl, int ret, size_t *processed)
260 {
261     const char *const *redirect = (const char *const *)BIO_get_callback_arg(bio);
262 
263     if (oper != (BIO_CB_CTRL | BIO_CB_RETURN))
264         return ret;
265     if (cmd == BIO_C_DO_STATE_MACHINE)
266         return 1; /* mock a successful connection */
267     if (cmd != BIO_CTRL_FLUSH)
268         return ret;
269     if (!TEST_int_eq(BIO_reset(bio), 1))
270         return 0;
271     if (*redirect != NULL) {
272         BIO_set_callback_arg(bio, (char *)(redirect + 1));
273         return BIO_printf(bio, "HTTP/1.0 302 Found\r\nLocation: %s\r\n\r\n",
274                    *redirect)
275             > 0;
276     }
277     return BIO_puts(bio, "HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\n" text1) > 0;
278 }
279 
280 /* The redirect policy uses the requested protocol; no actual TLS is needed. */
281 static BIO *http_noop_update(BIO *bio, void *arg, int connect, int detail)
282 {
283     return bio;
284 }
285 
286 static int test_http_redirect(int idx)
287 {
288     BIO *bio = BIO_new(BIO_s_mem());
289     BIO *rsp = NULL;
290     char buf[sizeof(text1)];
291     unsigned long err;
292     int res = 0;
293 
294     if (!TEST_ptr(bio))
295         goto end;
296     BIO_set_callback_ex(bio, http_redirect_cb);
297     BIO_set_callback_arg(bio, (char *)redirect_tests[idx].redirects);
298     ERR_clear_error();
299     rsp = OSSL_HTTP_get(redirect_tests[idx].url, NULL, NULL, bio, NULL,
300         http_noop_update, NULL, 0, NULL, NULL, 0,
301         OSSL_HTTP_DEFAULT_MAX_RESP_LEN, 0);
302     if (redirect_tests[idx].success) {
303         res = TEST_ptr(rsp)
304             && TEST_int_eq(BIO_read(rsp, buf, sizeof(buf)), sizeof(text1) - 1)
305             && TEST_mem_eq(buf, sizeof(text1) - 1, text1, sizeof(text1) - 1);
306     } else {
307         err = ERR_peek_last_error();
308         res = TEST_ptr_null(rsp)
309             && TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_HTTP)
310             && TEST_int_eq(ERR_GET_REASON(err), HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP);
311     }
312 
313 end:
314     BIO_free(rsp);
315     BIO_free(bio);
316     ERR_clear_error();
317     return res;
318 }
319 
320 static int test_http_keep_alive(char version, int keep_alive, int kept_alive)
321 {
322     BIO *wbio = BIO_new(BIO_s_mem());
323     BIO *rbio = BIO_new(BIO_s_mem());
324     BIO *rsp;
325     const char *const content_type = "application/x-x509-ca-cert";
326     server_args mock_args = { NULL, NULL, NULL, '0', 0 };
327     OSSL_HTTP_REQ_CTX *rctx = NULL;
328     int i, res = 0;
329 
330     if (wbio == NULL || rbio == NULL)
331         goto err;
332     mock_args.out = rbio;
333     mock_args.content_type = content_type;
334     mock_args.version = version;
335     mock_args.keep_alive = kept_alive;
336     BIO_set_callback_ex(wbio, http_bio_cb_ex);
337     BIO_set_callback_arg(wbio, (char *)&mock_args);
338 
339     for (res = 1, i = 1; res && i <= 2; i++) {
340         rsp = OSSL_HTTP_transfer(&rctx, NULL /* server */, NULL /* port */,
341             RPATH, 0 /* use_ssl */,
342             NULL /* proxy */, NULL /* no_proxy */,
343             wbio, rbio, NULL /* bio_update_fn */, NULL,
344             0 /* buf_size */, NULL /* headers */,
345             NULL /* content_type */, NULL /* req => GET */,
346             content_type, 0 /* ASN.1 not expected */,
347             0 /* max_resp_len */, 0 /* timeout */,
348             keep_alive);
349         if (keep_alive == 2 && kept_alive == 0)
350             res = res && TEST_ptr_null(rsp)
351                 && TEST_int_eq(OSSL_HTTP_is_alive(rctx), 0);
352         else
353             res = res && TEST_ptr(rsp)
354                 && TEST_int_eq(OSSL_HTTP_is_alive(rctx), keep_alive > 0);
355         BIO_free(rsp);
356         (void)BIO_reset(rbio); /* discard response contents */
357         keep_alive = 0;
358     }
359     OSSL_HTTP_close(rctx, res);
360 
361 err:
362     BIO_free(wbio);
363     BIO_free(rbio);
364     return res;
365 }
366 
367 static int test_http_url_frag_ok(const char *url, int exp_ssl, const char *exp_host,
368     const char *exp_port, const char *exp_path, const char *exp_frag)
369 {
370     char *user, *host, *port, *path, *query, *frag;
371     int exp_num, num, ssl;
372     int res;
373 
374     if (!TEST_int_eq(sscanf(exp_port, "%d", &exp_num), 1))
375         return 0;
376     res = TEST_true(OSSL_HTTP_parse_url(url, &ssl, &user, &host, &port, &num,
377               &path, &query, &frag))
378         && TEST_str_eq(host, exp_host)
379         && TEST_str_eq(port, exp_port)
380         && TEST_int_eq(num, exp_num)
381         && TEST_str_eq(path, exp_path)
382         && TEST_int_eq(ssl, exp_ssl);
383     if (res && *user != '\0')
384         res = TEST_str_eq(user, "user:pass");
385     if (res)
386         res = TEST_str_eq(frag, exp_frag);
387     if (res && *query != '\0')
388         res = TEST_str_eq(query, "q");
389     OPENSSL_free(user);
390     OPENSSL_free(host);
391     OPENSSL_free(port);
392     OPENSSL_free(path);
393     OPENSSL_free(query);
394     OPENSSL_free(frag);
395     return res;
396 }
397 
398 static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
399     const char *exp_port, const char *exp_path)
400 {
401     return test_http_url_frag_ok(url, exp_ssl, exp_host, exp_port, exp_path, "");
402 }
403 
404 static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu)
405 {
406     char *host, *path;
407     int res;
408 
409     res = TEST_true(OSSL_HTTP_parse_url(url, NULL, NULL, &host, NULL, NULL,
410               &path, NULL, NULL))
411         && TEST_str_eq(host, "host")
412         && TEST_str_eq(path, exp_path_qu);
413     OPENSSL_free(host);
414     OPENSSL_free(path);
415     return res;
416 }
417 
418 static int test_http_url_host_ok(const char *url, const char *exp_host)
419 {
420     char *host;
421     int res;
422 
423     res = TEST_true(OSSL_HTTP_parse_url(url, NULL, NULL, &host, NULL, NULL,
424               NULL, NULL, NULL))
425         && TEST_str_eq(host, exp_host);
426     OPENSSL_free(host);
427     return res;
428 }
429 
430 static int test_http_url_dns(void)
431 {
432     return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path");
433 }
434 
435 static int test_http_url_ip(void)
436 {
437     return test_http_url_ok("1.2.3.4:5678//blahblablah", 0, "1.2.3.4", "5678", "//blahblablah");
438 }
439 
440 static int test_http_url_timestamp(void)
441 {
442     return test_http_url_ok("host/p/2017-01-03T00:00:00", 0, "host", "80",
443                "/p/2017-01-03T00:00:00")
444         && test_http_url_ok("http://host/p/2017-01-03T00:00:00", 0, "host",
445             "80", "/p/2017-01-03T00:00:00")
446         && test_http_url_ok("https://host/p/2017-01-03T00:00:00", 1, "host",
447             "443", "/p/2017-01-03T00:00:00");
448 }
449 
450 static int test_http_url_path_query(void)
451 {
452     return test_http_url_path_query_ok("http://usr@host:1/p?q=x#frag", "/p?q=x")
453         && test_http_url_path_query_ok("http://host?query#frag", "/?query")
454         && test_http_url_path_query_ok("http://host:9999#frag", "/");
455 }
456 
457 static int test_http_url_userinfo_query_fragment(void)
458 {
459     return test_http_url_frag_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p", "fr")
460         && test_http_url_frag_ok("host.example.org/some/path#://not-a-scheme/not.a.host:404", 0,
461             "host.example.org", "80", "/some/path", "://not-a-scheme/not.a.host:404");
462 }
463 
464 static int test_http_url_at_sign_outside_authority(void)
465 {
466     return test_http_url_host_ok("http://host/p@attacker.test", "host")
467         && test_http_url_host_ok("http://host/p?q=@attacker.test", "host")
468         && test_http_url_host_ok("http://host/p?q#fr@attacker.test", "host");
469 }
470 
471 static int test_http_url_ipv4(void)
472 {
473     return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q");
474 }
475 
476 static int test_http_url_ipv6(void)
477 {
478     return test_http_url_ok("http://[FF01::101]:6", 0, "[FF01::101]", "6", "/");
479 }
480 
481 static int test_http_url_invalid(const char *url)
482 {
483     char *host = "1", *port = "1", *path = "1";
484     int num = 1, ssl = 1;
485     int res;
486 
487     res = TEST_false(OSSL_HTTP_parse_url(url, &ssl, NULL, &host, &port, &num,
488               &path, NULL, NULL))
489         && TEST_ptr_null(host)
490         && TEST_ptr_null(port)
491         && TEST_ptr_null(path);
492     if (!res) {
493         OPENSSL_free(host);
494         OPENSSL_free(port);
495         OPENSSL_free(path);
496     }
497     return res;
498 }
499 
500 static int test_http_url_invalid_prefix(void)
501 {
502     return test_http_url_invalid("htttps://1.2.3.4:65535/pkix");
503 }
504 
505 static int test_http_url_invalid_port(void)
506 {
507     return test_http_url_invalid("https://1.2.3.4:65536/pkix")
508         && test_http_url_invalid("https://1.2.3.4:");
509 }
510 
511 static int test_http_url_invalid_path(void)
512 {
513     return test_http_url_invalid("https://[FF01::101]pkix");
514 }
515 
516 static int test_http_crlf_rejected(void)
517 {
518     BIO *wbio = BIO_new(BIO_s_mem());
519     BIO *rbio = BIO_new(BIO_s_mem());
520     BIO *req = BIO_new(BIO_s_mem());
521     BIO *proxy_bio = BIO_new(BIO_s_mem());
522     OSSL_HTTP_REQ_CTX *rctx = NULL;
523     int res = 0;
524 
525     if (!TEST_ptr(wbio)
526         || !TEST_ptr(rbio)
527         || !TEST_ptr(req)
528         || !TEST_ptr(proxy_bio)
529         || !TEST_int_eq(BIO_puts(req, "x"), 1)
530         || !TEST_ptr(rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, 0)))
531         goto err;
532 
533     ERR_clear_error();
534     res = TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
535               NULL, NULL, "/path\r\nInjected: value"))
536         && TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
537             "server\r\nInjected: value", "80", RPATH))
538         && TEST_false(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
539             "server", "80\r\nInjected: value", RPATH))
540         && TEST_true(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
541             NULL, NULL, RPATH))
542         && TEST_false(OSSL_HTTP_REQ_CTX_add1_header(rctx,
543             "X-Test\r\nInjected", "value"))
544         && TEST_false(OSSL_HTTP_REQ_CTX_add1_header(rctx,
545             "X-Test", "value\r\nInjected: value"))
546         && TEST_false(OSSL_HTTP_set1_request(rctx, RPATH, NULL,
547             "text/plain\r\nInjected: value", req,
548             NULL, 0 /* expect_asn1 */, 0 /* max_resp_len */,
549             0 /* timeout */, 0 /* keep_alive */))
550         && TEST_false(OSSL_HTTP_proxy_connect(proxy_bio,
551             "server\r\nInjected: value", "443", NULL, NULL,
552             0 /* timeout */, NULL, NULL))
553         && TEST_false(OSSL_HTTP_proxy_connect(proxy_bio,
554             "server", "443\r\nInjected: value", NULL, NULL,
555             0 /* timeout */, NULL, NULL));
556 
557 err:
558     ERR_clear_error();
559     OSSL_HTTP_REQ_CTX_free(rctx);
560     BIO_free(wbio);
561     BIO_free(rbio);
562     BIO_free(req);
563     BIO_free(proxy_bio);
564     return res;
565 }
566 
567 static int test_http_get_txt(void)
568 {
569     return test_http_method(1 /* GET */, 1, HTTP_STATUS_CODE_OK);
570 }
571 
572 static int test_http_get_txt_redirected(void)
573 {
574     return test_http_method(2 /* GET with redirection */, 1, HTTP_STATUS_CODE_OK);
575 }
576 
577 static int test_http_get_txt_fatal_status(void)
578 {
579     return test_http_method(1 /* GET */, 1, HTTP_STATUS_CODES_FATAL_ERROR);
580 }
581 
582 static int test_http_get_txt_error_status(void)
583 {
584     return test_http_method(1 /* GET */, 1, HTTP_STATUS_CODES_NONFATAL_ERROR);
585 }
586 
587 static int test_http_post_txt(void)
588 {
589     return test_http_method(0 /* POST */, 1, HTTP_STATUS_CODE_OK);
590 }
591 
592 static int test_http_get_x509(void)
593 {
594     return test_http_method(1 /* GET */, 0, HTTP_STATUS_CODE_OK);
595 }
596 
597 static int test_http_get_x509_redirected(void)
598 {
599     return test_http_method(2 /* GET with redirection */, 0, HTTP_STATUS_CODE_OK);
600 }
601 
602 static int test_http_post_x509(void)
603 {
604     return test_http_method(0 /* POST */, 0, HTTP_STATUS_CODE_OK);
605 }
606 
607 static int test_http_post_x509_fatal_status(void)
608 {
609     return test_http_method(0 /* POST */, 0, HTTP_STATUS_CODES_FATAL_ERROR);
610 }
611 
612 static int test_http_post_x509_error_status(void)
613 {
614     return test_http_method(0 /* POST */, 0, HTTP_STATUS_CODES_NONFATAL_ERROR);
615 }
616 
617 static int test_http_keep_alive_0_no_no(void)
618 {
619     return test_http_keep_alive('0', 0, 0);
620 }
621 
622 static int test_http_keep_alive_1_no_no(void)
623 {
624     return test_http_keep_alive('1', 0, 0);
625 }
626 
627 static int test_http_keep_alive_0_prefer_yes(void)
628 {
629     return test_http_keep_alive('0', 1, 1);
630 }
631 
632 static int test_http_keep_alive_1_prefer_yes(void)
633 {
634     return test_http_keep_alive('1', 1, 1);
635 }
636 
637 static int test_http_keep_alive_0_require_yes(void)
638 {
639     return test_http_keep_alive('0', 2, 1);
640 }
641 
642 static int test_http_keep_alive_1_require_yes(void)
643 {
644     return test_http_keep_alive('1', 2, 1);
645 }
646 
647 static int test_http_keep_alive_0_require_no(void)
648 {
649     return test_http_keep_alive('0', 2, 0);
650 }
651 
652 static int test_http_keep_alive_1_require_no(void)
653 {
654     return test_http_keep_alive('1', 2, 0);
655 }
656 
657 static int test_http_resp_hdr_limit(size_t limit)
658 {
659     BIO *wbio = BIO_new(BIO_s_mem());
660     BIO *rbio = BIO_new(BIO_s_mem());
661     BIO *mem = NULL;
662     server_args mock_args = { NULL, NULL, NULL, '0', 0 };
663     int res = 0;
664     OSSL_HTTP_REQ_CTX *rctx = NULL;
665 
666     if (!TEST_ptr(wbio) || !TEST_ptr(rbio))
667         goto err;
668 
669     mock_args.txt = text1;
670     mock_args.content_type = "text/plain";
671     mock_args.version = '1';
672     mock_args.out = rbio;
673 
674     BIO_set_callback_ex(wbio, http_bio_cb_ex);
675     BIO_set_callback_arg(wbio, (char *)&mock_args);
676 
677     rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, 8192);
678     if (!TEST_ptr(rctx))
679         goto err;
680 
681     if (!TEST_true(OSSL_HTTP_REQ_CTX_set_request_line(rctx, 0 /* GET */,
682             NULL, NULL, RPATH)))
683         goto err;
684 
685     OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(rctx, limit);
686     mem = OSSL_HTTP_REQ_CTX_exchange(rctx);
687 
688     /*
689      * Note the server sends 4 http response headers, thus we expect to
690      * see failure here when we set header limit in http response to 1.
691      */
692     if (limit == 1)
693         res = TEST_ptr_null(mem);
694     else
695         res = TEST_ptr(mem);
696 
697 err:
698     BIO_free(wbio);
699     BIO_free(rbio);
700     OSSL_HTTP_REQ_CTX_free(rctx);
701 
702     return res;
703 }
704 
705 static int test_hdr_resp_hdr_limit_none(void)
706 {
707     return test_http_resp_hdr_limit(0);
708 }
709 
710 static int test_hdr_resp_hdr_limit_short(void)
711 {
712     return (test_http_resp_hdr_limit(1));
713 }
714 
715 static int test_hdr_resp_hdr_limit_256(void)
716 {
717     return test_http_resp_hdr_limit(256);
718 }
719 
720 static int test_http_adapt_proxy_empty_server(void)
721 {
722     const char *proxy = "http://proxy.local:8080";
723 
724     return TEST_str_eq(OSSL_HTTP_adapt_proxy(proxy, "abc", "", 0), proxy)
725         && TEST_str_eq(OSSL_HTTP_adapt_proxy(proxy, "abc", "[]", 0), proxy);
726 }
727 
728 void cleanup_tests(void)
729 {
730     X509_free(x509);
731 }
732 
733 OPT_TEST_DECLARE_USAGE("cert.pem\n")
734 
735 int setup_tests(void)
736 {
737     if (!test_skip_common_options())
738         return 0;
739 
740     x509_it = ASN1_ITEM_rptr(X509);
741     if (!TEST_ptr((x509 = load_cert_pem(test_get_argument(0), NULL))))
742         return 0;
743 
744     ADD_TEST(test_http_url_dns);
745     ADD_TEST(test_http_url_ip);
746     ADD_TEST(test_http_url_timestamp);
747     ADD_TEST(test_http_url_path_query);
748     ADD_TEST(test_http_url_userinfo_query_fragment);
749     ADD_TEST(test_http_url_at_sign_outside_authority);
750     ADD_TEST(test_http_url_ipv4);
751     ADD_TEST(test_http_url_ipv6);
752     ADD_TEST(test_http_url_invalid_prefix);
753     ADD_TEST(test_http_url_invalid_port);
754     ADD_TEST(test_http_url_invalid_path);
755     ADD_TEST(test_http_crlf_rejected);
756 
757     ADD_TEST(test_http_get_txt);
758     ADD_TEST(test_http_get_txt_redirected);
759     ADD_ALL_TESTS(test_http_redirect, OSSL_NELEM(redirect_tests));
760     ADD_TEST(test_http_get_txt_fatal_status);
761     ADD_TEST(test_http_get_txt_error_status);
762     ADD_TEST(test_http_post_txt);
763     ADD_TEST(test_http_get_x509);
764     ADD_TEST(test_http_get_x509_redirected);
765     ADD_TEST(test_http_post_x509);
766     ADD_TEST(test_http_post_x509_fatal_status);
767     ADD_TEST(test_http_post_x509_error_status);
768 
769     ADD_TEST(test_http_keep_alive_0_no_no);
770     ADD_TEST(test_http_keep_alive_1_no_no);
771     ADD_TEST(test_http_keep_alive_0_prefer_yes);
772     ADD_TEST(test_http_keep_alive_1_prefer_yes);
773     ADD_TEST(test_http_keep_alive_0_require_yes);
774     ADD_TEST(test_http_keep_alive_1_require_yes);
775     ADD_TEST(test_http_keep_alive_0_require_no);
776     ADD_TEST(test_http_keep_alive_1_require_no);
777 
778     ADD_TEST(test_hdr_resp_hdr_limit_none);
779     ADD_TEST(test_hdr_resp_hdr_limit_short);
780     ADD_TEST(test_hdr_resp_hdr_limit_256);
781     ADD_TEST(test_http_adapt_proxy_empty_server);
782     return 1;
783 }
784