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