]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpMsg.cc
Bug 3533: Cache still valid after HTTP/1.1 303 See Other
[thirdparty/squid.git] / src / HttpMsg.cc
CommitLineData
2246b732 1/*
ef57eb7b 2 * Copyright (C) 1996-2016 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
FC
11#include "squid.h"
12#include "Debug.h"
af2980f3 13#include "http/one/Parser.h"
0c90d3b1 14#include "HttpHdrCc.h"
a5bac1d2 15#include "HttpHeaderTools.h"
8596962e 16#include "HttpMsg.h"
0eb49b6d 17#include "MemBuf.h"
b6149797 18#include "mime_header.h"
582c2af2 19#include "profiler/Profiler.h"
4d5904f7 20#include "SquidConfig.h"
8596962e 21
ce867f0a 22HttpMsg::HttpMsg(http_hdr_owner_type owner):
f53969cc
SM
23 http_ver(Http::ProtocolVersion()),
24 header(owner),
25 cache_control(NULL),
26 hdr_sz(0),
27 content_length(0),
88df846b
CT
28 pstate(psReadyToParseStartLine),
29 sources(0)
8596962e 30{}
31
4a56ee8d 32HttpMsg::~HttpMsg()
33{
5f8252d2 34 assert(!body_pipe);
4a56ee8d 35}
36
0c90d3b1
DD
37void
38HttpMsg::putCc(const HttpHdrCc *otherCc)
39{
40 // get rid of the old CC, if any
41 if (cache_control) {
42 delete cache_control;
43 cache_control = nullptr;
44 if (!otherCc)
f5beb600 45 header.delById(Http::HdrType::CACHE_CONTROL);
0c90d3b1
DD
46 // else it will be deleted inside putCc() below
47 }
48
49 // add new CC, if any
50 if (otherCc) {
51 cache_control = new HttpHdrCc(*otherCc);
52 header.putCc(cache_control);
53 }
54}
55
8596962e 56HttpMsgParseState &operator++ (HttpMsgParseState &aState)
57{
58 int tmp = (int)aState;
59 aState = (HttpMsgParseState)(++tmp);
60 return aState;
61}
62
2246b732 63/* find end of headers */
784619e6 64static int
666f514b 65httpMsgIsolateHeaders(const char **parse_start, int l, const char **blk_start, const char **blk_end)
2246b732 66{
bdb1a5d5 67 /*
68 * parse_start points to the first line of HTTP message *headers*,
69 * not including the request or status lines
70 */
bdb1a5d5 71 size_t end = headersEnd(*parse_start, l);
72 int nnl;
62e76326 73
2246b732 74 if (end) {
62e76326 75 *blk_start = *parse_start;
76 *blk_end = *parse_start + end - 1;
77 /*
78 * leave blk_end pointing to the first character after the
79 * first newline which terminates the headers
80 */
81 assert(**blk_end == '\n');
82
83 while (*(*blk_end - 1) == '\r')
5e263176 84 --(*blk_end);
62e76326 85
86 assert(*(*blk_end - 1) == '\n');
87
88 *parse_start += end;
89
90 return 1;
2246b732 91 }
62e76326 92
bdb1a5d5 93 /*
94 * If we didn't find the end of headers, and parse_start does
95 * NOT point to a CR or NL character, then return failure
96 */
97 if (**parse_start != '\r' && **parse_start != '\n')
f53969cc 98 return 0; /* failure */
62e76326 99
bdb1a5d5 100 /*
101 * If we didn't find the end of headers, and parse_start does point
102 * to an empty line, then we have empty headers. Skip all CR and
103 * NL characters up to the first NL. Leave parse_start pointing at
104 * the first character after the first NL.
105 */
106 *blk_start = *parse_start;
62e76326 107
bdb1a5d5 108 *blk_end = *blk_start;
62e76326 109
95dc7ff4 110 for (nnl = 0; nnl == 0; ++(*parse_start)) {
62e76326 111 if (**parse_start == '\r')
112 (void) 0;
113 else if (**parse_start == '\n')
95dc7ff4 114 ++nnl;
62e76326 115 else
116 break;
2246b732 117 }
62e76326 118
bdb1a5d5 119 return 1;
2246b732 120}
121
8596962e 122/* find first CRLF */
123static int
124httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
125{
126 int slen = strcspn(*parse_start, "\r\n");
127
128 if (!(*parse_start)[slen]) /* no CRLF found */
129 return 0;
130
131 *blk_start = *parse_start;
132
133 *blk_end = *blk_start + slen;
134
135 while (**blk_end == '\r') /* CR */
95dc7ff4 136 ++(*blk_end);
8596962e 137
138 if (**blk_end == '\n') /* LF */
95dc7ff4 139 ++(*blk_end);
8596962e 140
141 *parse_start = *blk_end;
142
143 return 1;
144}
145
955394ce 146// negative return is the negated Http::StatusCode error code
8596962e 147// zero return means need more data
148// positive return is the size of parsed headers
955394ce 149bool
84ae6223 150HttpMsg::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error)
8596962e 151{
152 assert(error);
955394ce 153 *error = Http::scNone;
8596962e 154
8596962e 155 // find the end of headers
84ae6223 156 const size_t hdr_len = headersEnd(buf, sz);
8596962e 157
96ee497f
AJ
158 // sanity check the start line to see if this is in fact an HTTP message
159 if (!sanityCheckStartLine(buf, hdr_len, error)) {
281832c6
AJ
160 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
161 // if we have seen the connection close, this is an error too
955394ce
AJ
162 if (eof && *error == Http::scNone)
163 *error = Http::scInvalidHeader;
281832c6 164
96ee497f
AJ
165 return false;
166 }
167
84ae6223 168 if (hdr_len > Config.maxReplyHeaderSize || (hdr_len <= 0 && sz > Config.maxReplyHeaderSize)) {
e0236918 169 debugs(58, DBG_IMPORTANT, "HttpMsg::parse: Too large reply header (" << hdr_len << " > " << Config.maxReplyHeaderSize);
955394ce 170 *error = Http::scHeaderTooLarge;
c81e4de5 171 return false;
172 }
173
8596962e 174 if (hdr_len <= 0) {
84ae6223 175 debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof << ") in '" << buf << "'");
8596962e 176
177 if (eof) // iff we have seen the end, this is an error
955394ce 178 *error = Http::scInvalidHeader;
8596962e 179
180 return false;
181 }
182
84ae6223 183 const int res = httpMsgParseStep(buf, sz, eof);
8596962e 184
185 if (res < 0) { // error
84ae6223 186 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf << "'");
955394ce 187 *error = Http::scInvalidHeader;
8596962e 188 return false;
189 }
190
191 if (res == 0) {
84ae6223 192 debugs(58, 2, "HttpMsg::parse: strange, need more data near '" << buf << "'");
955394ce 193 *error = Http::scInvalidHeader;
8596962e 194 return false; // but this should not happen due to headersEnd() above
195 }
196
197 assert(res > 0);
84ae6223 198 debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) near '" << buf << "'");
8596962e 199
200 if (hdr_sz != (int)hdr_len) {
e0236918 201 debugs(58, DBG_IMPORTANT, "internal HttpMsg::parse vs. headersEnd error: " <<
8596962e 202 hdr_sz << " != " << hdr_len);
203 hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len
204 }
205
206 return true;
207}
208
59eed7dc 209/*
bf9fb8ff 210 * parseCharBuf() takes character buffer of HTTP headers (buf),
59eed7dc 211 * which may not be NULL-terminated, and fills in an HttpMsg
212 * structure. The parameter 'end' specifies the offset to
213 * the end of the reply headers. The caller may know where the
214 * end is, but is unable to NULL-terminate the buffer. This function
215 * returns true on success.
216 */
217bool
218HttpMsg::parseCharBuf(const char *buf, ssize_t end)
219{
220 MemBuf mb;
221 int success;
222 /* reset current state, because we are not used in incremental fashion */
223 reset();
224 mb.init();
225 mb.append(buf, end);
226 mb.terminate();
666f514b 227 success = httpMsgParseStep(mb.buf, mb.size, 0);
59eed7dc 228 mb.clean();
229 return success == 1;
230}
8596962e 231
232/*
233 * parses a 0-terminating buffer into HttpMsg.
234 * Returns:
235 * 1 -- success
236 * 0 -- need more data (partial parse)
237 * -1 -- parse error
238 */
239int
666f514b 240HttpMsg::httpMsgParseStep(const char *buf, int len, int atEnd)
8596962e 241{
242 const char *parse_start = buf;
666f514b 243 int parse_len = len;
8596962e 244 const char *blk_start, *blk_end;
245 const char **parse_end_ptr = &blk_end;
246 assert(parse_start);
247 assert(pstate < psParsed);
8596962e 248
249 *parse_end_ptr = parse_start;
250
9ea37c79 251 PROF_start(HttpMsg_httpMsgParseStep);
252
8596962e 253 if (pstate == psReadyToParseStartLine) {
9ea37c79 254 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
137e94fd
AJ
255 PROF_stop(HttpMsg_httpMsgParseStep);
256 return 0;
26ac0430 257 }
8596962e 258
9ea37c79 259 if (!parseFirstLine(blk_start, blk_end)) {
137e94fd
AJ
260 PROF_stop(HttpMsg_httpMsgParseStep);
261 return httpMsgParseError();
26ac0430 262 }
8596962e 263
264 *parse_end_ptr = parse_start;
265
266 hdr_sz = *parse_end_ptr - buf;
26ac0430 267 parse_len = parse_len - hdr_sz;
8596962e 268
269 ++pstate;
270 }
271
666f514b 272 /*
273 * XXX This code uses parse_start; but if we're incrementally parsing then
274 * this code might not actually be given parse_start at the right spot (just
275 * after headers.) Grr.
276 */
8596962e 277 if (pstate == psReadyToParseHeaders) {
666f514b 278 if (!httpMsgIsolateHeaders(&parse_start, parse_len, &blk_start, &blk_end)) {
9ea37c79 279 if (atEnd) {
784619e6
AJ
280 blk_start = parse_start;
281 blk_end = blk_start + strlen(blk_start);
26ac0430 282 } else {
137e94fd
AJ
283 PROF_stop(HttpMsg_httpMsgParseStep);
284 return 0;
9ea37c79 285 }
8596962e 286 }
287
784619e6 288 if (!header.parse(blk_start, blk_end-blk_start)) {
137e94fd 289 PROF_stop(HttpMsg_httpMsgParseStep);
8596962e 290 return httpMsgParseError();
137e94fd 291 }
8596962e 292
07947ad8 293 hdrCacheInit();
8596962e 294
295 *parse_end_ptr = parse_start;
296
297 hdr_sz = *parse_end_ptr - buf;
298
299 ++pstate;
300 }
137e94fd 301
9ea37c79 302 PROF_stop(HttpMsg_httpMsgParseStep);
137e94fd 303 return 1;
8596962e 304}
305
af2980f3
AJ
306bool
307HttpMsg::parseHeader(Http1::Parser &hp)
308{
309 // HTTP/1 message contains "zero or more header fields"
310 // zero does not need parsing
af2980f3 311 // XXX: c_str() reallocates. performance regression.
563afef6
AR
312 if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize())) {
313 pstate = psError;
314 return false;
af2980f3
AJ
315 }
316
bd59d61c
EB
317 // XXX: we are just parsing HTTP headers, not the whole message prefix here
318 hdr_sz = hp.messageHeaderSize();
563afef6
AR
319 pstate = psParsed;
320 hdrCacheInit();
321 return true;
af2980f3
AJ
322}
323
8596962e 324/* handy: resets and returns -1 */
325int
326HttpMsg::httpMsgParseError()
327{
328 reset();
8596962e 329 return -1;
330}
331
3ff65596
AR
332void
333HttpMsg::setContentLength(int64_t clen)
334{
789217a2
FC
335 header.delById(Http::HdrType::CONTENT_LENGTH); // if any
336 header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
3ff65596
AR
337 content_length = clen;
338}
339
4a1acc56
AJ
340bool
341HttpMsg::persistent() const
2246b732 342{
2592bc70 343 if (http_ver > Http::ProtocolVersion(1,0)) {
62e76326 344 /*
345 * for modern versions of HTTP: persistent unless there is
346 * a "Connection: close" header.
347 */
ef84c0fb 348 return !httpHeaderHasConnDir(&header, "close");
3872be7c 349 } else {
3872be7c 350 /* for old versions of HTTP: persistent if has "keep-alive" */
ef84c0fb 351 return httpHeaderHasConnDir(&header, "keep-alive");
3872be7c 352 }
2246b732 353}
8596962e 354
17802cf1 355void HttpMsg::packInto(Packable *p, bool full_uri) const
8596962e 356{
357 packFirstLineInto(p, full_uri);
a9925b40 358 header.packInto(p);
785b508d 359 p->append("\r\n", 2);
8596962e 360}
361
07947ad8 362void HttpMsg::hdrCacheInit()
363{
789217a2 364 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
07947ad8 365 assert(NULL == cache_control);
a9925b40 366 cache_control = header.getCc();
07947ad8 367}
3cfc19b3 368
369/*
370 * useful for debugging
371 */
372void HttpMsg::firstLineBuf(MemBuf& mb)
373{
10201568 374 packFirstLineInto(&mb, true);
3cfc19b3 375}
f53969cc 376