3 * DEBUG: section 74 HTTP Message
4 * AUTHOR: Alex Rousskov
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
36 #include "HttpHeaderTools.h"
39 #include "mime_header.h"
40 #include "profiler/Profiler.h"
42 HttpMsg::HttpMsg(http_hdr_owner_type owner
): header(owner
),
43 cache_control(NULL
), hdr_sz(0), content_length(0), protocol(AnyP::PROTO_NONE
),
44 pstate(psReadyToParseStartLine
), lock_count(0)
49 assert(lock_count
== 0);
53 HttpMsgParseState
&operator++ (HttpMsgParseState
&aState
)
55 int tmp
= (int)aState
;
56 aState
= (HttpMsgParseState
)(++tmp
);
60 /* find end of headers */
62 httpMsgIsolateHeaders(const char **parse_start
, int l
, const char **blk_start
, const char **blk_end
)
65 * parse_start points to the first line of HTTP message *headers*,
66 * not including the request or status lines
68 size_t end
= headersEnd(*parse_start
, l
);
72 *blk_start
= *parse_start
;
73 *blk_end
= *parse_start
+ end
- 1;
75 * leave blk_end pointing to the first character after the
76 * first newline which terminates the headers
78 assert(**blk_end
== '\n');
80 while (*(*blk_end
- 1) == '\r')
83 assert(*(*blk_end
- 1) == '\n');
91 * If we didn't find the end of headers, and parse_start does
92 * NOT point to a CR or NL character, then return failure
94 if (**parse_start
!= '\r' && **parse_start
!= '\n')
95 return 0; /* failure */
98 * If we didn't find the end of headers, and parse_start does point
99 * to an empty line, then we have empty headers. Skip all CR and
100 * NL characters up to the first NL. Leave parse_start pointing at
101 * the first character after the first NL.
103 *blk_start
= *parse_start
;
105 *blk_end
= *blk_start
;
107 for (nnl
= 0; nnl
== 0; ++(*parse_start
)) {
108 if (**parse_start
== '\r')
110 else if (**parse_start
== '\n')
119 /* find first CRLF */
121 httpMsgIsolateStart(const char **parse_start
, const char **blk_start
, const char **blk_end
)
123 int slen
= strcspn(*parse_start
, "\r\n");
125 if (!(*parse_start
)[slen
]) /* no CRLF found */
128 *blk_start
= *parse_start
;
130 *blk_end
= *blk_start
+ slen
;
132 while (**blk_end
== '\r') /* CR */
135 if (**blk_end
== '\n') /* LF */
138 *parse_start
= *blk_end
;
143 // negative return is the negated HTTP_ error code
144 // zero return means need more data
145 // positive return is the size of parsed headers
146 bool HttpMsg::parse(MemBuf
*buf
, bool eof
, http_status
*error
)
149 *error
= HTTP_STATUS_NONE
;
151 // httpMsgParseStep() and debugging require 0-termination, unfortunately
152 buf
->terminate(); // does not affect content size
154 // find the end of headers
155 const size_t hdr_len
= headersEnd(buf
->content(), buf
->contentSize());
157 // sanity check the start line to see if this is in fact an HTTP message
158 if (!sanityCheckStartLine(buf
, hdr_len
, error
)) {
159 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
160 // if we have seen the connection close, this is an error too
161 if (eof
&& *error
==HTTP_STATUS_NONE
)
162 *error
= HTTP_INVALID_HEADER
;
167 // TODO: move to httpReplyParseStep()
168 if (hdr_len
> Config
.maxReplyHeaderSize
|| (hdr_len
<= 0 && (size_t)buf
->contentSize() > Config
.maxReplyHeaderSize
)) {
169 debugs(58, DBG_IMPORTANT
, "HttpMsg::parse: Too large reply header (" << hdr_len
<< " > " << Config
.maxReplyHeaderSize
);
170 *error
= HTTP_HEADER_TOO_LARGE
;
175 debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof
<< ") in '" << buf
->content() << "'");
177 if (eof
) // iff we have seen the end, this is an error
178 *error
= HTTP_INVALID_HEADER
;
183 const int res
= httpMsgParseStep(buf
->content(), buf
->contentSize(), eof
);
185 if (res
< 0) { // error
186 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf
->content() << "'");
187 *error
= HTTP_INVALID_HEADER
;
192 debugs(58, 2, "HttpMsg::parse: strange, need more data near '" << buf
->content() << "'");
193 *error
= HTTP_INVALID_HEADER
;
194 return false; // but this should not happen due to headersEnd() above
198 debugs(58, 9, "HttpMsg::parse success (" << hdr_len
<< " bytes) near '" << buf
->content() << "'");
200 if (hdr_sz
!= (int)hdr_len
) {
201 debugs(58, DBG_IMPORTANT
, "internal HttpMsg::parse vs. headersEnd error: " <<
202 hdr_sz
<< " != " << hdr_len
);
203 hdr_sz
= (int)hdr_len
; // because old http.cc code used hdr_len
210 * parseCharBuf() takes character buffer of HTTP headers (buf),
211 * which may not be NULL-terminated, and fills in an HttpMsg
212 * structure. The parameter 'end' specifies the offset to
213 * the end of the reply headers. The caller may know where the
214 * end is, but is unable to NULL-terminate the buffer. This function
215 * returns true on success.
218 HttpMsg::parseCharBuf(const char *buf
, ssize_t end
)
222 /* reset current state, because we are not used in incremental fashion */
227 success
= httpMsgParseStep(mb
.buf
, mb
.size
, 0);
233 * parses a 0-terminating buffer into HttpMsg.
236 * 0 -- need more data (partial parse)
240 HttpMsg::httpMsgParseStep(const char *buf
, int len
, int atEnd
)
242 const char *parse_start
= buf
;
244 const char *blk_start
, *blk_end
;
245 const char **parse_end_ptr
= &blk_end
;
247 assert(pstate
< psParsed
);
249 *parse_end_ptr
= parse_start
;
251 PROF_start(HttpMsg_httpMsgParseStep
);
253 if (pstate
== psReadyToParseStartLine
) {
254 if (!httpMsgIsolateStart(&parse_start
, &blk_start
, &blk_end
)) {
255 PROF_stop(HttpMsg_httpMsgParseStep
);
259 if (!parseFirstLine(blk_start
, blk_end
)) {
260 PROF_stop(HttpMsg_httpMsgParseStep
);
261 return httpMsgParseError();
264 *parse_end_ptr
= parse_start
;
266 hdr_sz
= *parse_end_ptr
- buf
;
267 parse_len
= parse_len
- hdr_sz
;
273 * XXX This code uses parse_start; but if we're incrementally parsing then
274 * this code might not actually be given parse_start at the right spot (just
275 * after headers.) Grr.
277 if (pstate
== psReadyToParseHeaders
) {
278 if (!httpMsgIsolateHeaders(&parse_start
, parse_len
, &blk_start
, &blk_end
)) {
280 blk_start
= parse_start
, blk_end
= blk_start
+ strlen(blk_start
);
282 PROF_stop(HttpMsg_httpMsgParseStep
);
287 if (!header
.parse(blk_start
, blk_end
)) {
288 PROF_stop(HttpMsg_httpMsgParseStep
);
289 return httpMsgParseError();
294 *parse_end_ptr
= parse_start
;
296 hdr_sz
= *parse_end_ptr
- buf
;
301 PROF_stop(HttpMsg_httpMsgParseStep
);
305 /* handy: resets and returns -1 */
307 HttpMsg::httpMsgParseError()
314 HttpMsg::setContentLength(int64_t clen
)
316 header
.delById(HDR_CONTENT_LENGTH
); // if any
317 header
.putInt64(HDR_CONTENT_LENGTH
, clen
);
318 content_length
= clen
;
322 HttpMsg::persistent() const
324 if (http_ver
> HttpVersion(1, 0)) {
326 * for modern versions of HTTP: persistent unless there is
327 * a "Connection: close" header.
329 return !httpHeaderHasConnDir(&header
, "close");
331 /* for old versions of HTTP: persistent if has "keep-alive" */
332 return httpHeaderHasConnDir(&header
, "keep-alive");
336 void HttpMsg::packInto(Packer
*p
, bool full_uri
) const
338 packFirstLineInto(p
, full_uri
);
340 packerAppend(p
, "\r\n", 2);
343 void HttpMsg::hdrCacheInit()
345 content_length
= header
.getInt64(HDR_CONTENT_LENGTH
);
346 assert(NULL
== cache_control
);
347 cache_control
= header
.getCc();
351 * useful for debugging
353 void HttpMsg::firstLineBuf(MemBuf
& mb
)
356 packerToMemInit(&p
, &mb
);
357 packFirstLineInto(&p
, true);
361 // use HTTPMSGLOCK() instead of calling this directly
369 // use HTTPMSGUNLOCK() instead of calling this directly
373 assert(lock_count
> 0);