]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpMsg.cc
Bootstrapped
[thirdparty/squid.git] / src / HttpMsg.cc
CommitLineData
2246b732 1
2/*
6f3e5833 3 * $Id: HttpMsg.cc,v 1.20 2005/11/21 22:46:38 wessels Exp $
2246b732 4 *
5 * DEBUG: section 74 HTTP Message
6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
2246b732 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
2246b732 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
2246b732 34 */
35
36#include "squid.h"
8596962e 37#include "HttpMsg.h"
38#include "HttpRequest.h"
39#include "HttpReply.h"
0eb49b6d 40#include "MemBuf.h"
8596962e 41
42HttpMsg::HttpMsg(http_hdr_owner_type owner): header(owner),
43 cache_control(NULL), hdr_sz(0), content_length(0), protocol(PROTO_NONE),
44 pstate(psReadyToParseStartLine)
45{}
46
8596962e 47HttpMsgParseState &operator++ (HttpMsgParseState &aState)
48{
49 int tmp = (int)aState;
50 aState = (HttpMsgParseState)(++tmp);
51 return aState;
52}
53
2246b732 54/* find end of headers */
55int
56httpMsgIsolateHeaders(const char **parse_start, const char **blk_start, const char **blk_end)
57{
bdb1a5d5 58 /*
59 * parse_start points to the first line of HTTP message *headers*,
60 * not including the request or status lines
61 */
62 size_t l = strlen(*parse_start);
63 size_t end = headersEnd(*parse_start, l);
64 int nnl;
62e76326 65
2246b732 66 if (end) {
62e76326 67 *blk_start = *parse_start;
68 *blk_end = *parse_start + end - 1;
69 /*
70 * leave blk_end pointing to the first character after the
71 * first newline which terminates the headers
72 */
73 assert(**blk_end == '\n');
74
75 while (*(*blk_end - 1) == '\r')
76 (*blk_end)--;
77
78 assert(*(*blk_end - 1) == '\n');
79
80 *parse_start += end;
81
82 return 1;
2246b732 83 }
62e76326 84
bdb1a5d5 85 /*
86 * If we didn't find the end of headers, and parse_start does
87 * NOT point to a CR or NL character, then return failure
88 */
89 if (**parse_start != '\r' && **parse_start != '\n')
62e76326 90 return 0; /* failure */
91
bdb1a5d5 92 /*
93 * If we didn't find the end of headers, and parse_start does point
94 * to an empty line, then we have empty headers. Skip all CR and
95 * NL characters up to the first NL. Leave parse_start pointing at
96 * the first character after the first NL.
97 */
98 *blk_start = *parse_start;
62e76326 99
bdb1a5d5 100 *blk_end = *blk_start;
62e76326 101
a4295415 102 for (nnl = 0; nnl == 0; (*parse_start)++) {
62e76326 103 if (**parse_start == '\r')
104 (void) 0;
105 else if (**parse_start == '\n')
106 nnl++;
107 else
108 break;
2246b732 109 }
62e76326 110
bdb1a5d5 111 return 1;
2246b732 112}
113
8596962e 114/* find first CRLF */
115static int
116httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
117{
118 int slen = strcspn(*parse_start, "\r\n");
119
120 if (!(*parse_start)[slen]) /* no CRLF found */
121 return 0;
122
123 *blk_start = *parse_start;
124
125 *blk_end = *blk_start + slen;
126
127 while (**blk_end == '\r') /* CR */
128 (*blk_end)++;
129
130 if (**blk_end == '\n') /* LF */
131 (*blk_end)++;
132
133 *parse_start = *blk_end;
134
135 return 1;
136}
137
138// negative return is the negated HTTP_ error code
139// zero return means need more data
140// positive return is the size of parsed headers
141bool HttpMsg::parse(MemBuf *buf, bool eof, http_status *error)
142{
143 assert(error);
144 *error = HTTP_STATUS_NONE;
145
146 // httpMsgParseStep() and debugging require 0-termination, unfortunately
147 buf->terminate(); // does not affect content size
148
149 // find the end of headers
150 // TODO: Remove? httpReplyParseStep() should do similar checks
151 const size_t hdr_len = headersEnd(buf->content(), buf->contentSize());
152
153 if (hdr_len <= 0) {
154 debugs(58, 3, "HttpMsg::parse: failed to find end of headers " <<
155 "(eof: " << eof << ") in '" << buf->content() << "'");
156
157 if (eof) // iff we have seen the end, this is an error
158 *error = HTTP_INVALID_HEADER;
159
160 return false;
161 }
162
163 // TODO: move to httpReplyParseStep()
164 if (hdr_len > Config.maxReplyHeaderSize) {
165 debugs(58, 1, "HttpMsg::parse: Too large reply header (" <<
166 hdr_len << " > " << Config.maxReplyHeaderSize);
167 *error = HTTP_HEADER_TOO_LARGE;
168 return false;
169 }
170
171 if (!sanityCheckStartLine(buf, error)) // redundant; could be remvoed
172 return false;
173
174 const int res = httpMsgParseStep(buf->content(), eof);
175
176 if (res < 0) { // error
177 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers " <<
178 "in '" << buf->content() << "'");
179 *error = HTTP_INVALID_HEADER;
180 return false;
181 }
182
183 if (res == 0) {
184 debugs(58, 2, "HttpMsg::parse: strange, need more data near '" <<
185 buf->content() << "'");
186 return false; // but this should not happen due to headersEnd() above
187 }
188
189 assert(res > 0);
190 debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) " <<
191 "near '" << buf->content() << "'");
192
193 if (hdr_sz != (int)hdr_len) {
194 debugs(58, 1, "internal HttpMsg::parse vs. headersEnd error: " <<
195 hdr_sz << " != " << hdr_len);
196 hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len
197 }
198
199 return true;
200}
201
59eed7dc 202/*
203 * parse() takes character buffer of HTTP headers (buf),
204 * which may not be NULL-terminated, and fills in an HttpMsg
205 * structure. The parameter 'end' specifies the offset to
206 * the end of the reply headers. The caller may know where the
207 * end is, but is unable to NULL-terminate the buffer. This function
208 * returns true on success.
209 */
210bool
211HttpMsg::parseCharBuf(const char *buf, ssize_t end)
212{
213 MemBuf mb;
214 int success;
215 /* reset current state, because we are not used in incremental fashion */
216 reset();
217 mb.init();
218 mb.append(buf, end);
219 mb.terminate();
220 success = httpMsgParseStep(mb.buf, 0);
221 mb.clean();
222 return success == 1;
223}
8596962e 224
225/*
226 * parses a 0-terminating buffer into HttpMsg.
227 * Returns:
228 * 1 -- success
229 * 0 -- need more data (partial parse)
230 * -1 -- parse error
231 */
232int
233HttpMsg::httpMsgParseStep(const char *buf, int atEnd)
234{
235 const char *parse_start = buf;
236 const char *blk_start, *blk_end;
237 const char **parse_end_ptr = &blk_end;
238 assert(parse_start);
239 assert(pstate < psParsed);
8596962e 240
241 *parse_end_ptr = parse_start;
242
243 if (pstate == psReadyToParseStartLine) {
244 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end))
245 return 0;
246
429f7150 247 if (!parseFirstLine(blk_start, blk_end))
248 return httpMsgParseError();
8596962e 249
250 *parse_end_ptr = parse_start;
251
252 hdr_sz = *parse_end_ptr - buf;
253
254 ++pstate;
255 }
256
257 if (pstate == psReadyToParseHeaders) {
258 if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) {
259 if (atEnd)
260 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
261 else
262 return 0;
263 }
264
265 if (!httpHeaderParse(&header, blk_start, blk_end))
266 return httpMsgParseError();
267
07947ad8 268 hdrCacheInit();
8596962e 269
270 *parse_end_ptr = parse_start;
271
272 hdr_sz = *parse_end_ptr - buf;
273
274 ++pstate;
275 }
276
277 return 1;
278}
279
8596962e 280/* handy: resets and returns -1 */
281int
282HttpMsg::httpMsgParseError()
283{
284 reset();
285 /* indicate an error */
286
287 if (HttpReply *rep = dynamic_cast<HttpReply*>(this))
288 rep->sline.status = HTTP_INVALID_HEADER;
289
290 return -1;
291}
292
62e76326 293/* returns true if connection should be "persistent"
2246b732 294 * after processing this message */
295int
450e0c10 296httpMsgIsPersistent(HttpVersion const &http_ver, const HttpHeader * hdr)
2246b732 297{
6f3e5833 298#if WHEN_SQUID_IS_HTTP1_1
21b92762 299
bffee5af 300 if ((http_ver.major >= 1) && (http_ver.minor >= 1)) {
62e76326 301 /*
302 * for modern versions of HTTP: persistent unless there is
303 * a "Connection: close" header.
304 */
305 return !httpHeaderHasConnDir(hdr, "close");
8596962e 306 } else
21b92762 307#else
308 {
309#endif
62e76326 310 /*
311 * Persistent connections in Netscape 3.x are allegedly broken,
312 * return false if it is a browser connection. If there is a
313 * VIA header, then we assume this is NOT a browser connection.
314 */
315 const char *agent = httpHeaderGetStr(hdr, HDR_USER_AGENT);
316
8596962e 317 if (agent && !httpHeaderHas(hdr, HDR_VIA)) {
318 if (!strncasecmp(agent, "Mozilla/3.", 10))
319 return 0;
62e76326 320
8596962e 321 if (!strncasecmp(agent, "Netscape/3.", 11))
322 return 0;
2246b732 323 }
8596962e 324
325 /* for old versions of HTTP: persistent if has "keep-alive" */
326 return httpHeaderHasConnDir(hdr, "keep-alive");
327}
2246b732 328}
8596962e 329
330void HttpMsg::packInto(Packer *p, bool full_uri) const
331{
332 packFirstLineInto(p, full_uri);
333 httpHeaderPackInto(&header, p);
334 packerAppend(p, "\r\n", 2);
335}
336
07947ad8 337void HttpMsg::hdrCacheInit()
338{
339 content_length = httpHeaderGetInt(&header, HDR_CONTENT_LENGTH);
340 assert(NULL == cache_control);
341 cache_control = httpHeaderGetCc(&header);
342}