VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/h2h3.c@ 99371

Last change on this file since 99371 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
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
25#include "curl_setup.h"
26#include "urldata.h"
27#include "h2h3.h"
28#include "transfer.h"
29#include "sendf.h"
30#include "strcase.h"
31
32/* The last 3 #include files should be in this order */
33#include "curl_printf.h"
34#include "curl_memory.h"
35#include "memdebug.h"
36
37/*
38 * Curl_pseudo_headers() creates the array with pseudo headers to be
39 * used in an HTTP/2 or HTTP/3 request.
40 */
41
42#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
43
44/* Index where :authority header field will appear in request header
45 field list. */
46#define AUTHORITY_DST_IDX 3
47
48/* USHRT_MAX is 65535 == 0xffff */
49#define HEADER_OVERFLOW(x) \
50 (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
51
52/*
53 * Check header memory for the token "trailers".
54 * Parse the tokens as separated by comma and surrounded by whitespace.
55 * Returns TRUE if found or FALSE if not.
56 */
57static bool contains_trailers(const char *p, size_t len)
58{
59 const char *end = p + len;
60 for(;;) {
61 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
62 ;
63 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
64 return FALSE;
65 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
66 p += sizeof("trailers") - 1;
67 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
68 ;
69 if(p == end || *p == ',')
70 return TRUE;
71 }
72 /* skip to next token */
73 for(; p != end && *p != ','; ++p)
74 ;
75 if(p == end)
76 return FALSE;
77 ++p;
78 }
79}
80
81typedef enum {
82 /* Send header to server */
83 HEADERINST_FORWARD,
84 /* Don't send header to server */
85 HEADERINST_IGNORE,
86 /* Discard header, and replace it with "te: trailers" */
87 HEADERINST_TE_TRAILERS
88} header_instruction;
89
90/* Decides how to treat given header field. */
91static header_instruction inspect_header(const char *name, size_t namelen,
92 const char *value, size_t valuelen) {
93 switch(namelen) {
94 case 2:
95 if(!strncasecompare("te", name, namelen))
96 return HEADERINST_FORWARD;
97
98 return contains_trailers(value, valuelen) ?
99 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
100 case 7:
101 return strncasecompare("upgrade", name, namelen) ?
102 HEADERINST_IGNORE : HEADERINST_FORWARD;
103 case 10:
104 return (strncasecompare("connection", name, namelen) ||
105 strncasecompare("keep-alive", name, namelen)) ?
106 HEADERINST_IGNORE : HEADERINST_FORWARD;
107 case 16:
108 return strncasecompare("proxy-connection", name, namelen) ?
109 HEADERINST_IGNORE : HEADERINST_FORWARD;
110 case 17:
111 return strncasecompare("transfer-encoding", name, namelen) ?
112 HEADERINST_IGNORE : HEADERINST_FORWARD;
113 default:
114 return HEADERINST_FORWARD;
115 }
116}
117
118CURLcode Curl_pseudo_headers(struct Curl_easy *data,
119 const char *mem, /* the request */
120 const size_t len /* size of request */,
121 size_t* hdrlen /* opt size of headers read */,
122 struct h2h3req **hp)
123{
124 struct connectdata *conn = data->conn;
125 size_t nheader = 0;
126 size_t i;
127 size_t authority_idx;
128 char *hdbuf = (char *)mem;
129 char *end, *line_end;
130 struct h2h3pseudo *nva = NULL;
131 struct h2h3req *hreq = NULL;
132 char *vptr;
133
134 /* Calculate number of headers contained in [mem, mem + len). Assumes a
135 correctly generated HTTP header field block. */
136 for(i = 1; i < len; ++i) {
137 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
138 ++nheader;
139 ++i;
140 }
141 }
142 if(nheader < 2) {
143 goto fail;
144 }
145 /* We counted additional 2 \r\n in the first and last line. We need 3
146 new headers: :method, :path and :scheme. Therefore we need one
147 more space. */
148 nheader += 1;
149 hreq = malloc(sizeof(struct h2h3req) +
150 sizeof(struct h2h3pseudo) * (nheader - 1));
151 if(!hreq) {
152 goto fail;
153 }
154
155 nva = &hreq->header[0];
156
157 /* Extract :method, :path from request line
158 We do line endings with CRLF so checking for CR is enough */
159 line_end = memchr(hdbuf, '\r', len);
160 if(!line_end) {
161 goto fail;
162 }
163
164 /* Method does not contain spaces */
165 end = memchr(hdbuf, ' ', line_end - hdbuf);
166 if(!end || end == hdbuf)
167 goto fail;
168 nva[0].name = H2H3_PSEUDO_METHOD;
169 nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1;
170 nva[0].value = hdbuf;
171 nva[0].valuelen = (size_t)(end - hdbuf);
172
173 hdbuf = end + 1;
174
175 /* Path may contain spaces so scan backwards */
176 end = NULL;
177 for(i = (size_t)(line_end - hdbuf); i; --i) {
178 if(hdbuf[i - 1] == ' ') {
179 end = &hdbuf[i - 1];
180 break;
181 }
182 }
183 if(!end || end == hdbuf)
184 goto fail;
185 nva[1].name = H2H3_PSEUDO_PATH;
186 nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1;
187 nva[1].value = hdbuf;
188 nva[1].valuelen = (end - hdbuf);
189
190 nva[2].name = H2H3_PSEUDO_SCHEME;
191 nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1;
192 vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
193 if(vptr) {
194 vptr += sizeof(H2H3_PSEUDO_SCHEME);
195 while(*vptr && ISBLANK(*vptr))
196 vptr++;
197 nva[2].value = vptr;
198 infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
199 }
200 else {
201 if(conn->handler->flags & PROTOPT_SSL)
202 nva[2].value = "https";
203 else
204 nva[2].value = "http";
205 }
206 nva[2].valuelen = strlen((char *)nva[2].value);
207
208 authority_idx = 0;
209 i = 3;
210 while(i < nheader) {
211 size_t hlen;
212
213 hdbuf = line_end + 2;
214
215 /* check for next CR, but only within the piece of data left in the given
216 buffer */
217 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
218 if(!line_end || (line_end == hdbuf))
219 goto fail;
220
221 /* header continuation lines are not supported */
222 if(*hdbuf == ' ' || *hdbuf == '\t')
223 goto fail;
224
225 for(end = hdbuf; end < line_end && *end != ':'; ++end)
226 ;
227 if(end == hdbuf || end == line_end)
228 goto fail;
229 hlen = end - hdbuf;
230
231 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
232 authority_idx = i;
233 nva[i].name = H2H3_PSEUDO_AUTHORITY;
234 nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1;
235 }
236 else {
237 nva[i].namelen = (size_t)(end - hdbuf);
238 /* Lower case the header name for HTTP/3 */
239 Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
240 nva[i].name = hdbuf;
241 }
242 hdbuf = end + 1;
243 while(*hdbuf == ' ' || *hdbuf == '\t')
244 ++hdbuf;
245 end = line_end;
246
247 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
248 end - hdbuf)) {
249 case HEADERINST_IGNORE:
250 /* skip header fields prohibited by HTTP/2 specification. */
251 --nheader;
252 continue;
253 case HEADERINST_TE_TRAILERS:
254 nva[i].value = "trailers";
255 nva[i].valuelen = sizeof("trailers") - 1;
256 break;
257 default:
258 nva[i].value = hdbuf;
259 nva[i].valuelen = (end - hdbuf);
260 }
261
262 ++i;
263 }
264
265 /* :authority must come before non-pseudo header fields */
266 if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
267 struct h2h3pseudo authority = nva[authority_idx];
268 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
269 nva[i] = nva[i - 1];
270 }
271 nva[i] = authority;
272 }
273
274 /* Warn stream may be rejected if cumulative length of headers is too
275 large. */
276#define MAX_ACC 60000 /* <64KB to account for some overhead */
277 {
278 size_t acc = 0;
279
280 for(i = 0; i < nheader; ++i) {
281 acc += nva[i].namelen + nva[i].valuelen;
282
283 infof(data, "h2h3 [%.*s: %.*s]",
284 (int)nva[i].namelen, nva[i].name,
285 (int)nva[i].valuelen, nva[i].value);
286 }
287
288 if(acc > MAX_ACC) {
289 infof(data, "http_request: Warning: The cumulative length of all "
290 "headers exceeds %d bytes and that could cause the "
291 "stream to be rejected.", MAX_ACC);
292 }
293 }
294
295 if(hdrlen) {
296 /* Skip trailing CRLF */
297 end += 4;
298 *hdrlen = end - mem;
299 }
300
301 hreq->entries = nheader;
302 *hp = hreq;
303
304 return CURLE_OK;
305
306 fail:
307 free(hreq);
308 return CURLE_OUT_OF_MEMORY;
309}
310
311void Curl_pseudo_free(struct h2h3req *hp)
312{
313 free(hp);
314}
315
316#endif /* USE_NGHTTP2 or HTTP/3 enabled */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette