]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http/Message.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / http / Message.cc
CommitLineData
2246b732 1/*
5b74111a 2 * Copyright (C) 1996-2018 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"
63df1d28 13#include "http/Message.h"
af2980f3 14#include "http/one/Parser.h"
0c90d3b1 15#include "HttpHdrCc.h"
a5bac1d2 16#include "HttpHeaderTools.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
63df1d28 22Http::Message::Message(http_hdr_owner_type owner):
f53969cc 23 http_ver(Http::ProtocolVersion()),
63df1d28 24 header(owner)
8596962e 25{}
26
63df1d28 27Http::Message::~Message()
4a56ee8d 28{
5f8252d2 29 assert(!body_pipe);
4a56ee8d 30}
31
0c90d3b1 32void
63df1d28 33Http::Message::putCc(const HttpHdrCc *otherCc)
0c90d3b1
DD
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)
f5beb600 40 header.delById(Http::HdrType::CACHE_CONTROL);
0c90d3b1
DD
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
8596962e 51/* find first CRLF */
52static int
53httpMsgIsolateStart(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 */
95dc7ff4 65 ++(*blk_end);
8596962e 66
67 if (**blk_end == '\n') /* LF */
95dc7ff4 68 ++(*blk_end);
8596962e 69
70 *parse_start = *blk_end;
71
72 return 1;
73}
74
955394ce 75// negative return is the negated Http::StatusCode error code
8596962e 76// zero return means need more data
77// positive return is the size of parsed headers
955394ce 78bool
63df1d28 79Http::Message::parse(const char *buf, const size_t sz, bool eof, Http::StatusCode *error)
8596962e 80{
81 assert(error);
955394ce 82 *error = Http::scNone;
8596962e 83
8596962e 84 // find the end of headers
84ae6223 85 const size_t hdr_len = headersEnd(buf, sz);
8596962e 86
96ee497f
AJ
87 // sanity check the start line to see if this is in fact an HTTP message
88 if (!sanityCheckStartLine(buf, hdr_len, error)) {
281832c6
AJ
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
955394ce
AJ
91 if (eof && *error == Http::scNone)
92 *error = Http::scInvalidHeader;
281832c6 93
96ee497f
AJ
94 return false;
95 }
96
84ae6223 97 if (hdr_len > Config.maxReplyHeaderSize || (hdr_len <= 0 && sz > Config.maxReplyHeaderSize)) {
63df1d28 98 debugs(58, DBG_IMPORTANT, "Too large reply header (" << hdr_len << " > " << Config.maxReplyHeaderSize);
955394ce 99 *error = Http::scHeaderTooLarge;
c81e4de5 100 return false;
101 }
102
8596962e 103 if (hdr_len <= 0) {
63df1d28 104 debugs(58, 3, "failed to find end of headers (eof: " << eof << ") in '" << buf << "'");
8596962e 105
106 if (eof) // iff we have seen the end, this is an error
955394ce 107 *error = Http::scInvalidHeader;
8596962e 108
109 return false;
110 }
111
84ae6223 112 const int res = httpMsgParseStep(buf, sz, eof);
8596962e 113
114 if (res < 0) { // error
63df1d28 115 debugs(58, 3, "cannot parse isolated headers in '" << buf << "'");
955394ce 116 *error = Http::scInvalidHeader;
8596962e 117 return false;
118 }
119
120 if (res == 0) {
63df1d28 121 debugs(58, 2, "strange, need more data near '" << buf << "'");
955394ce 122 *error = Http::scInvalidHeader;
8596962e 123 return false; // but this should not happen due to headersEnd() above
124 }
125
126 assert(res > 0);
63df1d28 127 debugs(58, 9, "success (" << hdr_len << " bytes) near '" << buf << "'");
8596962e 128
129 if (hdr_sz != (int)hdr_len) {
63df1d28 130 debugs(58, DBG_IMPORTANT, "internal Http::Message::parse vs. headersEnd error: " <<
8596962e 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
63df1d28 138/**
bf9fb8ff 139 * parseCharBuf() takes character buffer of HTTP headers (buf),
63df1d28 140 * which may not be NULL-terminated, and fills in an Http::Message
59eed7dc 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 */
146bool
63df1d28 147Http::Message::parseCharBuf(const char *buf, ssize_t end)
59eed7dc 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();
666f514b 156 success = httpMsgParseStep(mb.buf, mb.size, 0);
59eed7dc 157 mb.clean();
158 return success == 1;
159}
8596962e 160
63df1d28
AJ
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
8596962e 167 */
168int
63df1d28 169Http::Message::httpMsgParseStep(const char *buf, int len, int atEnd)
8596962e 170{
171 const char *parse_start = buf;
666f514b 172 int parse_len = len;
8596962e 173 const char *blk_start, *blk_end;
174 const char **parse_end_ptr = &blk_end;
175 assert(parse_start);
fb654382 176 assert(pstate < Http::Message::psParsed);
8596962e 177
178 *parse_end_ptr = parse_start;
179
9ea37c79 180 PROF_start(HttpMsg_httpMsgParseStep);
181
fb654382 182 if (pstate == Http::Message::psReadyToParseStartLine) {
9ea37c79 183 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
137e94fd
AJ
184 PROF_stop(HttpMsg_httpMsgParseStep);
185 return 0;
26ac0430 186 }
8596962e 187
9ea37c79 188 if (!parseFirstLine(blk_start, blk_end)) {
137e94fd
AJ
189 PROF_stop(HttpMsg_httpMsgParseStep);
190 return httpMsgParseError();
26ac0430 191 }
8596962e 192
193 *parse_end_ptr = parse_start;
194
195 hdr_sz = *parse_end_ptr - buf;
26ac0430 196 parse_len = parse_len - hdr_sz;
8596962e 197
fb654382 198 pstate = Http::Message::psReadyToParseHeaders;
8596962e 199 }
200
666f514b 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 */
fb654382 206 if (pstate == Http::Message::psReadyToParseHeaders) {
69c698a3
EB
207 size_t hsize = 0;
208 const int parsed = header.parse(parse_start, parse_len, atEnd, hsize);
209 if (parsed <= 0) {
137e94fd 210 PROF_stop(HttpMsg_httpMsgParseStep);
69c698a3 211 return !parsed ? 0 : httpMsgParseError();
137e94fd 212 }
69c698a3 213 hdr_sz += hsize;
07947ad8 214 hdrCacheInit();
fb654382 215 pstate = Http::Message::psParsed;
8596962e 216 }
137e94fd 217
9ea37c79 218 PROF_stop(HttpMsg_httpMsgParseStep);
137e94fd 219 return 1;
8596962e 220}
221
af2980f3 222bool
63df1d28 223Http::Message::parseHeader(Http1::Parser &hp)
af2980f3
AJ
224{
225 // HTTP/1 message contains "zero or more header fields"
226 // zero does not need parsing
af2980f3 227 // XXX: c_str() reallocates. performance regression.
563afef6 228 if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize())) {
fb654382 229 pstate = Http::Message::psError;
563afef6 230 return false;
af2980f3
AJ
231 }
232
bd59d61c
EB
233 // XXX: we are just parsing HTTP headers, not the whole message prefix here
234 hdr_sz = hp.messageHeaderSize();
fb654382 235 pstate = Http::Message::psParsed;
563afef6
AR
236 hdrCacheInit();
237 return true;
af2980f3
AJ
238}
239
8596962e 240/* handy: resets and returns -1 */
241int
63df1d28 242Http::Message::httpMsgParseError()
8596962e 243{
244 reset();
8596962e 245 return -1;
246}
247
3ff65596 248void
63df1d28 249Http::Message::setContentLength(int64_t clen)
3ff65596 250{
789217a2
FC
251 header.delById(Http::HdrType::CONTENT_LENGTH); // if any
252 header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
3ff65596
AR
253 content_length = clen;
254}
255
4a1acc56 256bool
63df1d28 257Http::Message::persistent() const
2246b732 258{
2592bc70 259 if (http_ver > Http::ProtocolVersion(1,0)) {
62e76326 260 /*
261 * for modern versions of HTTP: persistent unless there is
262 * a "Connection: close" header.
263 */
d5f18517
AJ
264 static SBuf close("close", 5);
265 return !httpHeaderHasConnDir(&header, close);
3872be7c 266 } else {
3872be7c 267 /* for old versions of HTTP: persistent if has "keep-alive" */
d5f18517
AJ
268 static SBuf keepAlive("keep-alive", 10);
269 return httpHeaderHasConnDir(&header, keepAlive);
3872be7c 270 }
2246b732 271}
8596962e 272
63df1d28
AJ
273void
274Http::Message::packInto(Packable *p, bool full_uri) const
8596962e 275{
276 packFirstLineInto(p, full_uri);
a9925b40 277 header.packInto(p);
785b508d 278 p->append("\r\n", 2);
8596962e 279}
280
63df1d28
AJ
281void
282Http::Message::hdrCacheInit()
07947ad8 283{
789217a2 284 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
07947ad8 285 assert(NULL == cache_control);
a9925b40 286 cache_control = header.getCc();
07947ad8 287}
3cfc19b3 288
63df1d28
AJ
289/// useful for debugging
290void
291Http::Message::firstLineBuf(MemBuf &mb)
3cfc19b3 292{
10201568 293 packFirstLineInto(&mb, true);
3cfc19b3 294}
f53969cc 295