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