HttpReply.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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 58 HTTP Reply (Response) */
10
11#include "squid.h"
12#include "acl/AclSizeLimit.h"
13#include "acl/FilledChecklist.h"
14#include "base/EnumIterator.h"
15#include "globals.h"
17#include "HttpBody.h"
18#include "HttpHdrCc.h"
19#include "HttpHdrContRange.h"
20#include "HttpHdrSc.h"
21#include "HttpReply.h"
22#include "HttpRequest.h"
23#include "MemBuf.h"
24#include "SquidConfig.h"
25#include "Store.h"
26#include "StrList.h"
27
29 Http::Message(hoReply),
30 date(0),
31 last_modified(0),
32 expires(0),
33 surrogate_control(nullptr),
34 keep_alive(0),
35 protoPrefix("HTTP/"),
36 bodySizeMax(-2),
37 content_range(nullptr)
38{
39 init();
40}
41
43{
44 if (do_clean)
45 clean();
46}
47
48void
50{
52 sline.init();
54 do_clean = true;
55}
56
58{
59
60 // reset should not reset the protocol; could have made protoPrefix a
61 // virtual function instead, but it is not clear whether virtual methods
62 // are allowed with MEMPROXY_CLASS() and whether some cbdata void*
63 // conversions are not going to kill virtual tables
64 const String pfx = protoPrefix;
65 clean();
66 init();
67 protoPrefix = pfx;
68}
69
70void
72{
73 // we used to assert that the pipe is NULL, but now the message only
74 // points to a pipe that is owned and initiated by another object.
75 body_pipe = nullptr;
76
77 body.clear();
79 header.clean();
80 sline.clean();
81 bodySizeMax = -2; // hack: make calculatedBodySizeMax() false
82}
83
84void
86{
87 sline.packInto(&p);
88 header.packInto(&p);
89 p.append("\r\n", 2);
90}
91
92void
94{
95 MemBuf buf;
96 buf.init();
98 p.append(buf.content(), buf.contentSize());
99}
100
101void
103{
105 body.packInto(&buf);
106}
107
108/* create memBuf, create mem-based packer, pack, destroy packer, return MemBuf */
109MemBuf *
111{
112 MemBuf *mb = new MemBuf;
113 mb->init();
114 packInto(*mb);
115 return mb;
116}
117
120
122 rep->sline.set(Http::ProtocolVersion(), Http::scOkay, "Connection established");
123 return rep;
124}
125
128{
130
131 const HttpReplyPointer rv(new HttpReply);
132 int t;
134
135 /* rv->content_length; */
136 rv->date = date;
138 rv->expires = expires;
140 /* rv->content_range */
141 /* rv->keep_alive */
143
144 for (t = 0; ImsEntries[t] != Http::HdrType::OTHER; ++t) {
145 if ((e = header.findEntry(ImsEntries[t])))
146 rv->header.addEntry(e->clone());
147 }
148
149 rv->putCc(cache_control);
150
151 /* rv->body */
152 return rv;
153}
154
155MemBuf *
157{
158 /* Not as efficient as skipping the header duplication,
159 * but easier to maintain
160 */
161 const auto temp = make304();
162 MemBuf *rv = temp->pack();
163 return rv;
164}
165
166void
167HttpReply::setHeaders(Http::StatusCode status, const char *reason,
168 const char *ctype, int64_t clen, time_t lmt, time_t expiresTime)
169{
170 HttpHeader *hdr;
171 sline.set(Http::ProtocolVersion(), status, reason);
172 hdr = &header;
176
177 if (ctype) {
179 content_type = ctype;
180 } else
182
183 if (clen >= 0)
185
186 if (expiresTime >= 0)
187 hdr->putTime(Http::HdrType::EXPIRES, expiresTime);
188
189 if (lmt > 0) /* this used to be lmt != 0 @?@ */
191
193
194 content_length = clen;
195
196 expires = expiresTime;
197
198 last_modified = lmt;
199}
200
201void
202HttpReply::redirect(Http::StatusCode status, const char *loc)
203{
204 HttpHeader *hdr;
205 sline.set(Http::ProtocolVersion(), status, nullptr);
206 hdr = &header;
212 content_length = 0;
213}
214
215/* compare the validators of two replies.
216 * 1 = they match
217 * 0 = they do not match
218 */
219int
221{
222 String one,two;
223 assert (otherRep);
224 /* Numbers first - easiest to check */
225 /* Content-Length */
226 /* TODO: remove -1 bypass */
227
228 if (content_length != otherRep->content_length
229 && content_length > -1 &&
230 otherRep->content_length > -1)
231 return 0;
232
233 /* ETag */
235
237
238 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
239 one.clean();
240 two.clean();
241 return 0;
242 }
243
244 if (last_modified != otherRep->last_modified)
245 return 0;
246
247 /* MD5 */
249
251
252 if (one.size()==0 || two.size()==0 || one.caseCmp(two)!=0 ) {
253 one.clean();
254 two.clean();
255 return 0;
256 }
257
258 return 1;
259}
260
263{
264 // If enough 304s do not update, then this expensive checking is cheaper
265 // than blindly storing reply prefix identical to the already stored one.
266 if (!header.needUpdate(&reply304.header))
267 return nullptr;
268
269 const Pointer cloned = clone();
270 cloned->header.update(&reply304.header);
271 cloned->hdrCacheClean();
272 cloned->header.compact();
273 cloned->hdrCacheInit();
274 return cloned;
275}
276
277/* internal routines */
278
279time_t
281{
282 /* The s-maxage and max-age directive takes priority over Expires */
283
284 if (cache_control) {
285 int maxAge = -1;
286 /*
287 * Conservatively handle the case when we have a max-age
288 * header, but no Date for reference?
289 */
290 if (cache_control->hasSMaxAge(&maxAge) || cache_control->hasMaxAge(&maxAge))
291 return (date >= 0) ? date + maxAge : squid_curtime;
292 }
293
296 const time_t d = header.getTime(Http::HdrType::DATE);
297 const time_t e = header.getTime(Http::HdrType::EXPIRES);
298
299 if (d == e)
300 return -1;
301 }
302
304 const time_t e = header.getTime(Http::HdrType::EXPIRES);
305 /*
306 * HTTP/1.0 says that robust implementations should consider
307 * bad or malformed Expires header as equivalent to "expires
308 * immediately."
309 */
310 return e < 0 ? squid_curtime : e;
311 }
312
313 return -1;
314}
315
316/* sync this routine when you update HttpReply struct */
317void
319{
321
328 header.getContRange() : nullptr;
329 keep_alive = persistent() ? 1 : 0;
330 const char *str = header.getStr(Http::HdrType::CONTENT_TYPE);
331
332 if (str)
333 content_type.assign(str, strcspn(str, ";\t "));
334 else
336
337 /* be sure to set expires after date and cache-control */
339}
340
341const HttpHdrContRange *
343{
345 return content_range;
346}
347
348/* sync this routine when you update HttpReply struct */
349void
351{
353
354 if (cache_control) {
355 delete cache_control;
356 cache_control = nullptr;
357 }
358
359 if (surrogate_control) {
360 delete surrogate_control;
361 surrogate_control = nullptr;
362 }
363
364 if (content_range) {
365 delete content_range;
366 content_range = nullptr;
367 }
368}
369
370/*
371 * Returns the body size of a HTTP response
372 */
373int64_t
375{
376 if (sline.version.major < 1)
377 return -1;
378 else if (method.id() == Http::METHOD_HEAD)
379 return 0;
380 else if (sline.status() == Http::scOkay)
381 (void) 0; /* common case, continue */
382 else if (sline.status() == Http::scNoContent)
383 return 0;
384 else if (sline.status() == Http::scNotModified)
385 return 0;
386 else if (sline.status() < Http::scOkay)
387 return 0;
388
389 return content_length;
390}
391
398bool
399HttpReply::sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error)
400{
401 // hack warning: using psize instead of size here due to type mismatches with MemBuf.
402
403 // content is long enough to possibly hold a reply
404 // 4 being magic size of a 3-digit number plus space delimiter
405 if (hdr_len < (size_t)(protoPrefix.psize() + 4)) {
406 if (hdr_len > 0) {
407 debugs(58, 3, "Too small reply header (" << hdr_len << " bytes)");
409 }
410 return false;
411 }
412
413 int pos;
414 // catch missing or mismatched protocol identifier
415 // allow special-case for ICY protocol (non-HTTP identifier) in response to faked HTTP request.
416 if (strncmp(buf, "ICY", 3) == 0) {
417 protoPrefix = "ICY";
418 pos = protoPrefix.psize();
419 } else {
420
421 if (protoPrefix.cmp(buf, protoPrefix.size()) != 0) {
422 debugs(58, 3, "missing protocol prefix (" << protoPrefix << ") in '" << buf << "'");
424 return false;
425 }
426
427 // catch missing or negative status value (negative '-' is not a digit)
428 pos = protoPrefix.psize();
429
430 // skip arbitrary number of digits and a dot in the version portion
431 while ((size_t)pos <= hdr_len && (*(buf+pos) == '.' || xisdigit(*(buf+pos)) ) ) ++pos;
432
433 // catch missing version info
434 if (pos == protoPrefix.psize()) {
435 debugs(58, 3, "missing protocol version numbers (ie. " << protoPrefix << "/1.0) in '" << buf << "'");
437 return false;
438 }
439 }
440
441 // skip arbitrary number of spaces...
442 while ((size_t)pos <= hdr_len && (char)*(buf+pos) == ' ') ++pos;
443
444 if ((size_t)pos < hdr_len && !xisdigit(*(buf+pos))) {
445 debugs(58, 3, "missing or invalid status number in '" << buf << "'");
447 return false;
448 }
449
450 return true;
451}
452
453bool
454HttpReply::parseFirstLine(const char *blk_start, const char *blk_end)
455{
456 return sline.parse(protoPrefix, blk_start, blk_end);
457}
458
459void
461{
462 interpreter.applyStatusCodeRules(sline.status());
463}
464
465bool
467{
469 return Message::parseHeader(hp, clen);
470}
471
472/* handy: resets and returns -1 */
473int
475{
477 /* indicate an error in the status line */
479 return result;
480}
481
482/*
483 * Indicate whether or not we would usually expect an entity-body
484 * along with this response
485 */
486bool
487HttpReply::expectingBody(const HttpRequestMethod& req_method, int64_t& theSize) const
488{
489 bool expectBody = true;
490
491 if (req_method == Http::METHOD_HEAD)
492 expectBody = false;
493 else if (sline.status() == Http::scNoContent)
494 expectBody = false;
495 else if (sline.status() == Http::scNotModified)
496 expectBody = false;
497 // TODO: Consider assuming that gray-area 0xx responses have bodies, like 9xx responses.
498 else if (sline.status() < Http::scOkay)
499 expectBody = false;
500 else
501 expectBody = true;
502
503 if (expectBody) {
504 if (header.chunked())
505 theSize = -1;
506 else if (content_length >= 0)
507 theSize = content_length;
508 else
509 theSize = -1;
510 }
511
512 return expectBody;
513}
514
515bool
516HttpReply::receivedBodyTooLarge(HttpRequest& request, int64_t receivedSize)
517{
518 calcMaxBodySize(request);
519 debugs(58, 3, receivedSize << " >? " << bodySizeMax);
520 return bodySizeMax >= 0 && receivedSize > bodySizeMax;
521}
522
523bool
525{
526 calcMaxBodySize(request);
527 debugs(58, 7, "bodySizeMax=" << bodySizeMax);
528
529 if (bodySizeMax < 0) // no body size limit
530 return false;
531
532 int64_t expectedSize = -1;
533 if (!expectingBody(request.method, expectedSize))
534 return false;
535
536 debugs(58, 6, expectedSize << " >? " << bodySizeMax);
537
538 if (expectedSize < 0) // expecting body of an unknown length
539 return false;
540
541 return expectedSize > bodySizeMax;
542}
543
544void
546{
547 // hack: -2 is used as "we have not calculated max body size yet" state
548 if (bodySizeMax != -2) // already tried
549 return;
550 bodySizeMax = -1;
551
552 // short-circuit ACL testing if there are none configured
554 return;
555
556 ACLFilledChecklist ch(nullptr, &request, nullptr);
557 // XXX: cont-cast becomes irrelevant when checklist is HttpReply::Pointer
558 ch.reply = const_cast<HttpReply *>(this);
559 HTTPMSGLOCK(ch.reply);
560 for (AclSizeLimit *l = Config.ReplyBodySize; l; l = l -> next) {
561 /* if there is no ACL list or if the ACLs listed match use this size value */
562 if (!l->aclList || ch.fastCheck(l->aclList).allowed()) {
563 debugs(58, 4, "bodySizeMax=" << bodySizeMax);
564 bodySizeMax = l->size; // may be -1
565 break;
566 }
567 }
568}
569
570// XXX: check that this is sufficient for eCAP cloning
571HttpReply *
573{
574 HttpReply *rep = new HttpReply();
575 rep->sline = sline; // used in hdrCacheInit() call below
576 rep->header.append(&header);
577 rep->hdrCacheInit();
578 rep->hdr_sz = hdr_sz;
579 rep->http_ver = http_ver;
580 rep->pstate = pstate;
581 rep->body_pipe = body_pipe;
582
583 // keep_alive is handled in hdrCacheInit()
584 return rep;
585}
586
587bool
589{
590 const HttpReply *aRep = dynamic_cast<const HttpReply*>(aMsg);
591 if (!aRep)
592 return false;
593 keep_alive = aRep->keep_alive;
594 sources = aRep->sources;
595 return true;
596}
597
598bool
600{
601 if (!them || !them->date || !date)
602 return false;
603 return date < them->date;
604}
605
606void
610 debugs(58, 3, "Removing unexpected Content-Length header");
611}
612
@ hoReply
Definition: HttpHeader.h:37
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
void error(char *format,...)
#define assert(EX)
Definition: assert.h:17
Acl::Answer const & fastCheck()
Definition: Checklist.cc:332
representation of a class of Size-limit ACLs
Definition: AclSizeLimit.h:17
bool allowed() const
Definition: Acl.h:156
unsigned int major
major version number
void packInto(Packable *) const
Definition: HttpBody.cc:14
void clear()
clear the HttpBody content
Definition: HttpBody.h:35
bool hasSMaxAge(int32_t *val=nullptr) const
Definition: HttpHdrCc.h:127
bool hasMaxAge(int32_t *val=nullptr) const
Definition: HttpHdrCc.h:122
HttpHeaderEntry * clone() const
Definition: HttpHeader.cc:1539
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1027
bool chunked() const
Definition: HttpHeader.h:168
String getStrOrList(Http::HdrType id) const
Definition: HttpHeader.cc:854
void compact()
Definition: HttpHeader.cc:718
int delById(Http::HdrType id)
Definition: HttpHeader.cc:673
void update(const HttpHeader *fresh)
Definition: HttpHeader.cc:274
const char * getStr(Http::HdrType id) const
Definition: HttpHeader.cc:1196
bool needUpdate(const HttpHeader *fresh) const
Definition: HttpHeader.cc:251
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:743
HttpHdrContRange * getContRange() const
Definition: HttpHeader.cc:1294
void putInt64(Http::HdrType id, int64_t number)
Definition: HttpHeader.cc:1009
time_t getTime(Http::HdrType id) const
Definition: HttpHeader.cc:1179
int has(Http::HdrType id) const
Definition: HttpHeader.cc:969
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1166
void clean()
Definition: HttpHeader.cc:192
HttpHeaderEntry * findEntry(Http::HdrType id) const
Definition: HttpHeader.cc:609
void putTime(Http::HdrType id, time_t htime)
Definition: HttpHeader.cc:1018
HttpHdrSc * getSc() const
Definition: HttpHeader.cc:1272
void packInto(Packable *p, bool mask_sensitive_info=false) const
Definition: HttpHeader.cc:546
void append(const HttpHeader *src)
Definition: HttpHeader.cc:238
bool parseFirstLine(const char *start, const char *end) override
Definition: HttpReply.cc:454
Http::StatusLine sline
Definition: HttpReply.h:56
String content_type
Definition: HttpReply.h:46
void hdrCacheClean()
Definition: HttpReply.cc:350
MemBuf * pack() const
Definition: HttpReply.cc:110
time_t hdrExpirationTime()
Definition: HttpReply.cc:280
time_t last_modified
Definition: HttpReply.h:42
HttpHdrContRange * content_range
parsed Content-Range; nil for non-206 responses!
Definition: HttpReply.h:152
const HttpHdrContRange * contentRange() const
Definition: HttpReply.cc:342
int httpMsgParseError() override
Definition: HttpReply.cc:474
void configureContentLengthInterpreter(Http::ContentLengthInterpreter &) override
configures the interpreter as needed
Definition: HttpReply.cc:460
HttpReplyPointer make304() const
Definition: HttpReply.cc:127
bool olderThan(const HttpReply *them) const
Definition: HttpReply.cc:599
bool expectingBody(const HttpRequestMethod &, int64_t &) const override
Definition: HttpReply.cc:487
bool parseHeader(Http1::Parser &hp)
parses reply header using Parser
Definition: HttpReply.cc:466
HttpHdrSc * surrogate_control
Definition: HttpReply.h:48
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:167
void init()
Definition: HttpReply.cc:49
bool inheritProperties(const Http::Message *) override
Definition: HttpReply.cc:588
void calcMaxBodySize(HttpRequest &request) const
Definition: HttpReply.cc:545
int64_t bodySize(const HttpRequestMethod &) const
Definition: HttpReply.cc:374
MemBuf * packed304Reply() const
Definition: HttpReply.cc:156
HttpBody body
Definition: HttpReply.h:58
int64_t bodySizeMax
Definition: HttpReply.h:150
void removeIrrelevantContentLength()
Some response status codes prohibit sending Content-Length (RFC 7230 section 3.3.2).
Definition: HttpReply.cc:607
bool receivedBodyTooLarge(HttpRequest &, int64_t receivedBodySize)
Definition: HttpReply.cc:516
void redirect(Http::StatusCode, const char *)
Definition: HttpReply.cc:202
bool sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error) override
Definition: HttpReply.cc:399
int validatorsMatch(HttpReply const *other) const
Definition: HttpReply.cc:220
void hdrCacheInit() override
Definition: HttpReply.cc:318
void reset() override
Definition: HttpReply.cc:57
void packHeadersUsingSlowPacker(Packable &p) const
same as packHeadersUsingFastPacker() but assumes that p cannot quickly process small additions
Definition: HttpReply.cc:93
~HttpReply() override
Definition: HttpReply.cc:42
bool do_clean
Definition: HttpReply.h:62
void packInto(MemBuf &) const
Definition: HttpReply.cc:102
void packHeadersUsingFastPacker(Packable &p) const
Definition: HttpReply.cc:85
Pointer recreateOnNotModified(const HttpReply &reply304) const
Definition: HttpReply.cc:262
void clean()
Definition: HttpReply.cc:71
bool expectedBodyTooLarge(HttpRequest &request)
Definition: HttpReply.cc:524
time_t date
Definition: HttpReply.h:40
HttpReply * clone() const override
Definition: HttpReply.cc:572
static HttpReplyPointer MakeConnectionEstablished()
construct and return an HTTP/200 (Connection Established) response
Definition: HttpReply.cc:119
String protoPrefix
Definition: HttpReply.h:60
short int keep_alive
Definition: HttpReply.h:53
time_t expires
Definition: HttpReply.h:44
Http::MethodType id() const
Definition: RequestMethod.h:70
HttpRequestMethod method
Definition: HttpRequest.h:114
void applyStatusCodeRules(const StatusCode code)
prohibits Content-Length in 1xx and 204 responses
common parts of HttpRequest and HttpReply
Definition: Message.h:26
virtual void hdrCacheInit()
Definition: Message.cc:272
bool persistent() const
Definition: Message.cc:247
uint32_t sources
The message sources.
Definition: Message.h:99
virtual int httpMsgParseError()
Definition: Message.cc:232
@ psReadyToParseStartLine
Definition: Message.h:87
HttpHeader header
Definition: Message.h:74
int hdr_sz
Definition: Message.h:81
ParseState pstate
the current parsing state
Definition: Message.h:94
int64_t content_length
Definition: Message.h:83
void putCc(const HttpHdrCc *otherCc)
copies Cache-Control header to this message
Definition: Message.cc:33
BodyPipe::Pointer body_pipe
optional pipeline to receive message body
Definition: Message.h:97
HttpHdrCc * cache_control
Definition: Message.h:76
AnyP::ProtocolVersion http_ver
Definition: Message.h:72
bool parse(const String &protoPrefix, const char *start, const char *end)
Definition: StatusLine.cc:86
void init()
reset this status-line back to empty state
Definition: StatusLine.cc:22
void clean()
reset this status-line back to Internal Server Error state
Definition: StatusLine.cc:28
void packInto(Packable *) const
pack fields into a Packable object
Definition: StatusLine.cc:50
AnyP::ProtocolVersion version
breakdown of protocol version label: (HTTP/ICY) and (0.9/1.0/1.1)
Definition: StatusLine.h:62
void set(const AnyP::ProtocolVersion &newVersion, Http::StatusCode newStatus, const char *newReason=nullptr)
Definition: StatusLine.cc:35
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
Definition: MemBuf.h:24
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
char * content()
start of the added data
Definition: MemBuf.h:41
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
int vary_ignore_expire
Definition: SquidConfig.h:310
struct SquidConfig::@106 onoff
AclSizeLimit * ReplyBodySize
Definition: SquidConfig.h:136
int psize() const
Definition: SquidString.h:77
int cmp(char const *) const
Definition: String.cc:255
void clean()
Definition: String.cc:118
void assign(const char *str, int len)
Definition: String.cc:89
size_type size() const
Definition: SquidString.h:73
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
char const * visible_appname_string
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:160
Definition: forward.h:18
bool ProhibitsContentLength(const StatusCode sc)
whether this response status code prohibits sending Content-Length
Definition: StatusCode.h:96
StatusCode
Definition: StatusCode.h:20
@ scInvalidHeader
Definition: StatusCode.h:86
@ scNotModified
Definition: StatusCode.h:40
@ scOkay
Definition: StatusCode.h:26
@ scNoContent
Definition: StatusCode.h:30
@ scPartialContent
Definition: StatusCode.h:32
@ METHOD_HEAD
Definition: MethodType.h:28
@ CONTENT_LENGTH
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.
#define xisdigit(x)
Definition: xis.h:18

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors