1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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#include "curlcheck.h" 25 26#include "urldata.h" 27#include "http.h" 28#include "http1.h" 29#include "curl_trc.h" 30 31static CURLcode unit_setup(void) 32{ 33 return CURLE_OK; 34} 35 36static void unit_stop(void) 37{ 38} 39 40struct tcase { 41 const char **input; 42 const char *default_scheme; 43 const char *method; 44 const char *scheme; 45 const char *authority; 46 const char *path; 47 size_t header_count; 48 size_t input_remain; 49}; 50 51static void check_eq(const char *s, const char *exp_s, const char *name) 52{ 53 if(s && exp_s) { 54 if(strcmp(s, exp_s)) { 55 fprintf(stderr, "expected %s: '%s' but got '%s'\n", name, exp_s, s); 56 fail("unexpected req component"); 57 } 58 } 59 else if(!s && exp_s) { 60 fprintf(stderr, "expected %s: '%s' but got NULL\n", name, exp_s); 61 fail("unexpected req component"); 62 } 63 else if(s && !exp_s) { 64 fprintf(stderr, "expected %s: NULL but got '%s'\n", name, s); 65 fail("unexpected req component"); 66 } 67} 68 69static void parse_success(struct tcase *t) 70{ 71 struct h1_req_parser p; 72 const char *buf; 73 size_t buflen, i, in_len, in_consumed; 74 CURLcode err; 75 ssize_t nread; 76 77 Curl_h1_req_parse_init(&p, 1024); 78 in_len = in_consumed = 0; 79 for(i = 0; t->input[i]; ++i) { 80 buf = t->input[i]; 81 buflen = strlen(buf); 82 in_len += buflen; 83 nread = Curl_h1_req_parse_read(&p, buf, buflen, t->default_scheme, 84 0, &err); 85 if(nread < 0) { 86 fprintf(stderr, "got err %d parsing: '%s'\n", err, buf); 87 fail("error consuming"); 88 } 89 in_consumed += (size_t)nread; 90 if((size_t)nread != buflen) { 91 if(!p.done) { 92 fprintf(stderr, "only %zd/%zu consumed for: '%s'\n", 93 nread, buflen, buf); 94 fail("not all consumed"); 95 } 96 } 97 } 98 99 fail_if(!p.done, "end not detected"); 100 fail_if(!p.req, "not request created"); 101 if(t->input_remain != (in_len - in_consumed)) { 102 fprintf(stderr, "expected %zu input bytes to remain, but got %zu\n", 103 t->input_remain, in_len - in_consumed); 104 fail("unexpected input consumption"); 105 } 106 if(p.req) { 107 check_eq(p.req->method, t->method, "method"); 108 check_eq(p.req->scheme, t->scheme, "scheme"); 109 check_eq(p.req->authority, t->authority, "authority"); 110 check_eq(p.req->path, t->path, "path"); 111 if(Curl_dynhds_count(&p.req->headers) != t->header_count) { 112 fprintf(stderr, "expected %zu headers but got %zu\n", t->header_count, 113 Curl_dynhds_count(&p.req->headers)); 114 fail("unexpected req header count"); 115 } 116 } 117 118 Curl_h1_req_parse_free(&p); 119} 120 121static const char *T1_INPUT[] = { 122 "GET /path HTTP/1.1\r\nHost: test.curl.se\r\n\r\n", 123 NULL, 124}; 125static struct tcase TEST1a = { 126 T1_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 0 127}; 128static struct tcase TEST1b = { 129 T1_INPUT, "https", "GET", "https", NULL, "/path", 1, 0 130}; 131 132static const char *T2_INPUT[] = { 133 "GET /path HTT", 134 "P/1.1\r\nHost: te", 135 "st.curl.se\r\n\r", 136 "\n12345678", 137 NULL, 138}; 139static struct tcase TEST2 = { 140 T2_INPUT, NULL, "GET", NULL, NULL, "/path", 1, 8 141}; 142 143static const char *T3_INPUT[] = { 144 "GET ftp://ftp.curl.se/xxx?a=2 HTTP/1.1\r\nContent-Length: 0\r", 145 "\nUser-Agent: xxx\r\n\r\n", 146 NULL, 147}; 148static struct tcase TEST3a = { 149 T3_INPUT, NULL, "GET", "ftp", "ftp.curl.se", "/xxx?a=2", 2, 0 150}; 151 152static const char *T4_INPUT[] = { 153 "CONNECT ftp.curl.se:123 HTTP/1.1\r\nContent-Length: 0\r\n", 154 "User-Agent: xxx\r\n", 155 "nothing: \r\n\r\n\n\n", 156 NULL, 157}; 158static struct tcase TEST4a = { 159 T4_INPUT, NULL, "CONNECT", NULL, "ftp.curl.se:123", NULL, 3, 2 160}; 161 162static const char *T5_INPUT[] = { 163 "OPTIONS * HTTP/1.1\r\nContent-Length: 0\r\nBlabla: xxx.yyy\r", 164 "\n\tzzzzzz\r\n\r\n", 165 "123", 166 NULL, 167}; 168static struct tcase TEST5a = { 169 T5_INPUT, NULL, "OPTIONS", NULL, NULL, "*", 2, 3 170}; 171 172static const char *T6_INPUT[] = { 173 "PUT /path HTTP/1.1\nHost: test.curl.se\n\n123", 174 NULL, 175}; 176static struct tcase TEST6a = { 177 T6_INPUT, NULL, "PUT", NULL, NULL, "/path", 1, 3 178}; 179 180UNITTEST_START 181 182 parse_success(&TEST1a); 183 parse_success(&TEST1b); 184 parse_success(&TEST2); 185 parse_success(&TEST3a); 186 parse_success(&TEST4a); 187 parse_success(&TEST5a); 188 parse_success(&TEST6a); 189 190UNITTEST_STOP 191