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