]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http/Message.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / http / Message.cc
CommitLineData
2246b732 1/*
bf95c10a 2 * Copyright (C) 1996-2022 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 11#include "squid.h"
675b8408 12#include "debug/Stream.h"
4f1c93a7 13#include "http/ContentLengthInterpreter.h"
63df1d28 14#include "http/Message.h"
af2980f3 15#include "http/one/Parser.h"
0c90d3b1 16#include "HttpHdrCc.h"
a5bac1d2 17#include "HttpHeaderTools.h"
0eb49b6d 18#include "MemBuf.h"
b6149797 19#include "mime_header.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
7d59dbc9
JR
87 if (hdr_len > Config.maxReplyHeaderSize || (hdr_len == 0 && sz > Config.maxReplyHeaderSize)) {
88 debugs(58, 3, "input too large: " << hdr_len << " or " << sz << " > " << Config.maxReplyHeaderSize);
89 *error = Http::scHeaderTooLarge;
90 return false;
91 }
92
96ee497f
AJ
93 // sanity check the start line to see if this is in fact an HTTP message
94 if (!sanityCheckStartLine(buf, hdr_len, error)) {
281832c6
AJ
95 // NP: sanityCheck sets *error and sends debug warnings on syntax errors.
96 // if we have seen the connection close, this is an error too
955394ce
AJ
97 if (eof && *error == Http::scNone)
98 *error = Http::scInvalidHeader;
281832c6 99
96ee497f
AJ
100 return false;
101 }
102
7d59dbc9 103 assert(hdr_len > 0); // sanityCheckStartLine() rejects buffers that cannot be parsed
8596962e 104
84ae6223 105 const int res = httpMsgParseStep(buf, sz, eof);
8596962e 106
107 if (res < 0) { // error
63df1d28 108 debugs(58, 3, "cannot parse isolated headers in '" << buf << "'");
955394ce 109 *error = Http::scInvalidHeader;
8596962e 110 return false;
111 }
112
113 if (res == 0) {
63df1d28 114 debugs(58, 2, "strange, need more data near '" << buf << "'");
955394ce 115 *error = Http::scInvalidHeader;
8596962e 116 return false; // but this should not happen due to headersEnd() above
117 }
118
119 assert(res > 0);
63df1d28 120 debugs(58, 9, "success (" << hdr_len << " bytes) near '" << buf << "'");
8596962e 121
122 if (hdr_sz != (int)hdr_len) {
d816f28d 123 debugs(58, DBG_IMPORTANT, "ERROR: internal Http::Message::parse vs. headersEnd failure: " <<
8596962e 124 hdr_sz << " != " << hdr_len);
125 hdr_sz = (int)hdr_len; // because old http.cc code used hdr_len
126 }
127
128 return true;
129}
130
63df1d28 131/**
bf9fb8ff 132 * parseCharBuf() takes character buffer of HTTP headers (buf),
63df1d28 133 * which may not be NULL-terminated, and fills in an Http::Message
59eed7dc 134 * structure. The parameter 'end' specifies the offset to
135 * the end of the reply headers. The caller may know where the
136 * end is, but is unable to NULL-terminate the buffer. This function
137 * returns true on success.
138 */
139bool
63df1d28 140Http::Message::parseCharBuf(const char *buf, ssize_t end)
59eed7dc 141{
142 MemBuf mb;
143 int success;
144 /* reset current state, because we are not used in incremental fashion */
145 reset();
146 mb.init();
147 mb.append(buf, end);
148 mb.terminate();
666f514b 149 success = httpMsgParseStep(mb.buf, mb.size, 0);
59eed7dc 150 mb.clean();
151 return success == 1;
152}
8596962e 153
63df1d28
AJ
154/**
155 * parses a 0-terminated buffer into Http::Message.
156 *
157 * \retval 1 success
158 * \retval 0 need more data (partial parse)
159 * \retval -1 parse error
8596962e 160 */
161int
63df1d28 162Http::Message::httpMsgParseStep(const char *buf, int len, int atEnd)
8596962e 163{
164 const char *parse_start = buf;
666f514b 165 int parse_len = len;
8596962e 166 const char *blk_start, *blk_end;
167 const char **parse_end_ptr = &blk_end;
168 assert(parse_start);
fb654382 169 assert(pstate < Http::Message::psParsed);
8596962e 170
171 *parse_end_ptr = parse_start;
172
fb654382 173 if (pstate == Http::Message::psReadyToParseStartLine) {
9ea37c79 174 if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
137e94fd 175 return 0;
26ac0430 176 }
8596962e 177
9ea37c79 178 if (!parseFirstLine(blk_start, blk_end)) {
137e94fd 179 return httpMsgParseError();
26ac0430 180 }
8596962e 181
182 *parse_end_ptr = parse_start;
183
184 hdr_sz = *parse_end_ptr - buf;
26ac0430 185 parse_len = parse_len - hdr_sz;
8596962e 186
fb654382 187 pstate = Http::Message::psReadyToParseHeaders;
8596962e 188 }
189
666f514b 190 /*
191 * XXX This code uses parse_start; but if we're incrementally parsing then
192 * this code might not actually be given parse_start at the right spot (just
193 * after headers.) Grr.
194 */
fb654382 195 if (pstate == Http::Message::psReadyToParseHeaders) {
69c698a3 196 size_t hsize = 0;
4f1c93a7
EB
197 Http::ContentLengthInterpreter interpreter;
198 configureContentLengthInterpreter(interpreter);
199 const int parsed = header.parse(parse_start, parse_len, atEnd, hsize, interpreter);
69c698a3 200 if (parsed <= 0) {
69c698a3 201 return !parsed ? 0 : httpMsgParseError();
137e94fd 202 }
69c698a3 203 hdr_sz += hsize;
07947ad8 204 hdrCacheInit();
fb654382 205 pstate = Http::Message::psParsed;
8596962e 206 }
137e94fd 207
137e94fd 208 return 1;
8596962e 209}
210
af2980f3 211bool
4f1c93a7 212Http::Message::parseHeader(Http1::Parser &hp, Http::ContentLengthInterpreter &clen)
af2980f3
AJ
213{
214 // HTTP/1 message contains "zero or more header fields"
215 // zero does not need parsing
af2980f3 216 // XXX: c_str() reallocates. performance regression.
4f1c93a7
EB
217 configureContentLengthInterpreter(clen);
218 if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize(), clen)) {
fb654382 219 pstate = Http::Message::psError;
563afef6 220 return false;
af2980f3
AJ
221 }
222
bd59d61c
EB
223 // XXX: we are just parsing HTTP headers, not the whole message prefix here
224 hdr_sz = hp.messageHeaderSize();
fb654382 225 pstate = Http::Message::psParsed;
563afef6
AR
226 hdrCacheInit();
227 return true;
af2980f3
AJ
228}
229
8596962e 230/* handy: resets and returns -1 */
231int
63df1d28 232Http::Message::httpMsgParseError()
8596962e 233{
234 reset();
8596962e 235 return -1;
236}
237
3ff65596 238void
63df1d28 239Http::Message::setContentLength(int64_t clen)
3ff65596 240{
789217a2
FC
241 header.delById(Http::HdrType::CONTENT_LENGTH); // if any
242 header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
3ff65596
AR
243 content_length = clen;
244}
245
4a1acc56 246bool
63df1d28 247Http::Message::persistent() const
2246b732 248{
2592bc70 249 if (http_ver > Http::ProtocolVersion(1,0)) {
62e76326 250 /*
251 * for modern versions of HTTP: persistent unless there is
252 * a "Connection: close" header.
253 */
d5f18517
AJ
254 static SBuf close("close", 5);
255 return !httpHeaderHasConnDir(&header, close);
3872be7c 256 } else {
3872be7c 257 /* for old versions of HTTP: persistent if has "keep-alive" */
d5f18517
AJ
258 static SBuf keepAlive("keep-alive", 10);
259 return httpHeaderHasConnDir(&header, keepAlive);
3872be7c 260 }
2246b732 261}
8596962e 262
63df1d28
AJ
263void
264Http::Message::packInto(Packable *p, bool full_uri) const
8596962e 265{
266 packFirstLineInto(p, full_uri);
a9925b40 267 header.packInto(p);
785b508d 268 p->append("\r\n", 2);
8596962e 269}
270
63df1d28
AJ
271void
272Http::Message::hdrCacheInit()
07947ad8 273{
789217a2 274 content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
aee3523a 275 assert(nullptr == cache_control);
a9925b40 276 cache_control = header.getCc();
07947ad8 277}
3cfc19b3 278
63df1d28
AJ
279/// useful for debugging
280void
281Http::Message::firstLineBuf(MemBuf &mb)
3cfc19b3 282{
10201568 283 packFirstLineInto(&mb, true);
3cfc19b3 284}
4106be3f 285