]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpMsg.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / HttpMsg.cc
CommitLineData
2246b732 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
2246b732 7 */
8
bbc27441
AJ
9/* DEBUG: section 74 HTTP Message */
10
582c2af2
FC
11#include "squid.h"
12#include "Debug.h"
af2980f3 13#include "http/one/Parser.h"
0c90d3b1 14#include "HttpHdrCc.h"
a5bac1d2 15#include "HttpHeaderTools.h"
8596962e 16#include "HttpMsg.h"
0eb49b6d 17#include "MemBuf.h"
b6149797 18#include "mime_header.h"
582c2af2 19#include "profiler/Profiler.h"
4d5904f7 20#include "SquidConfig.h"
8596962e 21
ce867f0a 22HttpMsg::HttpMsg(http_hdr_owner_type owner):
f53969cc
SM
23 http_ver(Http::ProtocolVersion()),
24 header(owner),
25 cache_control(NULL),
26 hdr_sz(0),
27 content_length(0),
88df846b
CT
28 pstate(psReadyToParseStartLine),
29 sources(0)
8596962e 30{}
31
4a56ee8d 32HttpMsg::~HttpMsg()
33{
5f8252d2 34 assert(!body_pipe);
4a56ee8d 35}
36
0c90d3b1
DD
37void
38HttpMsg::putCc(const HttpHdrCc *otherCc)
39{
40 // get rid of the old CC, if any
41 if (cache_control) {
42 delete cache_control;
43 cache_control = nullptr;
44 if (!otherCc)
f5beb600 45 header.delById(Http::HdrType::CACHE_CONTROL);
0c90d3b1
DD
46 // else it will be deleted inside putCc() below
47 }
48
49 // add new CC, if any
50 if (otherCc) {
51 cache_control = new HttpHdrCc(*otherCc);
52 header.putCc(cache_control);
53 }
54}
55
8596962e 56HttpMsgParseState &operator++ (HttpMsgParseState &aState)
57{
58 int tmp = (int)aState;
59 aState = (HttpMsgParseState)(++tmp);
60 return aState;
61}
62
8596962e 63/* find first CRLF */
64static int
65httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
66{
67 int slen = strcspn(*parse_start, "\r\n");
68
69 if (!(*parse_start)[slen]) /* no CRLF found */
70 return 0;
71
72 *blk_start = *parse_start;
73
74 *blk_end = *blk_start + slen;
75
76 while (**blk_end == '\r') /* CR */
95dc7ff4 77 ++(*blk_end);
8596962e 78
79 if (**blk_end == '\n') /* LF */
95dc7ff4 80 ++(*blk_end);
8596962e 81
82 *parse_start = *blk_end;
83
84 return 1;
85}
86
955394ce 87// negative return is the negated Http::StatusCode error code
8596962e 88// zero return means need more data
89// positive return is the size of parsed headers
955394ce 90bool
84ae6223 91HttpMsg::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error)
8596962e 92{
93 assert(error);
955394ce 94 *error = Http::scNone;
8596962e 95
8596962e 96 // find the end of headers
84ae6223 97 const size_t hdr_len = headersEnd(buf, sz);
8596962e 98
96ee497f
AJ
99 // sanity check the start line to see if this is in fact an HTTP message
100 if (!sanityCheckStartLine(buf, hdr_len, error)) {
281832c6
AJ
101 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
102 // if we have seen the connection close, this is an error too
955394ce
AJ
103 if (eof && *error == Http::scNone)
104 *error = Http::scInvalidHeader;
281832c6 105
96ee497f
AJ
106 return false;
107 }
108
84ae6223 109 if (hdr_len > Config.maxReplyHeaderSize || (hdr_len <= 0 && sz > Config.maxReplyHeaderSize)) {
e0236918 110 debugs(58, DBG_IMPORTANT, "HttpMsg::parse: Too large reply header (" << hdr_len << " > " << Config.maxReplyHeaderSize);
955394ce 111 *error = Http::scHeaderTooLarge;
c81e4de5 112 return false;
113 }
114
8596962e 115 if (hdr_len <= 0) {
84ae6223 116 debugs(58, 3, "HttpMsg::parse: failed to find end of headers (eof: " << eof << ") in '" << buf << "'");
8596962e 117
118 if (eof) // iff we have seen the end, this is an error
955394ce 119 *error = Http::scInvalidHeader;
8596962e 120
121 return false;
122 }
123
84ae6223 124 const int res = httpMsgParseStep(buf, sz, eof);
8596962e 125
126 if (res < 0) { // error
84ae6223 127 debugs(58, 3, "HttpMsg::parse: cannot parse isolated headers in '" << buf << "'");
955394ce 128 *error = Http::scInvalidHeader;
8596962e 129 return false;
130 }
131
132 if (res == 0) {
84ae6223 133 debugs(58, 2, "HttpMsg::parse: strange, need more data near '" << buf << "'");
955394ce 134 *error = Http::scInvalidHeader;
8596962e 135 return false; // but this should not happen due to headersEnd() above
136 }
137
138 assert(res > 0);
84ae6223 139 debugs(58, 9, "HttpMsg::parse success (" << hdr_len << " bytes) near '" << buf << "'");
8596962e 140
141 if (hdr_sz != (int)hdr_len) {
e0236918 142 debugs(58, DBG_IMPORTANT, "internal HttpMsg::parse vs. headersEnd error: " <<
8596962e 143 hdr_sz << " != " << hdr_len);
144 hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len
145 }
146
147 return true;
148}
149
59eed7dc 150/*
bf9fb8ff 151 * parseCharBuf() takes character buffer of HTTP headers (buf),
59eed7dc 152 * which may not be NULL-terminated, and fills in an HttpMsg
153 * structure. The parameter 'end' specifies the offset to
154 * the end of the reply headers. The caller may know where the
155 * end is, but is unable to NULL-terminate the buffer. This function
156 * returns true on success.
157 */
158bool
159HttpMsg::parseCharBuf(const char *buf, ssize_t end)
160{
161 MemBuf mb;
162 int success;
163 /* reset current state, because we are not used in incremental fashion */
164 reset();
165 mb.init();
166 mb.append(buf, end);
167 mb.terminate();
666f514b 168 success = httpMsgParseStep(mb.buf, mb.size, 0);
59eed7dc 169 mb.clean();
170 return success == 1;
171}
8596962e 172
173/*
174 * parses a 0-terminating buffer into HttpMsg.
175 * Returns:
176 * 1 -- success
177 * 0 -- need more data (partial parse)
178 * -1 -- parse error
179 */
180int
666f514b 181HttpMsg::httpMsgParseStep(const char *buf, int len, int atEnd)
8596962e 182{
183 const char *parse_start = buf;
666f514b 184 int parse_len = len;
8596962e 185 const char *blk_start, *blk_end;
186 const char **parse_end_ptr = &blk_end;
187 assert(parse_start);
188 assert(pstate < psParsed);
8596962e 189
190 *parse_end_ptr = parse_start;
191
9ea37c79 192 PROF_start(HttpMsg_httpMsgParseStep);
193
8596962e 194 if (pstate == psReadyToParseStartLine) {
9ea37c79 195 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
137e94fd
AJ
196 PROF_stop(HttpMsg_httpMsgParseStep);
197 return 0;
26ac0430 198 }
8596962e 199
9ea37c79 200 if (!parseFirstLine(blk_start, blk_end)) {
137e94fd
AJ
201 PROF_stop(HttpMsg_httpMsgParseStep);
202 return httpMsgParseError();
26ac0430 203 }
8596962e 204
205 *parse_end_ptr = parse_start;
206
207 hdr_sz = *parse_end_ptr - buf;
26ac0430 208 parse_len = parse_len - hdr_sz;
8596962e 209
210 ++pstate;
211 }
212
666f514b 213 /*
214 * XXX This code uses parse_start; but if we're incrementally parsing then
215 * this code might not actually be given parse_start at the right spot (just
216 * after headers.) Grr.
217 */
8596962e 218 if (pstate == psReadyToParseHeaders) {
69c698a3
EB
219 size_t hsize = 0;
220 const int parsed = header.parse(parse_start, parse_len, atEnd, hsize);
221 if (parsed <= 0) {
137e94fd 222 PROF_stop(HttpMsg_httpMsgParseStep);
69c698a3 223 return !parsed ? 0 : httpMsgParseError();
137e94fd 224 }
69c698a3 225 hdr_sz += hsize;
07947ad8 226 hdrCacheInit();
8596962e 227 ++pstate;
228 }
137e94fd 229
9ea37c79 230 PROF_stop(HttpMsg_httpMsgParseStep);
137e94fd 231 return 1;
8596962e 232}
233
af2980f3
AJ
234bool
235HttpMsg::parseHeader(Http1::Parser &hp)
236{
237 // HTTP/1 message contains "zero or more header fields"
238 // zero does not need parsing
af2980f3 239 // XXX: c_str() reallocates. performance regression.
563afef6
AR
240 if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize())) {
241 pstate = psError;
242 return false;
af2980f3
AJ
243 }
244
bd59d61c
EB
245 // XXX: we are just parsing HTTP headers, not the whole message prefix here
246 hdr_sz = hp.messageHeaderSize();
563afef6
AR
247 pstate = psParsed;
248 hdrCacheInit();
249 return true;
af2980f3
AJ
250}
251
8596962e 252/* handy: resets and returns -1 */
253int
254HttpMsg::httpMsgParseError()
255{
256 reset();
8596962e 257 return -1;
258}
259
3ff65596
AR
260void
261HttpMsg::setContentLength(int64_t clen)
262{
789217a2
FC
263 header.delById(Http::HdrType::CONTENT_LENGTH); // if any
264 header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
3ff65596
AR
265 content_length = clen;
266}
267
4a1acc56
AJ
268bool
269HttpMsg::persistent() const
2246b732 270{
2592bc70 271 if (http_ver > Http::ProtocolVersion(1,0)) {
62e76326 272 /*
273 * for modern versions of HTTP: persistent unless there is
274 * a "Connection: close" header.
275 */
ef84c0fb 276 return !httpHeaderHasConnDir(&header, "close");
3872be7c 277 } else {
3872be7c 278 /* for old versions of HTTP: persistent if has "keep-alive" */
ef84c0fb 279 return httpHeaderHasConnDir(&header, "keep-alive");
3872be7c 280 }
2246b732 281}
8596962e 282
17802cf1 283void HttpMsg::packInto(Packable *p, bool full_uri) const
8596962e 284{
285 packFirstLineInto(p, full_uri);
a9925b40 286 header.packInto(p);
785b508d 287 p->append("\r\n", 2);
8596962e 288}
289
07947ad8 290void HttpMsg::hdrCacheInit()
291{
789217a2 292 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
07947ad8 293 assert(NULL == cache_control);
a9925b40 294 cache_control = header.getCc();
07947ad8 295}
3cfc19b3 296
297/*
298 * useful for debugging
299 */
300void HttpMsg::firstLineBuf(MemBuf& mb)
301{
10201568 302 packFirstLineInto(&mb, true);
3cfc19b3 303}
f53969cc 304