2 * Copyright (C) 1996-2021 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/ContentLengthInterpreter.h"
14 #include "http/Message.h"
15 #include "http/one/Parser.h"
16 #include "HttpHdrCc.h"
17 #include "HttpHeaderTools.h"
19 #include "mime_header.h"
20 #include "profiler/Profiler.h"
21 #include "SquidConfig.h"
23 Http::Message::Message(http_hdr_owner_type owner
):
24 http_ver(Http::ProtocolVersion()),
28 Http::Message::~Message()
34 Http::Message::putCc(const HttpHdrCc
*otherCc
)
36 // get rid of the old CC, if any
39 cache_control
= nullptr;
41 header
.delById(Http::HdrType::CACHE_CONTROL
);
42 // else it will be deleted inside putCc() below
47 cache_control
= new HttpHdrCc(*otherCc
);
48 header
.putCc(cache_control
);
54 httpMsgIsolateStart(const char **parse_start
, const char **blk_start
, const char **blk_end
)
56 int slen
= strcspn(*parse_start
, "\r\n");
58 if (!(*parse_start
)[slen
]) /* no CRLF found */
61 *blk_start
= *parse_start
;
63 *blk_end
= *blk_start
+ slen
;
65 while (**blk_end
== '\r') /* CR */
68 if (**blk_end
== '\n') /* LF */
71 *parse_start
= *blk_end
;
76 // negative return is the negated Http::StatusCode error code
77 // zero return means need more data
78 // positive return is the size of parsed headers
80 Http::Message::parse(const char *buf
, const size_t sz
, bool eof
, Http::StatusCode
*error
)
83 *error
= Http::scNone
;
85 // find the end of headers
86 const size_t hdr_len
= headersEnd(buf
, sz
);
88 // sanity check the start line to see if this is in fact an HTTP message
89 if (!sanityCheckStartLine(buf
, hdr_len
, error
)) {
90 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
91 // if we have seen the connection close, this is an error too
92 if (eof
&& *error
== Http::scNone
)
93 *error
= Http::scInvalidHeader
;
98 if (hdr_len
> Config
.maxReplyHeaderSize
|| (hdr_len
<= 0 && sz
> Config
.maxReplyHeaderSize
)) {
99 debugs(58, DBG_IMPORTANT
, "Too large reply header (" << hdr_len
<< " > " << Config
.maxReplyHeaderSize
);
100 *error
= Http::scHeaderTooLarge
;
105 debugs(58, 3, "failed to find end of headers (eof: " << eof
<< ") in '" << buf
<< "'");
107 if (eof
) // iff we have seen the end, this is an error
108 *error
= Http::scInvalidHeader
;
113 const int res
= httpMsgParseStep(buf
, sz
, eof
);
115 if (res
< 0) { // error
116 debugs(58, 3, "cannot parse isolated headers in '" << buf
<< "'");
117 *error
= Http::scInvalidHeader
;
122 debugs(58, 2, "strange, need more data near '" << buf
<< "'");
123 *error
= Http::scInvalidHeader
;
124 return false; // but this should not happen due to headersEnd() above
128 debugs(58, 9, "success (" << hdr_len
<< " bytes) near '" << buf
<< "'");
130 if (hdr_sz
!= (int)hdr_len
) {
131 debugs(58, DBG_IMPORTANT
, "internal Http::Message::parse vs. headersEnd error: " <<
132 hdr_sz
<< " != " << hdr_len
);
133 hdr_sz
= (int)hdr_len
; // because old http.cc code used hdr_len
140 * parseCharBuf() takes character buffer of HTTP headers (buf),
141 * which may not be NULL-terminated, and fills in an Http::Message
142 * structure. The parameter 'end' specifies the offset to
143 * the end of the reply headers. The caller may know where the
144 * end is, but is unable to NULL-terminate the buffer. This function
145 * returns true on success.
148 Http::Message::parseCharBuf(const char *buf
, ssize_t end
)
152 /* reset current state, because we are not used in incremental fashion */
157 success
= httpMsgParseStep(mb
.buf
, mb
.size
, 0);
163 * parses a 0-terminated buffer into Http::Message.
166 * \retval 0 need more data (partial parse)
167 * \retval -1 parse error
170 Http::Message::httpMsgParseStep(const char *buf
, int len
, int atEnd
)
172 const char *parse_start
= buf
;
174 const char *blk_start
, *blk_end
;
175 const char **parse_end_ptr
= &blk_end
;
177 assert(pstate
< Http::Message::psParsed
);
179 *parse_end_ptr
= parse_start
;
181 PROF_start(HttpMsg_httpMsgParseStep
);
183 if (pstate
== Http::Message::psReadyToParseStartLine
) {
184 if (!httpMsgIsolateStart(&parse_start
, &blk_start
, &blk_end
)) {
185 PROF_stop(HttpMsg_httpMsgParseStep
);
189 if (!parseFirstLine(blk_start
, blk_end
)) {
190 PROF_stop(HttpMsg_httpMsgParseStep
);
191 return httpMsgParseError();
194 *parse_end_ptr
= parse_start
;
196 hdr_sz
= *parse_end_ptr
- buf
;
197 parse_len
= parse_len
- hdr_sz
;
199 pstate
= Http::Message::psReadyToParseHeaders
;
203 * XXX This code uses parse_start; but if we're incrementally parsing then
204 * this code might not actually be given parse_start at the right spot (just
205 * after headers.) Grr.
207 if (pstate
== Http::Message::psReadyToParseHeaders
) {
209 Http::ContentLengthInterpreter interpreter
;
210 configureContentLengthInterpreter(interpreter
);
211 const int parsed
= header
.parse(parse_start
, parse_len
, atEnd
, hsize
, interpreter
);
213 PROF_stop(HttpMsg_httpMsgParseStep
);
214 return !parsed
? 0 : httpMsgParseError();
218 pstate
= Http::Message::psParsed
;
221 PROF_stop(HttpMsg_httpMsgParseStep
);
226 Http::Message::parseHeader(Http1::Parser
&hp
, Http::ContentLengthInterpreter
&clen
)
228 // HTTP/1 message contains "zero or more header fields"
229 // zero does not need parsing
230 // XXX: c_str() reallocates. performance regression.
231 configureContentLengthInterpreter(clen
);
232 if (hp
.headerBlockSize() && !header
.parse(hp
.mimeHeader().c_str(), hp
.headerBlockSize(), clen
)) {
233 pstate
= Http::Message::psError
;
237 // XXX: we are just parsing HTTP headers, not the whole message prefix here
238 hdr_sz
= hp
.messageHeaderSize();
239 pstate
= Http::Message::psParsed
;
244 /* handy: resets and returns -1 */
246 Http::Message::httpMsgParseError()
253 Http::Message::setContentLength(int64_t clen
)
255 header
.delById(Http::HdrType::CONTENT_LENGTH
); // if any
256 header
.putInt64(Http::HdrType::CONTENT_LENGTH
, clen
);
257 content_length
= clen
;
261 Http::Message::persistent() const
263 if (http_ver
> Http::ProtocolVersion(1,0)) {
265 * for modern versions of HTTP: persistent unless there is
266 * a "Connection: close" header.
268 static SBuf
close("close", 5);
269 return !httpHeaderHasConnDir(&header
, close
);
271 /* for old versions of HTTP: persistent if has "keep-alive" */
272 static SBuf
keepAlive("keep-alive", 10);
273 return httpHeaderHasConnDir(&header
, keepAlive
);
278 Http::Message::packInto(Packable
*p
, bool full_uri
) const
280 packFirstLineInto(p
, full_uri
);
282 p
->append("\r\n", 2);
286 Http::Message::hdrCacheInit()
288 content_length
= header
.getInt64(Http::HdrType::CONTENT_LENGTH
);
289 assert(NULL
== cache_control
);
290 cache_control
= header
.getCc();
293 /// useful for debugging
295 Http::Message::firstLineBuf(MemBuf
&mb
)
297 packFirstLineInto(&mb
, true);