2 * Copyright (C) 1996-2018 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/Message.h"
14 #include "http/one/Parser.h"
15 #include "HttpHdrCc.h"
16 #include "HttpHeaderTools.h"
18 #include "mime_header.h"
19 #include "profiler/Profiler.h"
20 #include "SquidConfig.h"
22 Http::Message::Message(http_hdr_owner_type owner
):
23 http_ver(Http::ProtocolVersion()),
27 Http::Message::~Message()
33 Http::Message::putCc(const HttpHdrCc
*otherCc
)
35 // get rid of the old CC, if any
38 cache_control
= nullptr;
40 header
.delById(Http::HdrType::CACHE_CONTROL
);
41 // else it will be deleted inside putCc() below
46 cache_control
= new HttpHdrCc(*otherCc
);
47 header
.putCc(cache_control
);
53 httpMsgIsolateStart(const char **parse_start
, const char **blk_start
, const char **blk_end
)
55 int slen
= strcspn(*parse_start
, "\r\n");
57 if (!(*parse_start
)[slen
]) /* no CRLF found */
60 *blk_start
= *parse_start
;
62 *blk_end
= *blk_start
+ slen
;
64 while (**blk_end
== '\r') /* CR */
67 if (**blk_end
== '\n') /* LF */
70 *parse_start
= *blk_end
;
75 // negative return is the negated Http::StatusCode error code
76 // zero return means need more data
77 // positive return is the size of parsed headers
79 Http::Message::parse(const char *buf
, const size_t sz
, bool eof
, Http::StatusCode
*error
)
82 *error
= Http::scNone
;
84 // find the end of headers
85 const size_t hdr_len
= headersEnd(buf
, sz
);
87 // sanity check the start line to see if this is in fact an HTTP message
88 if (!sanityCheckStartLine(buf
, hdr_len
, error
)) {
89 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
90 // if we have seen the connection close, this is an error too
91 if (eof
&& *error
== Http::scNone
)
92 *error
= Http::scInvalidHeader
;
97 if (hdr_len
> Config
.maxReplyHeaderSize
|| (hdr_len
<= 0 && sz
> Config
.maxReplyHeaderSize
)) {
98 debugs(58, DBG_IMPORTANT
, "Too large reply header (" << hdr_len
<< " > " << Config
.maxReplyHeaderSize
);
99 *error
= Http::scHeaderTooLarge
;
104 debugs(58, 3, "failed to find end of headers (eof: " << eof
<< ") in '" << buf
<< "'");
106 if (eof
) // iff we have seen the end, this is an error
107 *error
= Http::scInvalidHeader
;
112 const int res
= httpMsgParseStep(buf
, sz
, eof
);
114 if (res
< 0) { // error
115 debugs(58, 3, "cannot parse isolated headers in '" << buf
<< "'");
116 *error
= Http::scInvalidHeader
;
121 debugs(58, 2, "strange, need more data near '" << buf
<< "'");
122 *error
= Http::scInvalidHeader
;
123 return false; // but this should not happen due to headersEnd() above
127 debugs(58, 9, "success (" << hdr_len
<< " bytes) near '" << buf
<< "'");
129 if (hdr_sz
!= (int)hdr_len
) {
130 debugs(58, DBG_IMPORTANT
, "internal Http::Message::parse vs. headersEnd error: " <<
131 hdr_sz
<< " != " << hdr_len
);
132 hdr_sz
= (int)hdr_len
; // because old http.cc code used hdr_len
139 * parseCharBuf() takes character buffer of HTTP headers (buf),
140 * which may not be NULL-terminated, and fills in an Http::Message
141 * structure. The parameter 'end' specifies the offset to
142 * the end of the reply headers. The caller may know where the
143 * end is, but is unable to NULL-terminate the buffer. This function
144 * returns true on success.
147 Http::Message::parseCharBuf(const char *buf
, ssize_t end
)
151 /* reset current state, because we are not used in incremental fashion */
156 success
= httpMsgParseStep(mb
.buf
, mb
.size
, 0);
162 * parses a 0-terminated buffer into Http::Message.
165 * \retval 0 need more data (partial parse)
166 * \retval -1 parse error
169 Http::Message::httpMsgParseStep(const char *buf
, int len
, int atEnd
)
171 const char *parse_start
= buf
;
173 const char *blk_start
, *blk_end
;
174 const char **parse_end_ptr
= &blk_end
;
176 assert(pstate
< Http::Message::psParsed
);
178 *parse_end_ptr
= parse_start
;
180 PROF_start(HttpMsg_httpMsgParseStep
);
182 if (pstate
== Http::Message::psReadyToParseStartLine
) {
183 if (!httpMsgIsolateStart(&parse_start
, &blk_start
, &blk_end
)) {
184 PROF_stop(HttpMsg_httpMsgParseStep
);
188 if (!parseFirstLine(blk_start
, blk_end
)) {
189 PROF_stop(HttpMsg_httpMsgParseStep
);
190 return httpMsgParseError();
193 *parse_end_ptr
= parse_start
;
195 hdr_sz
= *parse_end_ptr
- buf
;
196 parse_len
= parse_len
- hdr_sz
;
198 pstate
= Http::Message::psReadyToParseHeaders
;
202 * XXX This code uses parse_start; but if we're incrementally parsing then
203 * this code might not actually be given parse_start at the right spot (just
204 * after headers.) Grr.
206 if (pstate
== Http::Message::psReadyToParseHeaders
) {
208 const int parsed
= header
.parse(parse_start
, parse_len
, atEnd
, hsize
);
210 PROF_stop(HttpMsg_httpMsgParseStep
);
211 return !parsed
? 0 : httpMsgParseError();
215 pstate
= Http::Message::psParsed
;
218 PROF_stop(HttpMsg_httpMsgParseStep
);
223 Http::Message::parseHeader(Http1::Parser
&hp
)
225 // HTTP/1 message contains "zero or more header fields"
226 // zero does not need parsing
227 // XXX: c_str() reallocates. performance regression.
228 if (hp
.headerBlockSize() && !header
.parse(hp
.mimeHeader().c_str(), hp
.headerBlockSize())) {
229 pstate
= Http::Message::psError
;
233 // XXX: we are just parsing HTTP headers, not the whole message prefix here
234 hdr_sz
= hp
.messageHeaderSize();
235 pstate
= Http::Message::psParsed
;
240 /* handy: resets and returns -1 */
242 Http::Message::httpMsgParseError()
249 Http::Message::setContentLength(int64_t clen
)
251 header
.delById(Http::HdrType::CONTENT_LENGTH
); // if any
252 header
.putInt64(Http::HdrType::CONTENT_LENGTH
, clen
);
253 content_length
= clen
;
257 Http::Message::persistent() const
259 if (http_ver
> Http::ProtocolVersion(1,0)) {
261 * for modern versions of HTTP: persistent unless there is
262 * a "Connection: close" header.
264 static SBuf
close("close", 5);
265 return !httpHeaderHasConnDir(&header
, close
);
267 /* for old versions of HTTP: persistent if has "keep-alive" */
268 static SBuf
keepAlive("keep-alive", 10);
269 return httpHeaderHasConnDir(&header
, keepAlive
);
274 Http::Message::packInto(Packable
*p
, bool full_uri
) const
276 packFirstLineInto(p
, full_uri
);
278 p
->append("\r\n", 2);
282 Http::Message::hdrCacheInit()
284 content_length
= header
.getInt64(Http::HdrType::CONTENT_LENGTH
);
285 assert(NULL
== cache_control
);
286 cache_control
= header
.getCc();
289 /// useful for debugging
291 Http::Message::firstLineBuf(MemBuf
&mb
)
293 packFirstLineInto(&mb
, true);