]> git.ipfire.org Git - thirdparty/squid.git/blob - src/http/Message.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / http / Message.cc
1 /*
2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 74 HTTP Message */
10
11 #include "squid.h"
12 #include "Debug.h"
13 #include "http/Message.h"
14 #include "http/one/Parser.h"
15 #include "HttpHdrCc.h"
16 #include "HttpHeaderTools.h"
17 #include "MemBuf.h"
18 #include "mime_header.h"
19 #include "profiler/Profiler.h"
20 #include "SquidConfig.h"
21
22 Http::Message::Message(http_hdr_owner_type owner):
23 http_ver(Http::ProtocolVersion()),
24 header(owner)
25 {}
26
27 Http::Message::~Message()
28 {
29 assert(!body_pipe);
30 }
31
32 void
33 Http::Message::putCc(const HttpHdrCc *otherCc)
34 {
35 // get rid of the old CC, if any
36 if (cache_control) {
37 delete cache_control;
38 cache_control = nullptr;
39 if (!otherCc)
40 header.delById(Http::HdrType::CACHE_CONTROL);
41 // else it will be deleted inside putCc() below
42 }
43
44 // add new CC, if any
45 if (otherCc) {
46 cache_control = new HttpHdrCc(*otherCc);
47 header.putCc(cache_control);
48 }
49 }
50
51 /* find first CRLF */
52 static int
53 httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
54 {
55 int slen = strcspn(*parse_start, "\r\n");
56
57 if (!(*parse_start)[slen]) /* no CRLF found */
58 return 0;
59
60 *blk_start = *parse_start;
61
62 *blk_end = *blk_start + slen;
63
64 while (**blk_end == '\r') /* CR */
65 ++(*blk_end);
66
67 if (**blk_end == '\n') /* LF */
68 ++(*blk_end);
69
70 *parse_start = *blk_end;
71
72 return 1;
73 }
74
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
78 bool
79 Http::Message::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error)
80 {
81 assert(error);
82 *error = Http::scNone;
83
84 // find the end of headers
85 const size_t hdr_len = headersEnd(buf, sz);
86
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;
93
94 return false;
95 }
96
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;
100 return false;
101 }
102
103 if (hdr_len <= 0) {
104 debugs(58, 3, "failed to find end of headers (eof: " << eof << ") in '" << buf << "'");
105
106 if (eof) // iff we have seen the end, this is an error
107 *error = Http::scInvalidHeader;
108
109 return false;
110 }
111
112 const int res = httpMsgParseStep(buf, sz, eof);
113
114 if (res < 0) { // error
115 debugs(58, 3, "cannot parse isolated headers in '" << buf << "'");
116 *error = Http::scInvalidHeader;
117 return false;
118 }
119
120 if (res == 0) {
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
124 }
125
126 assert(res > 0);
127 debugs(58, 9, "success (" << hdr_len << " bytes) near '" << buf << "'");
128
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
133 }
134
135 return true;
136 }
137
138 /**
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.
145 */
146 bool
147 Http::Message::parseCharBuf(const char *buf, ssize_t end)
148 {
149 MemBuf mb;
150 int success;
151 /* reset current state, because we are not used in incremental fashion */
152 reset();
153 mb.init();
154 mb.append(buf, end);
155 mb.terminate();
156 success = httpMsgParseStep(mb.buf, mb.size, 0);
157 mb.clean();
158 return success == 1;
159 }
160
161 /**
162 * parses a 0-terminated buffer into Http::Message.
163 *
164 * \retval 1 success
165 * \retval 0 need more data (partial parse)
166 * \retval -1 parse error
167 */
168 int
169 Http::Message::httpMsgParseStep(const char *buf, int len, int atEnd)
170 {
171 const char *parse_start = buf;
172 int parse_len = len;
173 const char *blk_start, *blk_end;
174 const char **parse_end_ptr = &blk_end;
175 assert(parse_start);
176 assert(pstate < Http::Message::psParsed);
177
178 *parse_end_ptr = parse_start;
179
180 PROF_start(HttpMsg_httpMsgParseStep);
181
182 if (pstate == Http::Message::psReadyToParseStartLine) {
183 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
184 PROF_stop(HttpMsg_httpMsgParseStep);
185 return 0;
186 }
187
188 if (!parseFirstLine(blk_start, blk_end)) {
189 PROF_stop(HttpMsg_httpMsgParseStep);
190 return httpMsgParseError();
191 }
192
193 *parse_end_ptr = parse_start;
194
195 hdr_sz = *parse_end_ptr - buf;
196 parse_len = parse_len - hdr_sz;
197
198 pstate = Http::Message::psReadyToParseHeaders;
199 }
200
201 /*
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.
205 */
206 if (pstate == Http::Message::psReadyToParseHeaders) {
207 size_t hsize = 0;
208 const int parsed = header.parse(parse_start, parse_len, atEnd, hsize);
209 if (parsed <= 0) {
210 PROF_stop(HttpMsg_httpMsgParseStep);
211 return !parsed ? 0 : httpMsgParseError();
212 }
213 hdr_sz += hsize;
214 hdrCacheInit();
215 pstate = Http::Message::psParsed;
216 }
217
218 PROF_stop(HttpMsg_httpMsgParseStep);
219 return 1;
220 }
221
222 bool
223 Http::Message::parseHeader(Http1::Parser &hp)
224 {
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;
230 return false;
231 }
232
233 // XXX: we are just parsing HTTP headers, not the whole message prefix here
234 hdr_sz = hp.messageHeaderSize();
235 pstate = Http::Message::psParsed;
236 hdrCacheInit();
237 return true;
238 }
239
240 /* handy: resets and returns -1 */
241 int
242 Http::Message::httpMsgParseError()
243 {
244 reset();
245 return -1;
246 }
247
248 void
249 Http::Message::setContentLength(int64_t clen)
250 {
251 header.delById(Http::HdrType::CONTENT_LENGTH); // if any
252 header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
253 content_length = clen;
254 }
255
256 bool
257 Http::Message::persistent() const
258 {
259 if (http_ver > Http::ProtocolVersion(1,0)) {
260 /*
261 * for modern versions of HTTP: persistent unless there is
262 * a "Connection: close" header.
263 */
264 static SBuf close("close", 5);
265 return !httpHeaderHasConnDir(&header, close);
266 } else {
267 /* for old versions of HTTP: persistent if has "keep-alive" */
268 static SBuf keepAlive("keep-alive", 10);
269 return httpHeaderHasConnDir(&header, keepAlive);
270 }
271 }
272
273 void
274 Http::Message::packInto(Packable *p, bool full_uri) const
275 {
276 packFirstLineInto(p, full_uri);
277 header.packInto(p);
278 p->append("\r\n", 2);
279 }
280
281 void
282 Http::Message::hdrCacheInit()
283 {
284 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
285 assert(NULL == cache_control);
286 cache_control = header.getCc();
287 }
288
289 /// useful for debugging
290 void
291 Http::Message::firstLineBuf(MemBuf &mb)
292 {
293 packFirstLineInto(&mb, true);
294 }
295