1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
32 /* The last 3 #include files should be in this order */
33 #include "curl_printf.h"
34 #include "curl_memory.h"
38 * Curl_pseudo_headers() creates the array with pseudo headers to be
39 * used in a HTTP/2 or HTTP/3 request.
42 #if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
44 /* Index where :authority header field will appear in request header
46 #define AUTHORITY_DST_IDX 3
48 /* USHRT_MAX is 65535 == 0xffff */
49 #define HEADER_OVERFLOW(x) \
50 (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
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.
57 static bool contains_trailers(const char *p, size_t len)
59 const char *end = p + len;
61 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
63 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
65 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
66 p += sizeof("trailers") - 1;
67 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
69 if(p == end || *p == ',')
72 /* skip to next token */
73 for(; p != end && *p != ','; ++p)
82 /* Send header to server */
84 /* Don't send header to server */
86 /* Discard header, and replace it with "te: trailers" */
87 HEADERINST_TE_TRAILERS
90 /* Decides how to treat given header field. */
91 static header_instruction inspect_header(const char *name, size_t namelen,
92 const char *value, size_t valuelen) {
95 if(!strncasecompare("te", name, namelen))
96 return HEADERINST_FORWARD;
98 return contains_trailers(value, valuelen) ?
99 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
101 return strncasecompare("upgrade", name, namelen) ?
102 HEADERINST_IGNORE : HEADERINST_FORWARD;
104 return (strncasecompare("connection", name, namelen) ||
105 strncasecompare("keep-alive", name, namelen)) ?
106 HEADERINST_IGNORE : HEADERINST_FORWARD;
108 return strncasecompare("proxy-connection", name, namelen) ?
109 HEADERINST_IGNORE : HEADERINST_FORWARD;
111 return strncasecompare("transfer-encoding", name, namelen) ?
112 HEADERINST_IGNORE : HEADERINST_FORWARD;
114 return HEADERINST_FORWARD;
118 CURLcode Curl_pseudo_headers(struct Curl_easy *data,
119 const char *mem, /* the request */
120 const size_t len /* size of request */,
123 struct connectdata *conn = data->conn;
126 size_t authority_idx;
127 char *hdbuf = (char *)mem;
128 char *end, *line_end;
129 struct h2h3pseudo *nva = NULL;
130 struct h2h3req *hreq = NULL;
133 /* Calculate number of headers contained in [mem, mem + len). Assumes a
134 correctly generated HTTP header field block. */
135 for(i = 1; i < len; ++i) {
136 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
144 /* We counted additional 2 \r\n in the first and last line. We need 3
145 new headers: :method, :path and :scheme. Therefore we need one
148 hreq = malloc(sizeof(struct h2h3req) +
149 sizeof(struct h2h3pseudo) * (nheader - 1));
154 nva = &hreq->header[0];
156 /* Extract :method, :path from request line
157 We do line endings with CRLF so checking for CR is enough */
158 line_end = memchr(hdbuf, '\r', len);
163 /* Method does not contain spaces */
164 end = memchr(hdbuf, ' ', line_end - hdbuf);
165 if(!end || end == hdbuf)
167 nva[0].name = H2H3_PSEUDO_METHOD;
168 nva[0].namelen = sizeof(H2H3_PSEUDO_METHOD) - 1;
169 nva[0].value = hdbuf;
170 nva[0].valuelen = (size_t)(end - hdbuf);
174 /* Path may contain spaces so scan backwards */
176 for(i = (size_t)(line_end - hdbuf); i; --i) {
177 if(hdbuf[i - 1] == ' ') {
182 if(!end || end == hdbuf)
184 nva[1].name = H2H3_PSEUDO_PATH;
185 nva[1].namelen = sizeof(H2H3_PSEUDO_PATH) - 1;
186 nva[1].value = hdbuf;
187 nva[1].valuelen = (end - hdbuf);
189 nva[2].name = H2H3_PSEUDO_SCHEME;
190 nva[2].namelen = sizeof(H2H3_PSEUDO_SCHEME) - 1;
191 vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
193 vptr += sizeof(H2H3_PSEUDO_SCHEME);
194 while(*vptr && ISBLANK(*vptr))
197 infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
200 if(conn->handler->flags & PROTOPT_SSL)
201 nva[2].value = "https";
203 nva[2].value = "http";
205 nva[2].valuelen = strlen((char *)nva[2].value);
212 hdbuf = line_end + 2;
214 /* check for next CR, but only within the piece of data left in the given
216 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
217 if(!line_end || (line_end == hdbuf))
220 /* header continuation lines are not supported */
221 if(*hdbuf == ' ' || *hdbuf == '\t')
224 for(end = hdbuf; end < line_end && *end != ':'; ++end)
226 if(end == hdbuf || end == line_end)
230 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
232 nva[i].name = H2H3_PSEUDO_AUTHORITY;
233 nva[i].namelen = sizeof(H2H3_PSEUDO_AUTHORITY) - 1;
236 nva[i].namelen = (size_t)(end - hdbuf);
237 /* Lower case the header name for HTTP/3 */
238 Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
242 while(*hdbuf == ' ' || *hdbuf == '\t')
246 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
248 case HEADERINST_IGNORE:
249 /* skip header fields prohibited by HTTP/2 specification. */
252 case HEADERINST_TE_TRAILERS:
253 nva[i].value = "trailers";
254 nva[i].valuelen = sizeof("trailers") - 1;
257 nva[i].value = hdbuf;
258 nva[i].valuelen = (end - hdbuf);
264 /* :authority must come before non-pseudo header fields */
265 if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
266 struct h2h3pseudo authority = nva[authority_idx];
267 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
273 /* Warn stream may be rejected if cumulative length of headers is too
275 #define MAX_ACC 60000 /* <64KB to account for some overhead */
279 for(i = 0; i < nheader; ++i) {
280 acc += nva[i].namelen + nva[i].valuelen;
282 infof(data, "h2h3 [%.*s: %.*s]",
283 (int)nva[i].namelen, nva[i].name,
284 (int)nva[i].valuelen, nva[i].value);
288 infof(data, "http_request: Warning: The cumulative length of all "
289 "headers exceeds %d bytes and that could cause the "
290 "stream to be rejected.", MAX_ACC);
294 hreq->entries = nheader;
301 return CURLE_OUT_OF_MEMORY;
304 void Curl_pseudo_free(struct h2h3req *hp)
309 #endif /* USE_NGHTTP2 or HTTP/3 enabled */