2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 74 HTTP Message */
13 #include "http/one/Parser.h"
14 #include "HttpHeaderTools.h"
17 #include "mime_header.h"
18 #include "profiler/Profiler.h"
19 #include "SquidConfig.h"
21 HttpMsg::HttpMsg(http_hdr_owner_type owner
):
22 http_ver(Http::ProtocolVersion()),
27 pstate(psReadyToParseStartLine
)
35 HttpMsgParseState
&operator++ (HttpMsgParseState
&aState
)
37 int tmp
= (int)aState
;
38 aState
= (HttpMsgParseState
)(++tmp
);
42 /* find end of headers */
44 httpMsgIsolateHeaders(const char **parse_start
, int l
, const char **blk_start
, const char **blk_end
)
47 * parse_start points to the first line of HTTP message *headers*,
48 * not including the request or status lines
50 size_t end
= headersEnd(*parse_start
, l
);
54 *blk_start
= *parse_start
;
55 *blk_end
= *parse_start
+ end
- 1;
57 * leave blk_end pointing to the first character after the
58 * first newline which terminates the headers
60 assert(**blk_end
== '\n');
62 while (*(*blk_end
- 1) == '\r')
65 assert(*(*blk_end
- 1) == '\n');
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
76 if (**parse_start
!= '\r' && **parse_start
!= '\n')
77 return 0; /* failure */
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.
85 *blk_start
= *parse_start
;
87 *blk_end
= *blk_start
;
89 for (nnl
= 0; nnl
== 0; ++(*parse_start
)) {
90 if (**parse_start
== '\r')
92 else if (**parse_start
== '\n')
101 /* find first CRLF */
103 httpMsgIsolateStart(const char **parse_start
, const char **blk_start
, const char **blk_end
)
105 int slen
= strcspn(*parse_start
, "\r\n");
107 if (!(*parse_start
)[slen
]) /* no CRLF found */
110 *blk_start
= *parse_start
;
112 *blk_end
= *blk_start
+ slen
;
114 while (**blk_end
== '\r') /* CR */
117 if (**blk_end
== '\n') /* LF */
120 *parse_start
= *blk_end
;
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
129 HttpMsg::parse(const char *buf
, const size_t sz
, bool eof
, Http::StatusCode
*error
)
132 *error
= Http::scNone
;
134 // find the end of headers
135 const size_t hdr_len
= headersEnd(buf
, sz
);
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
;
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
;
154 debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof
<< ") in '" << buf
<< "'");
156 if (eof
) // iff we have seen the end, this is an error
157 *error
= Http::scInvalidHeader
;
162 const int res
= httpMsgParseStep(buf
, sz
, eof
);
164 if (res
< 0) { // error
165 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf
<< "'");
166 *error
= Http::scInvalidHeader
;
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
177 debugs(58, 9, "HttpMsg::parse success (" << hdr_len
<< " bytes) near '" << buf
<< "'");
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
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.
197 HttpMsg::parseCharBuf(const char *buf
, ssize_t end
)
201 /* reset current state, because we are not used in incremental fashion */
206 success
= httpMsgParseStep(mb
.buf
, mb
.size
, 0);
212 * parses a 0-terminating buffer into HttpMsg.
215 * 0 -- need more data (partial parse)
219 HttpMsg::httpMsgParseStep(const char *buf
, int len
, int atEnd
)
221 const char *parse_start
= buf
;
223 const char *blk_start
, *blk_end
;
224 const char **parse_end_ptr
= &blk_end
;
226 assert(pstate
< psParsed
);
228 *parse_end_ptr
= parse_start
;
230 PROF_start(HttpMsg_httpMsgParseStep
);
232 if (pstate
== psReadyToParseStartLine
) {
233 if (!httpMsgIsolateStart(&parse_start
, &blk_start
, &blk_end
)) {
234 PROF_stop(HttpMsg_httpMsgParseStep
);
238 if (!parseFirstLine(blk_start
, blk_end
)) {
239 PROF_stop(HttpMsg_httpMsgParseStep
);
240 return httpMsgParseError();
243 *parse_end_ptr
= parse_start
;
245 hdr_sz
= *parse_end_ptr
- buf
;
246 parse_len
= parse_len
- hdr_sz
;
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.
256 if (pstate
== psReadyToParseHeaders
) {
257 if (!httpMsgIsolateHeaders(&parse_start
, parse_len
, &blk_start
, &blk_end
)) {
259 blk_start
= parse_start
;
260 blk_end
= blk_start
+ strlen(blk_start
);
262 PROF_stop(HttpMsg_httpMsgParseStep
);
267 if (!header
.parse(blk_start
, blk_end
-blk_start
)) {
268 PROF_stop(HttpMsg_httpMsgParseStep
);
269 return httpMsgParseError();
274 *parse_end_ptr
= parse_start
;
276 hdr_sz
= *parse_end_ptr
- buf
;
281 PROF_stop(HttpMsg_httpMsgParseStep
);
286 HttpMsg::parseHeader(Http1::Parser
&hp
)
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())) {
301 /* handy: resets and returns -1 */
303 HttpMsg::httpMsgParseError()
310 HttpMsg::setContentLength(int64_t clen
)
312 header
.delById(Http::HdrType::CONTENT_LENGTH
); // if any
313 header
.putInt64(Http::HdrType::CONTENT_LENGTH
, clen
);
314 content_length
= clen
;
318 HttpMsg::persistent() const
320 if (http_ver
> Http::ProtocolVersion(1,0)) {
322 * for modern versions of HTTP: persistent unless there is
323 * a "Connection: close" header.
325 return !httpHeaderHasConnDir(&header
, "close");
327 /* for old versions of HTTP: persistent if has "keep-alive" */
328 return httpHeaderHasConnDir(&header
, "keep-alive");
332 void HttpMsg::packInto(Packable
*p
, bool full_uri
) const
334 packFirstLineInto(p
, full_uri
);
336 p
->append("\r\n", 2);
339 void HttpMsg::hdrCacheInit()
341 content_length
= header
.getInt64(Http::HdrType::CONTENT_LENGTH
);
342 assert(NULL
== cache_control
);
343 cache_control
= header
.getCc();
347 * useful for debugging
349 void HttpMsg::firstLineBuf(MemBuf
& mb
)
351 packFirstLineInto(&mb
, true);