]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpMsg.cc
Bug #1479: wrong timezone declaration for 64 bit Irix
[thirdparty/squid.git] / src / HttpMsg.cc
CommitLineData
2246b732 1
2/*
3cfc19b3 3 * $Id: HttpMsg.cc,v 1.23 2006/01/09 20:38:44 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
7d5c6423 171 if (!sanityCheckStartLine(buf, error)) {
172 debugs(58,1, HERE << "first line of HTTP message is invalid");
173 *error = HTTP_INVALID_HEADER;
8596962e 174 return false;
7d5c6423 175 }
8596962e 176
177 const int res = httpMsgParseStep(buf->content(), eof);
178
179 if (res < 0) { // error
180 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers " <<
181 "in '" << buf->content() << "'");
182 *error = HTTP_INVALID_HEADER;
183 return false;
184 }
185
186 if (res == 0) {
187 debugs(58, 2, "HttpMsg::parse: strange, need more data near '" <<
188 buf->content() << "'");
189 return false; // but this should not happen due to headersEnd() above
190 }
191
192 assert(res > 0);
193 debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) " <<
194 "near '" << buf->content() << "'");
195
196 if (hdr_sz != (int)hdr_len) {
197 debugs(58, 1, "internal HttpMsg::parse vs. headersEnd error: " <<
198 hdr_sz << " != " << hdr_len);
199 hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len
200 }
201
202 return true;
203}
204
59eed7dc 205/*
bf9fb8ff 206 * parseCharBuf() takes character buffer of HTTP headers (buf),
59eed7dc 207 * which may not be NULL-terminated, and fills in an HttpMsg
208 * structure. The parameter 'end' specifies the offset to
209 * the end of the reply headers. The caller may know where the
210 * end is, but is unable to NULL-terminate the buffer. This function
211 * returns true on success.
212 */
213bool
214HttpMsg::parseCharBuf(const char *buf, ssize_t end)
215{
216 MemBuf mb;
217 int success;
218 /* reset current state, because we are not used in incremental fashion */
219 reset();
220 mb.init();
221 mb.append(buf, end);
222 mb.terminate();
223 success = httpMsgParseStep(mb.buf, 0);
224 mb.clean();
225 return success == 1;
226}
8596962e 227
228/*
229 * parses a 0-terminating buffer into HttpMsg.
230 * Returns:
231 * 1 -- success
232 * 0 -- need more data (partial parse)
233 * -1 -- parse error
234 */
235int
236HttpMsg::httpMsgParseStep(const char *buf, int atEnd)
237{
238 const char *parse_start = buf;
239 const char *blk_start, *blk_end;
240 const char **parse_end_ptr = &blk_end;
241 assert(parse_start);
242 assert(pstate < psParsed);
8596962e 243
244 *parse_end_ptr = parse_start;
245
246 if (pstate == psReadyToParseStartLine) {
247 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end))
248 return 0;
249
429f7150 250 if (!parseFirstLine(blk_start, blk_end))
251 return httpMsgParseError();
8596962e 252
253 *parse_end_ptr = parse_start;
254
255 hdr_sz = *parse_end_ptr - buf;
256
257 ++pstate;
258 }
259
260 if (pstate == psReadyToParseHeaders) {
261 if (!httpMsgIsolateHeaders(&parse_start, &blk_start, &blk_end)) {
262 if (atEnd)
263 blk_start = parse_start, blk_end = blk_start + strlen(blk_start);
264 else
265 return 0;
266 }
267
268 if (!httpHeaderParse(&header, blk_start, blk_end))
269 return httpMsgParseError();
270
07947ad8 271 hdrCacheInit();
8596962e 272
273 *parse_end_ptr = parse_start;
274
275 hdr_sz = *parse_end_ptr - buf;
276
277 ++pstate;
278 }
279
280 return 1;
281}
282
8596962e 283/* handy: resets and returns -1 */
284int
285HttpMsg::httpMsgParseError()
286{
287 reset();
288 /* indicate an error */
289
290 if (HttpReply *rep = dynamic_cast<HttpReply*>(this))
291 rep->sline.status = HTTP_INVALID_HEADER;
292
293 return -1;
294}
295
62e76326 296/* returns true if connection should be "persistent"
2246b732 297 * after processing this message */
298int
450e0c10 299httpMsgIsPersistent(HttpVersion const &http_ver, const HttpHeader * hdr)
2246b732 300{
6f3e5833 301#if WHEN_SQUID_IS_HTTP1_1
21b92762 302
bffee5af 303 if ((http_ver.major >= 1) && (http_ver.minor >= 1)) {
62e76326 304 /*
305 * for modern versions of HTTP: persistent unless there is
306 * a "Connection: close" header.
307 */
308 return !httpHeaderHasConnDir(hdr, "close");
8596962e 309 } else
21b92762 310#else
311 {
312#endif
62e76326 313 /*
314 * Persistent connections in Netscape 3.x are allegedly broken,
315 * return false if it is a browser connection. If there is a
316 * VIA header, then we assume this is NOT a browser connection.
317 */
318 const char *agent = httpHeaderGetStr(hdr, HDR_USER_AGENT);
319
8596962e 320 if (agent && !httpHeaderHas(hdr, HDR_VIA)) {
321 if (!strncasecmp(agent, "Mozilla/3.", 10))
322 return 0;
62e76326 323
8596962e 324 if (!strncasecmp(agent, "Netscape/3.", 11))
325 return 0;
2246b732 326 }
8596962e 327
328 /* for old versions of HTTP: persistent if has "keep-alive" */
329 return httpHeaderHasConnDir(hdr, "keep-alive");
330}
2246b732 331}
8596962e 332
333void HttpMsg::packInto(Packer *p, bool full_uri) const
334{
335 packFirstLineInto(p, full_uri);
336 httpHeaderPackInto(&header, p);
337 packerAppend(p, "\r\n", 2);
338}
339
07947ad8 340void HttpMsg::hdrCacheInit()
341{
342 content_length = httpHeaderGetInt(&header, HDR_CONTENT_LENGTH);
343 assert(NULL == cache_control);
344 cache_control = httpHeaderGetCc(&header);
345}
3cfc19b3 346
347/*
348 * useful for debugging
349 */
350void HttpMsg::firstLineBuf(MemBuf& mb)
351{
352 Packer p;
353 packerToMemInit(&p, &mb);
354 packFirstLineInto(&p, true);
355 packerClean(&p);
356}