Message.cc
Go to the documentation of this file.
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"
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 "SquidConfig.h"
21 
23  http_ver(Http::ProtocolVersion()),
24  header(owner)
25 {}
26 
28 {
29  assert(!body_pipe);
30 }
31 
32 void
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);
83 
84  // find the end of headers
85  const size_t hdr_len = headersEnd(buf, sz);
86 
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);
90  return false;
91  }
92 
93  // sanity check the start line to see if this is in fact an HTTP message
94  if (!sanityCheckStartLine(buf, hdr_len, error)) {
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
97  if (eof && *error == Http::scNone)
99 
100  return false;
101  }
102 
103  assert(hdr_len > 0); // sanityCheckStartLine() rejects buffers that cannot be parsed
104 
105  const int res = httpMsgParseStep(buf, sz, eof);
106 
107  if (res < 0) { // error
108  debugs(58, 3, "cannot parse isolated headers in '" << buf << "'");
110  return false;
111  }
112 
113  if (res == 0) {
114  debugs(58, 2, "strange, need more data near '" << buf << "'");
116  return false; // but this should not happen due to headersEnd() above
117  }
118 
119  assert(res > 0);
120  debugs(58, 9, "success (" << hdr_len << " bytes) near '" << buf << "'");
121 
122  if (hdr_sz != (int)hdr_len) {
123  debugs(58, DBG_IMPORTANT, "internal Http::Message::parse vs. headersEnd error: " <<
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 
139 bool
140 Http::Message::parseCharBuf(const char *buf, ssize_t end)
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();
149  success = httpMsgParseStep(mb.buf, mb.size, 0);
150  mb.clean();
151  return success == 1;
152 }
153 
161 int
162 Http::Message::httpMsgParseStep(const char *buf, int len, int atEnd)
163 {
164  const char *parse_start = buf;
165  int parse_len = len;
166  const char *blk_start, *blk_end;
167  const char **parse_end_ptr = &blk_end;
168  assert(parse_start);
170 
171  *parse_end_ptr = parse_start;
172 
174  if (!httpMsgIsolateStart(&parse_start, &blk_start, &blk_end)) {
175  return 0;
176  }
177 
178  if (!parseFirstLine(blk_start, blk_end)) {
179  return httpMsgParseError();
180  }
181 
182  *parse_end_ptr = parse_start;
183 
184  hdr_sz = *parse_end_ptr - buf;
185  parse_len = parse_len - hdr_sz;
186 
188  }
189 
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  */
195  if (pstate == Http::Message::psReadyToParseHeaders) {
196  size_t hsize = 0;
197  Http::ContentLengthInterpreter interpreter;
198  configureContentLengthInterpreter(interpreter);
199  const int parsed = header.parse(parse_start, parse_len, atEnd, hsize, interpreter);
200  if (parsed <= 0) {
201  return !parsed ? 0 : httpMsgParseError();
202  }
203  hdr_sz += hsize;
204  hdrCacheInit();
205  pstate = Http::Message::psParsed;
206  }
207 
208  return 1;
209 }
210 
211 bool
213 {
214  // HTTP/1 message contains "zero or more header fields"
215  // zero does not need parsing
216  // XXX: c_str() reallocates. performance regression.
217  configureContentLengthInterpreter(clen);
218  if (hp.headerBlockSize() && !header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize(), clen)) {
219  pstate = Http::Message::psError;
220  return false;
221  }
222 
223  // XXX: we are just parsing HTTP headers, not the whole message prefix here
224  hdr_sz = hp.messageHeaderSize();
225  pstate = Http::Message::psParsed;
226  hdrCacheInit();
227  return true;
228 }
229 
230 /* handy: resets and returns -1 */
231 int
233 {
234  reset();
235  return -1;
236 }
237 
238 void
240 {
241  header.delById(Http::HdrType::CONTENT_LENGTH); // if any
242  header.putInt64(Http::HdrType::CONTENT_LENGTH, clen);
243  content_length = clen;
244 }
245 
246 bool
248 {
249  if (http_ver > Http::ProtocolVersion(1,0)) {
250  /*
251  * for modern versions of HTTP: persistent unless there is
252  * a "Connection: close" header.
253  */
254  static SBuf close("close", 5);
255  return !httpHeaderHasConnDir(&header, close);
256  } else {
257  /* for old versions of HTTP: persistent if has "keep-alive" */
258  static SBuf keepAlive("keep-alive", 10);
259  return httpHeaderHasConnDir(&header, keepAlive);
260  }
261 }
262 
263 void
264 Http::Message::packInto(Packable *p, bool full_uri) const
265 {
266  packFirstLineInto(p, full_uri);
267  header.packInto(p);
268  p->append("\r\n", 2);
269 }
270 
271 void
273 {
274  content_length = header.getInt64(Http::HdrType::CONTENT_LENGTH);
275  assert(NULL == cache_control);
276  cache_control = header.getCc();
277 }
278 
280 void
282 {
283  packFirstLineInto(&mb, true);
284 }
285 
char * buf
Definition: MemBuf.h:134
void terminate()
Definition: MemBuf.cc:243
void packInto(Packable *, bool full_uri) const
produce a message copy, except for a few connection-specific settings
Definition: Message.cc:264
AnyP::ProtocolVersion ProtocolVersion()
Protocol version to use in Http::Message structures wrapping FTP messages.
Definition: Elements.cc:24
@ scNone
Definition: StatusCode.h:21
mb_size_t size
Definition: MemBuf.h:135
virtual ~Message()
Definition: Message.cc:27
void error(char *format,...)
size_type messageHeaderSize() const
Definition: Parser.h:78
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:95
Definition: SBuf.h:87
size_t maxReplyHeaderSize
Definition: SquidConfig.h:135
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:211
StatusCode
Definition: StatusCode.h:20
size_type headerBlockSize() const
Definition: Parser.h:73
Definition: forward.h:22
#define DBG_IMPORTANT
Definition: Debug.h:41
SBuf mimeHeader() const
buffer containing HTTP mime headers, excluding message first-line.
Definition: Parser.h:81
bool parseCharBuf(const char *buf, ssize_t end)
Definition: Message.cc:140
int httpMsgParseStep(const char *buf, int len, int atEnd)
Definition: Message.cc:162
@ CONTENT_LENGTH
bool persistent() const
Definition: Message.cc:247
virtual void hdrCacheInit()
Definition: Message.cc:272
bool httpHeaderHasConnDir(const HttpHeader *hdr, const SBuf &directive)
void setContentLength(int64_t)
[re]sets Content-Length header and cached value
Definition: Message.cc:239
#define NULL
Definition: types.h:166
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
Definition: MemBuf.h:24
void clean()
Definition: MemBuf.cc:112
static int httpMsgIsolateStart(const char **parse_start, const char **blk_start, const char **blk_end)
Definition: Message.cc:53
#define assert(EX)
Definition: assert.h:19
const char * c_str()
Definition: SBuf.cc:516
void const char int sz
Definition: stub_cbdata.cc:16
void putCc(const HttpHdrCc *otherCc)
copies Cache-Control header to this message
Definition: Message.cc:33
@ psReadyToParseHeaders
Definition: Message.h:89
@ scInvalidHeader
Definition: StatusCode.h:86
Message(http_hdr_owner_type)
Definition: Message.cc:22
virtual int httpMsgParseError()
Definition: Message.cc:232
void firstLineBuf(MemBuf &)
useful for debugging
Definition: Message.cc:281
bool parseHeader(Http1::Parser &, Http::ContentLengthInterpreter &)
Definition: Message.cc:212
@ scHeaderTooLarge
Definition: StatusCode.h:87
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:17
class SquidConfig Config
Definition: SquidConfig.cc:12
http_hdr_owner_type
Definition: HttpHeader.h:31
int unsigned int
Definition: stub_fd.cc:19
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
bool parse(const char *buf, const size_t sz, bool eol, Http::StatusCode *error)
Definition: Message.cc:79
@ psReadyToParseStartLine
Definition: Message.h:88

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors