]>
Commit | Line | Data |
---|---|---|
2246b732 | 1 | /* |
bf95c10a | 2 | * Copyright (C) 1996-2022 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
2246b732 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 74 HTTP Message */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
675b8408 | 12 | #include "debug/Stream.h" |
4f1c93a7 | 13 | #include "http/ContentLengthInterpreter.h" |
63df1d28 | 14 | #include "http/Message.h" |
af2980f3 | 15 | #include "http/one/Parser.h" |
0c90d3b1 | 16 | #include "HttpHdrCc.h" |
a5bac1d2 | 17 | #include "HttpHeaderTools.h" |
0eb49b6d | 18 | #include "MemBuf.h" |
b6149797 | 19 | #include "mime_header.h" |
4d5904f7 | 20 | #include "SquidConfig.h" |
8596962e | 21 | |
63df1d28 | 22 | Http::Message::Message(http_hdr_owner_type owner): |
f53969cc | 23 | http_ver(Http::ProtocolVersion()), |
63df1d28 | 24 | header(owner) |
8596962e | 25 | {} |
26 | ||
63df1d28 | 27 | Http::Message::~Message() |
4a56ee8d | 28 | { |
5f8252d2 | 29 | assert(!body_pipe); |
4a56ee8d | 30 | } |
31 | ||
0c90d3b1 | 32 | void |
63df1d28 | 33 | Http::Message::putCc(const HttpHdrCc *otherCc) |
0c90d3b1 DD |
34 | { |
35 | // get rid of the old CC, if any | |
36 | if (cache_control) { | |
37 | delete cache_control; | |
38 | cache_control = nullptr; | |
39 | if (!otherCc) | |
f5beb600 | 40 | header.delById(Http::HdrType::CACHE_CONTROL); |
0c90d3b1 DD |
41 | // else it will be deleted inside putCc() below |
42 | } | |
43 | ||
44 | // add new CC, if any | |
45 | if (otherCc) { | |
46 | cache_control = new HttpHdrCc(*otherCc); | |
47 | header.putCc(cache_control); | |
48 | } | |
49 | } | |
50 | ||
8596962e | 51 | /* find first CRLF */ |
52 | static int | |
53 | httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end) | |
54 | { | |
55 | int slen = strcspn(*parse_start, "\r\n"); | |
56 | ||
57 | if (!(*parse_start)[slen]) /* no CRLF found */ | |
58 | return 0; | |
59 | ||
60 | *blk_start = *parse_start; | |
61 | ||
62 | *blk_end = *blk_start + slen; | |
63 | ||
64 | while (**blk_end == '\r') /* CR */ | |
95dc7ff4 | 65 | ++(*blk_end); |
8596962e | 66 | |
67 | if (**blk_end == '\n') /* LF */ | |
95dc7ff4 | 68 | ++(*blk_end); |
8596962e | 69 | |
70 | *parse_start = *blk_end; | |
71 | ||
72 | return 1; | |
73 | } | |
74 | ||
955394ce | 75 | // negative return is the negated Http::StatusCode error code |
8596962e | 76 | // zero return means need more data |
77 | // positive return is the size of parsed headers | |
955394ce | 78 | bool |
63df1d28 | 79 | Http::Message::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error) |
8596962e | 80 | { |
81 | assert(error); | |
955394ce | 82 | *error = Http::scNone; |
8596962e | 83 | |
8596962e | 84 | // find the end of headers |
84ae6223 | 85 | const size_t hdr_len = headersEnd(buf, sz); |
8596962e | 86 | |
7d59dbc9 JR |
87 | if (hdr_len > Config.maxReplyHeaderSize || (hdr_len == 0 && sz > Config.maxReplyHeaderSize)) { |
88 | debugs(58, 3, "input too large: " << hdr_len << " or " << sz << " > " << Config.maxReplyHeaderSize); | |
89 | *error = Http::scHeaderTooLarge; | |
90 | return false; | |
91 | } | |
92 | ||
96ee497f AJ |
93 | // sanity check the start line to see if this is in fact an HTTP message |
94 | if (!sanityCheckStartLine(buf, hdr_len, error)) { | |
281832c6 AJ |
95 | // NP: sanityCheck sets *error and sends debug warnings on syntax errors. |
96 | // if we have seen the connection close, this is an error too | |
955394ce AJ |
97 | if (eof && *error == Http::scNone) |
98 | *error = Http::scInvalidHeader; | |
281832c6 | 99 | |
96ee497f AJ |
100 | return false; |
101 | } | |
102 | ||
7d59dbc9 | 103 | assert(hdr_len > 0); // sanityCheckStartLine() rejects buffers that cannot be parsed |
8596962e | 104 | |
84ae6223 | 105 | const int res = httpMsgParseStep(buf, sz, eof); |
8596962e | 106 | |
107 | if (res < 0) { // error | |
63df1d28 | 108 | debugs(58, 3, "cannot parse isolated headers in '" << buf << "'"); |
955394ce | 109 | *error = Http::scInvalidHeader; |
8596962e | 110 | return false; |
111 | } | |
112 | ||
113 | if (res == 0) { | |
63df1d28 | 114 | debugs(58, 2, "strange, need more data near '" << buf << "'"); |
955394ce | 115 | *error = Http::scInvalidHeader; |
8596962e | 116 | return false; // but this should not happen due to headersEnd() above |
117 | } | |
118 | ||
119 | assert(res > 0); | |
63df1d28 | 120 | debugs(58, 9, "success (" << hdr_len << " bytes) near '" << buf << "'"); |
8596962e | 121 | |
122 | if (hdr_sz != (int)hdr_len) { | |
d816f28d | 123 | debugs(58, DBG_IMPORTANT, "ERROR: internal Http::Message::parse vs. headersEnd failure: " << |
8596962e | 124 | hdr_sz << " != " << hdr_len); |
125 | hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len | |
126 | } | |
127 | ||
128 | return true; | |
129 | } | |
130 | ||
63df1d28 | 131 | /** |
bf9fb8ff | 132 | * parseCharBuf() takes character buffer of HTTP headers (buf), |
63df1d28 | 133 | * which may not be NULL-terminated, and fills in an Http::Message |
59eed7dc | 134 | * structure. The parameter 'end' specifies the offset to |
135 | * the end of the reply headers. The caller may know where the | |
136 | * end is, but is unable to NULL-terminate the buffer. This function | |
137 | * returns true on success. | |
138 | */ | |
139 | bool | |
63df1d28 | 140 | Http::Message::parseCharBuf(const char *buf, ssize_t end) |
59eed7dc | 141 | { |
142 | MemBuf mb; | |
143 | int success; | |
144 | /* reset current state, because we are not used in incremental fashion */ | |
145 | reset(); | |
146 | mb.init(); | |
147 | mb.append(buf, end); | |
148 | mb.terminate(); | |
666f514b | 149 | success = httpMsgParseStep(mb.buf, mb.size, 0); |
59eed7dc | 150 | mb.clean(); |
151 | return success == 1; | |
152 | } | |
8596962e | 153 | |
63df1d28 AJ |
154 | /** |
155 | * parses a 0-terminated buffer into Http::Message. | |
156 | * | |
157 | * \retval 1 success | |
158 | * \retval 0 need more data (partial parse) | |
159 | * \retval -1 parse error | |
8596962e | 160 | */ |
161 | int | |
63df1d28 | 162 | Http::Message::httpMsgParseStep(const char *buf, int len, int atEnd) |
8596962e | 163 | { |
164 | const char *parse_start = buf; | |
666f514b | 165 | int parse_len = len; |
8596962e | 166 | const char *blk_start, *blk_end; |
167 | const char **parse_end_ptr = &blk_end; | |
168 | assert(parse_start); | |
fb654382 | 169 | assert(pstate < Http::Message::psParsed); |
8596962e | 170 | |
171 | *parse_end_ptr = parse_start; | |
172 | ||
fb654382 | 173 | if (pstate == Http::Message::psReadyToParseStartLine) { |
9ea37c79 | 174 | if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) { |
137e94fd | 175 | return 0; |
26ac0430 | 176 | } |
8596962e | 177 | |
9ea37c79 | 178 | if (!parseFirstLine(blk_start, blk_end)) { |
137e94fd | 179 | return httpMsgParseError(); |
26ac0430 | 180 | } |
8596962e | 181 | |
182 | *parse_end_ptr = parse_start; | |
183 | ||
184 | hdr_sz = *parse_end_ptr - buf; | |
26ac0430 | 185 | parse_len = parse_len - hdr_sz; |
8596962e | 186 | |
fb654382 | 187 | pstate = Http::Message::psReadyToParseHeaders; |
8596962e | 188 | } |
189 | ||
666f514b | 190 | /* |
191 | * XXX This code uses parse_start; but if we're incrementally parsing then | |
192 | * this code might not actually be given parse_start at the right spot (just | |
193 | * after headers.) Grr. | |
194 | */ | |
fb654382 | 195 | if (pstate == Http::Message::psReadyToParseHeaders) { |
69c698a3 | 196 | size_t hsize = 0; |
4f1c93a7 EB |
197 | Http::ContentLengthInterpreter interpreter; |
198 | configureContentLengthInterpreter(interpreter); | |
199 | const int parsed = header.parse(parse_start, parse_len, atEnd, hsize, interpreter); | |
69c698a3 | 200 | if (parsed <= 0) { |
69c698a3 | 201 | return !parsed ? 0 : httpMsgParseError(); |
137e94fd | 202 | } |
69c698a3 | 203 | hdr_sz += hsize; |
07947ad8 | 204 | hdrCacheInit(); |
fb654382 | 205 | pstate = Http::Message::psParsed; |
8596962e | 206 | } |
137e94fd | 207 | |
137e94fd | 208 | return 1; |
8596962e | 209 | } |
210 | ||
af2980f3 | 211 | bool |
4f1c93a7 | 212 | Http::Message::parseHeader(Http1::Parser &hp, Http::ContentLengthInterpreter &clen) |
af2980f3 AJ |
213 | { |
214 | // HTTP/1 message contains "zero or more header fields" | |
215 | // zero does not need parsing | |
af2980f3 | 216 | // XXX: c_str() reallocates. performance regression. |
4f1c93a7 EB |
217 | configureContentLengthInterpreter(clen); |
218 | if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize(), clen)) { | |
fb654382 | 219 | pstate = Http::Message::psError; |
563afef6 | 220 | return false; |
af2980f3 AJ |
221 | } |
222 | ||
bd59d61c EB |
223 | // XXX: we are just parsing HTTP headers, not the whole message prefix here |
224 | hdr_sz = hp.messageHeaderSize(); | |
fb654382 | 225 | pstate = Http::Message::psParsed; |
563afef6 AR |
226 | hdrCacheInit(); |
227 | return true; | |
af2980f3 AJ |
228 | } |
229 | ||
8596962e | 230 | /* handy: resets and returns -1 */ |
231 | int | |
63df1d28 | 232 | Http::Message::httpMsgParseError() |
8596962e | 233 | { |
234 | reset(); | |
8596962e | 235 | return -1; |
236 | } | |
237 | ||
3ff65596 | 238 | void |
63df1d28 | 239 | Http::Message::setContentLength(int64_t clen) |
3ff65596 | 240 | { |
789217a2 FC |
241 | header.delById(Http::HdrType::CONTENT_LENGTH); // if any |
242 | header.putInt64(Http::HdrType::CONTENT_LENGTH, clen); | |
3ff65596 AR |
243 | content_length = clen; |
244 | } | |
245 | ||
4a1acc56 | 246 | bool |
63df1d28 | 247 | Http::Message::persistent() const |
2246b732 | 248 | { |
2592bc70 | 249 | if (http_ver > Http::ProtocolVersion(1,0)) { |
62e76326 | 250 | /* |
251 | * for modern versions of HTTP: persistent unless there is | |
252 | * a "Connection: close" header. | |
253 | */ | |
d5f18517 AJ |
254 | static SBuf close("close", 5); |
255 | return !httpHeaderHasConnDir(&header, close); | |
3872be7c | 256 | } else { |
3872be7c | 257 | /* for old versions of HTTP: persistent if has "keep-alive" */ |
d5f18517 AJ |
258 | static SBuf keepAlive("keep-alive", 10); |
259 | return httpHeaderHasConnDir(&header, keepAlive); | |
3872be7c | 260 | } |
2246b732 | 261 | } |
8596962e | 262 | |
63df1d28 AJ |
263 | void |
264 | Http::Message::packInto(Packable *p, bool full_uri) const | |
8596962e | 265 | { |
266 | packFirstLineInto(p, full_uri); | |
a9925b40 | 267 | header.packInto(p); |
785b508d | 268 | p->append("\r\n", 2); |
8596962e | 269 | } |
270 | ||
63df1d28 AJ |
271 | void |
272 | Http::Message::hdrCacheInit() | |
07947ad8 | 273 | { |
789217a2 | 274 | content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH); |
aee3523a | 275 | assert(nullptr == cache_control); |
a9925b40 | 276 | cache_control = header.getCc(); |
07947ad8 | 277 | } |
3cfc19b3 | 278 | |
63df1d28 AJ |
279 | /// useful for debugging |
280 | void | |
281 | Http::Message::firstLineBuf(MemBuf &mb) | |
3cfc19b3 | 282 | { |
10201568 | 283 | packFirstLineInto(&mb, true); |
3cfc19b3 | 284 | } |
4106be3f | 285 |