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