Stream.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#include "squid.h"
10#include "client_side_request.h"
11#include "http/Stream.h"
12#include "HttpHdrContRange.h"
13#include "HttpHeaderTools.h"
14#include "Store.h"
15#include "TimeOrTag.h"
16#if USE_DELAY_POOLS
17#include "acl/FilledChecklist.h"
18#include "ClientInfo.h"
19#include "fde.h"
20#include "MessageDelayPools.h"
21#endif
22
24 clientConnection(aConn),
25 http(aReq),
26 reply(nullptr),
27 writtenToSocket(0),
28 mayUseConnection_(false),
29 connRegistered_(false)
30{
31 assert(http != nullptr);
32 memset(reqbuf, '\0', sizeof (reqbuf));
33 flags.deferred = 0;
34 flags.parsed_ok = 0;
35 deferredparams.node = nullptr;
36 deferredparams.rep = nullptr;
37}
38
40{
41 if (auto node = getTail()) {
42 if (auto ctx = dynamic_cast<Http::Stream *>(node->data.getRaw())) {
43 /* We are *always* the tail - prevent recursive free */
44 assert(this == ctx);
45 node->data = nullptr;
46 }
47 }
48 httpRequestFree(http);
49}
50
51void
53{
54 assert(!connRegistered_);
55 assert(getConn());
56 connRegistered_ = true;
57 getConn()->add(this);
58}
59
60bool
62{
63 return http->out.size == 0;
64}
65
66void
68{
69 const StoreEntry *entry = http->storeEntry();
70 debugs(33, 5, clientConnection << ", sz " << size <<
71 ", off " << (http->out.size + size) << ", len " <<
72 (entry ? entry->objectLen() : 0));
73
74 http->out.size += size;
75
76 switch (socketState()) {
77
78 case STREAM_NONE:
79 pullData();
80 break;
81
82 case STREAM_COMPLETE: {
83 debugs(33, 5, clientConnection << " Stream complete, keepalive is " <<
84 http->request->flags.proxyKeepalive);
85 // XXX: This code assumes we are done with the transaction, but we may
86 // still be receiving request body. TODO: Extend stopSending() instead.
87 ConnStateData *c = getConn();
88 if (!http->request->flags.proxyKeepalive)
89 clientConnection->close();
90 finished();
91 c->kick();
92 }
93 return;
94
96 initiateClose("STREAM_UNPLANNED_COMPLETE");
97 return;
98
99 case STREAM_FAILED:
100 initiateClose("STREAM_FAILED");
101 return;
102
103 default:
104 fatal("Hit unreachable code in Http::Stream::writeComplete\n");
105 }
106}
107
108void
110{
111 debugs(33, 5, reply << " written " << http->out.size << " into " << clientConnection);
112
113 /* More data will be coming from the stream. */
114 StoreIOBuffer readBuffer;
115 /* XXX: Next requested byte in the range sequence */
116 /* XXX: length = getmaximumrangelenfgth */
117 readBuffer.offset = getNextRangeOffset();
118 readBuffer.length = HTTP_REQBUF_SZ;
119 readBuffer.data = reqbuf;
120 /* we may note we have reached the end of the wanted ranges */
121 clientStreamRead(getTail(), http, readBuffer);
122}
123
124bool
126{
127 return http->multipartRangeRequest();
128}
129
130int64_t
132{
133 debugs (33, 5, "range: " << http->request->range <<
134 "; http offset " << http->out.offset <<
135 "; reply " << reply);
136
137 // XXX: This method is called from many places, including pullData() which
138 // may be called before prepareReply() [on some Squid-generated errors].
139 // Hence, we may not even know yet whether we should honor/do ranges.
140
141 if (http->request->range) {
142 /* offset in range specs does not count the prefix of an http msg */
143 /* check: reply was parsed and range iterator was initialized */
144 assert(http->range_iter.valid);
145 /* filter out data according to range specs */
146 assert(canPackMoreRanges());
147 {
148 assert(http->range_iter.currentSpec());
149 /* offset of still missing data */
150 int64_t start = http->range_iter.currentSpec()->offset +
151 http->range_iter.currentSpec()->length -
152 http->range_iter.debt();
153 debugs(33, 3, "clientPackMoreRanges: in: offset: " << http->out.offset);
154 debugs(33, 3, "clientPackMoreRanges: out:"
155 " start: " << start <<
156 " spec[" << http->range_iter.pos - http->request->range->begin() << "]:" <<
157 " [" << http->range_iter.currentSpec()->offset <<
158 ", " << http->range_iter.currentSpec()->offset +
159 http->range_iter.currentSpec()->length << "),"
160 " len: " << http->range_iter.currentSpec()->length <<
161 " debt: " << http->range_iter.debt());
162 if (http->range_iter.currentSpec()->length != -1)
163 assert(http->out.offset <= start); /* we did not miss it */
164
165 return start;
166 }
167
168 } else if (const auto cr = reply ? reply->contentRange() : nullptr) {
169 /* request does not have ranges, but reply does */
170 /* TODO: should use range_iter_pos on reply, as soon as reply->content_range
171 * becomes HttpHdrRange rather than HttpHdrRangeSpec.
172 */
173 if (cr->spec.offset != HttpHdrRangeSpec::UnknownPosition)
174 return http->out.offset + cr->spec.offset;
175 }
176
177 return http->out.offset;
178}
179
187bool
189{
191 if (!http->range_iter.debt()) {
192 debugs(33, 5, "At end of current range spec for " << clientConnection);
193
194 if (http->range_iter.pos != http->range_iter.end)
195 ++http->range_iter.pos;
196
197 http->range_iter.updateSpec();
198 }
199
200 assert(!http->range_iter.debt() == !http->range_iter.currentSpec());
201
202 /* paranoid sync condition */
203 /* continue condition: need_more_data */
204 debugs(33, 5, "returning " << (http->range_iter.currentSpec() ? true : false));
205 return http->range_iter.currentSpec() ? true : false;
206}
207
211{
212 switch (clientStreamStatus(getTail(), http)) {
213
214 case STREAM_NONE:
215 /* check for range support ending */
216 if (http->request->range) {
217 /* check: reply was parsed and range iterator was initialized */
218 assert(http->range_iter.valid);
219 /* filter out data according to range specs */
220
221 if (!canPackMoreRanges()) {
222 debugs(33, 5, "Range request at end of returnable " <<
223 "range sequence on " << clientConnection);
224 // we got everything we wanted from the store
225 return STREAM_COMPLETE;
226 }
227 } else if (reply && reply->contentRange()) {
228 /* reply has content-range, but Squid is not managing ranges */
229 const int64_t &bytesSent = http->out.offset;
230 const int64_t &bytesExpected = reply->contentRange()->spec.length;
231
232 debugs(33, 7, "body bytes sent vs. expected: " <<
233 bytesSent << " ? " << bytesExpected << " (+" <<
234 reply->contentRange()->spec.offset << ")");
235
236 // did we get at least what we expected, based on range specs?
237
238 // this Content-Range does not tell us how many bytes to expect
239 if (bytesExpected == HttpHdrRangeSpec::UnknownPosition)
240 return STREAM_NONE;
241
242 if (bytesSent == bytesExpected) // got everything
243 return STREAM_COMPLETE;
244
245 if (bytesSent > bytesExpected) // Error: Sent more than expected
247 }
248
249 return STREAM_NONE;
250
251 case STREAM_COMPLETE:
252 return STREAM_COMPLETE;
253
256
257 case STREAM_FAILED:
258 return STREAM_FAILED;
259 }
260
261 fatal ("unreachable code\n");
262 return STREAM_NONE;
263}
264
265void
267{
268 prepareReply(rep);
269 assert(rep);
270 MemBuf *mb = rep->pack();
271
272 // dump now, so we do not output any body.
273 debugs(11, 2, "HTTP Client " << clientConnection);
274 debugs(11, 2, "HTTP Client REPLY:\n---------\n" << mb->buf << "\n----------");
275
276 /* Save length of headers for persistent conn checks */
277 http->out.headers_sz = mb->contentSize();
278
279 if (bodyData.data && bodyData.length) {
280 if (multipartRangeRequest())
281 packRange(bodyData, mb);
282 else if (http->request->flags.chunkedReply) {
283 packChunk(bodyData, *mb);
284 } else {
285 size_t length = lengthToSend(bodyData.range());
286 noteSentBodyBytes(length);
287 mb->append(bodyData.data, length);
288 }
289 }
290#if USE_DELAY_POOLS
291 for (const auto &pool: MessageDelayPools::Instance()->pools) {
292 if (pool->access) {
293 std::unique_ptr<ACLFilledChecklist> chl(clientAclChecklistCreate(pool->access, http));
294 chl->reply = rep;
295 HTTPMSGLOCK(chl->reply);
296 const auto answer = chl->fastCheck();
297 if (answer.allowed()) {
298 writeQuotaHandler = pool->createBucket();
299 fd_table[clientConnection->fd].writeQuotaHandler = writeQuotaHandler;
300 break;
301 } else {
302 debugs(83, 4, "Response delay pool " << pool->poolName <<
303 " skipped because ACL " << answer);
304 }
305 }
306 }
307#endif
308
309 getConn()->write(mb);
310 delete mb;
311}
312
313void
315{
316 if (!multipartRangeRequest() && !http->request->flags.chunkedReply) {
317 size_t length = lengthToSend(bodyData.range());
318 noteSentBodyBytes(length);
319 getConn()->write(bodyData.data, length);
320 return;
321 }
322
323 MemBuf mb;
324 mb.init();
325 if (multipartRangeRequest())
326 packRange(bodyData, &mb);
327 else
328 packChunk(bodyData, mb);
329
330 if (mb.contentSize())
331 getConn()->write(&mb);
332 else
333 writeComplete(0);
334}
335
336size_t
338{
339 // the size of available range can always fit into a size_t type
340 size_t maximum = available.size();
341
342 if (!http->request->range)
343 return maximum;
344
345 assert(canPackMoreRanges());
346
347 if (http->range_iter.debt() == -1)
348 return maximum;
349
350 assert(http->range_iter.debt() > 0);
351
352 /* TODO this + the last line could be a range intersection calculation */
353 if (available.start < http->range_iter.currentSpec()->offset)
354 return 0;
355
356 return min(http->range_iter.debt(), static_cast<int64_t>(maximum));
357}
358
359void
361{
362 debugs(33, 7, bytes << " body bytes");
363 http->out.offset += bytes;
364
365 if (!http->request->range)
366 return;
367
368 if (http->range_iter.debt() != -1) {
369 http->range_iter.debt(http->range_iter.debt() - bytes);
370 assert (http->range_iter.debt() >= 0);
371 }
372
373 /* debt() always stops at -1, below that is a bug */
374 assert(http->range_iter.debt() >= -1);
375}
376
378static bool
380{
382
383 /* check for parsing failure */
384 if (!spec.valid)
385 return false;
386
387 /* got an ETag? */
388 if (spec.tag.str) {
389 ETag rep_tag = rep->header.getETag(Http::HdrType::ETAG);
390 debugs(33, 3, "ETags: " << spec.tag.str << " and " <<
391 (rep_tag.str ? rep_tag.str : "<none>"));
392
393 if (!rep_tag.str)
394 return false; // entity has no etag to compare with!
395
396 if (spec.tag.weak || rep_tag.weak) {
397 debugs(33, DBG_IMPORTANT, "Weak ETags are not allowed in If-Range: " <<
398 spec.tag.str << " ? " << rep_tag.str);
399 return false; // must use strong validator for sub-range requests
400 }
401
402 return etagIsStrongEqual(rep_tag, spec.tag);
403 }
404
405 /* got modification time? */
406 if (spec.time >= 0)
407 return !http->storeEntry()->modifiedSince(spec.time);
408
409 assert(0); /* should not happen */
410 return false;
411}
412
413// seems to be something better suited to Server logic
415void
417{
418 HttpHeader *hdr = rep ? &rep->header : nullptr;
419 const char *range_err = nullptr;
420 HttpRequest *request = http->request;
421 assert(request->range);
422 /* check if we still want to do ranges */
423 int64_t roffLimit = request->getRangeOffsetLimit();
424 auto contentRange = rep ? rep->contentRange() : nullptr;
425
426 if (!rep)
427 range_err = "no [parse-able] reply";
428 else if ((rep->sline.status() != Http::scOkay) && (rep->sline.status() != Http::scPartialContent))
429 range_err = "wrong status code";
430 else if (rep->sline.status() == Http::scPartialContent)
431 range_err = "too complex response"; // probably contains what the client needs
432 else if (rep->sline.status() != Http::scOkay)
433 range_err = "wrong status code";
434 else if (hdr->has(Http::HdrType::CONTENT_RANGE)) {
435 Must(!contentRange); // this is a 200, not 206 response
436 range_err = "meaningless response"; // the status code or the header is wrong
437 }
438 else if (rep->content_length < 0)
439 range_err = "unknown length";
440 else if (rep->content_length != http->storeEntry()->mem().baseReply().content_length)
441 range_err = "INCONSISTENT length"; /* a bug? */
442
443 /* hits only - upstream CachePeer determines correct behaviour on misses,
444 * and client_side_reply determines hits candidates
445 */
446 else if (http->loggingTags().isTcpHit() &&
447 http->request->header.has(Http::HdrType::IF_RANGE) &&
448 !clientIfRangeMatch(http, rep))
449 range_err = "If-Range match failed";
450
451 else if (!http->request->range->canonize(rep))
452 range_err = "canonization failed";
453 else if (http->request->range->isComplex())
454 range_err = "too complex range header";
455 else if (!http->loggingTags().isTcpHit() && http->request->range->offsetLimitExceeded(roffLimit))
456 range_err = "range outside range_offset_limit";
457
458 /* get rid of our range specs on error */
459 if (range_err) {
460 /* XXX We do this here because we need canonisation etc. However, this current
461 * code will lead to incorrect store offset requests - the store will have the
462 * offset data, but we won't be requesting it.
463 * So, we can either re-request, or generate an error
464 */
465 http->request->ignoreRange(range_err);
466 } else {
467 /* XXX: TODO: Review, this unconditional set may be wrong. */
469
470 // before range_iter accesses
471 const auto actual_clen = http->prepPartialResponseGeneration();
472
473 const int spec_count = http->request->range->specs.size();
474
475 debugs(33, 3, "range spec count: " << spec_count <<
476 " virgin clen: " << rep->content_length);
477 assert(spec_count > 0);
478 /* append appropriate header(s) */
479 if (spec_count == 1) {
480 const auto singleSpec = *http->request->range->begin();
481 assert(singleSpec);
482 httpHeaderAddContRange(hdr, *singleSpec, rep->content_length);
483 } else {
484 /* multipart! */
485 /* delete old Content-Type, add ours */
488 "multipart/byteranges; boundary=\"" SQUIDSTRINGPH "\"",
489 SQUIDSTRINGPRINT(http->range_iter.boundary));
490 }
491
492 /* replace Content-Length header */
493 assert(actual_clen >= 0);
495 hdr->putInt64(Http::HdrType::CONTENT_LENGTH, actual_clen);
496 debugs(33, 3, "actual content length: " << actual_clen);
497 }
498}
499
502{
503 if (http->client_stream.tail)
504 return static_cast<clientStreamNode *>(http->client_stream.tail->data);
505
506 return nullptr;
507}
508
511{
512 return static_cast<clientStreamNode *>(http->client_stream.tail->prev->data);
513}
514
517{
518 assert(http && http->getConn());
519 return http->getConn();
520}
521
523void
525{
526 if (http) {
527 http->updateError(error);
528 http->al->cache.code.err.update(lte);
529 }
530}
531
532void
534{
535 CodeContext::Reset(clientConnection);
536 ConnStateData *conn = getConn();
537
538 /* we can't handle any more stream data - detach */
539 clientStreamDetach(getTail(), http);
540
541 assert(connRegistered_);
542 connRegistered_ = false;
543 conn->pipeline.popMe(Http::StreamPointer(this));
544}
545
547void
549{
550 debugs(33, 4, clientConnection << " because " << reason);
551 getConn()->stopSending(reason); // closes ASAP
552}
553
554void
556{
557 debugs(33, 2, "Deferring request " << http->uri);
558 assert(flags.deferred == 0);
559 flags.deferred = 1;
560 deferredparams.node = node;
561 deferredparams.rep = rep;
562 deferredparams.queuedBuffer = receivedData;
563}
564
565void
567{
568 reply = rep;
569 if (http->request->range)
570 buildRangeHeader(rep);
571}
572
577void
579{
580 const uint64_t length =
581 static_cast<uint64_t>(lengthToSend(bodyData.range()));
582 noteSentBodyBytes(length);
583
584 mb.appendf("%" PRIX64 "\r\n", length);
585 mb.append(bodyData.data, length);
586 mb.append("\r\n", 2);
587}
588
593void
595{
596 HttpHdrRangeIter * i = &http->range_iter;
597 Range<int64_t> available(source.range());
598 char const *buf = source.data;
599
600 while (i->currentSpec() && available.size()) {
601 const size_t copy_sz = lengthToSend(available);
602 if (copy_sz) {
603 // intersection of "have" and "need" ranges must not be empty
604 assert(http->out.offset < i->currentSpec()->offset + i->currentSpec()->length);
605 assert(http->out.offset + (int64_t)available.size() > i->currentSpec()->offset);
606
607 /*
608 * put boundary and headers at the beginning of a range in a
609 * multi-range
610 */
611 if (http->multipartRangeRequest() && i->debt() == i->currentSpec()->length) {
613 &http->storeEntry()->mem().freshestReply(),
614 i->currentSpec(), /* current range */
615 i->boundary, /* boundary, the same for all */
616 mb);
617 }
618
619 // append content
620 debugs(33, 3, "appending " << copy_sz << " bytes");
621 noteSentBodyBytes(copy_sz);
622 mb->append(buf, copy_sz);
623
624 // update offsets
625 available.start += copy_sz;
626 buf += copy_sz;
627 }
628
629 if (!canPackMoreRanges()) {
630 debugs(33, 3, "Returning because !canPackMoreRanges.");
631 if (i->debt() == 0)
632 // put terminating boundary for multiparts
634 return;
635 }
636
637 int64_t nextOffset = getNextRangeOffset();
638 assert(nextOffset >= http->out.offset);
639 int64_t skip = nextOffset - http->out.offset;
640 /* adjust for not to be transmitted bytes */
641 http->out.offset = nextOffset;
642
643 if (available.size() <= (uint64_t)skip)
644 return;
645
646 available.start += skip;
647 buf += skip;
648
649 if (copy_sz == 0)
650 return;
651 }
652}
653
654void
656{
657 clientConnection->close();
658}
659
bool etagIsStrongEqual(const ETag &tag1, const ETag &tag2)
whether etags are strong-equal
Definition: ETag.cc:49
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
int size
Definition: ModDevPoll.cc:75
#define SQUIDSTRINGPH
Definition: SquidString.h:21
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
static bool clientIfRangeMatch(ClientHttpRequest *http, HttpReply *rep)
Definition: Stream.cc:379
#define Must(condition)
Definition: TextException.h:75
int conn
the current server connection FD
Definition: Transport.cc:26
void error(char *format,...)
#define assert(EX)
Definition: assert.h:17
HttpRequest *const request
StoreEntry * storeEntry() const
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:77
void kick()
try to make progress on a transaction or read more I/O
Definition: client_side.cc:918
Definition: ETag.h:18
int weak
true if it is a weak validator
Definition: ETag.h:21
const char * str
quoted-string
Definition: ETag.h:20
a transaction problem
Definition: Error.h:27
const HttpHdrRangeSpec * currentSpec() const
int64_t debt() const
static int64_t const UnknownPosition
TimeOrTag getTimeOrTag(Http::HdrType id) const
Definition: HttpHeader.cc:1331
ETag getETag(Http::HdrType id) const
Definition: HttpHeader.cc:1318
int delById(Http::HdrType id)
Definition: HttpHeader.cc:667
void putInt64(Http::HdrType id, int64_t number)
Definition: HttpHeader.cc:978
int has(Http::HdrType id) const
Definition: HttpHeader.cc:938
Http::StatusLine sline
Definition: HttpReply.h:56
MemBuf * pack() const
Definition: HttpReply.cc:112
const HttpHdrContRange * contentRange() const
Definition: HttpReply.cc:345
HttpHdrRange * range
Definition: HttpRequest.h:143
int64_t getRangeOffsetLimit()
Definition: HttpRequest.cc:594
HttpHeader header
Definition: Message.h:74
int64_t content_length
Definition: Message.h:83
AnyP::ProtocolVersion version
breakdown of protocol version label: (HTTP/ICY) and (0.9/1.0/1.1)
Definition: StatusLine.h:65
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
clientStreamNode * node
Definition: Stream.h:154
void sendStartOfMessage(HttpReply *, StoreIOBuffer bodyData)
send an HTTP reply message headers and maybe some initial payload
Definition: Stream.cc:266
struct Http::Stream::@74 flags
void deferRecipientForLater(clientStreamNode *, HttpReply *, StoreIOBuffer receivedData)
Definition: Stream.cc:555
DeferredParams deferredparams
Definition: Stream.h:159
size_t lengthToSend(Range< int64_t > const &available) const
Definition: Stream.cc:337
bool canPackMoreRanges() const
Definition: Stream.cc:188
~Stream() override
Definition: Stream.cc:39
void registerWithConn()
register this stream with the Server
Definition: Stream.cc:52
void packChunk(const StoreIOBuffer &bodyData, MemBuf &)
Definition: Stream.cc:578
ConnStateData * getConn() const
Definition: Stream.cc:516
clientStream_status_t socketState()
Adapt stream status to account for Range cases.
Definition: Stream.cc:210
void sendBody(StoreIOBuffer bodyData)
send some HTTP reply message payload
Definition: Stream.cc:314
void buildRangeHeader(HttpReply *)
add Range headers (if any) to the given HTTP reply message
Definition: Stream.cc:416
char reqbuf[HTTP_REQBUF_SZ]
Definition: Stream.h:137
clientStreamNode * getClientReplyContext() const
Definition: Stream.cc:510
void packRange(StoreIOBuffer const &, MemBuf *)
Definition: Stream.cc:594
void initiateClose(const char *reason)
terminate due to a send/write error (may continue reading)
Definition: Stream.cc:548
void doClose()
Definition: Stream.cc:655
int64_t getNextRangeOffset() const
Definition: Stream.cc:131
bool multipartRangeRequest() const
Definition: Stream.cc:125
bool startOfOutput() const
whether the reply has started being sent
Definition: Stream.cc:61
void noteSentBodyBytes(size_t)
Definition: Stream.cc:360
void finished()
cleanup when the transaction has finished. may destroy 'this'
Definition: Stream.cc:533
void writeComplete(size_t size)
update stream state after a write, may initiate more I/O
Definition: Stream.cc:67
void prepareReply(HttpReply *)
Definition: Stream.cc:566
clientStreamNode * getTail() const
Definition: Stream.cc:501
Stream(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq)
construct with HTTP/1.x details
Definition: Stream.cc:23
void noteIoError(const Error &, const LogTagsErrors &)
update state to reflect I/O error
Definition: Stream.cc:524
void pullData()
get more data to send
Definition: Stream.cc:109
ClientHttpRequest * http
Definition: Stream.h:135
Definition: MemBuf.h:24
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
char * buf
Definition: MemBuf.h:134
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
static MessageDelayPools * Instance()
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
Definition: Range.h:19
C start
Definition: Range.h:24
S size() const
Definition: Range.h:61
bool modifiedSince(const time_t ims, const int imslen=-1) const
Definition: store.cc:1822
int64_t objectLen() const
Definition: Store.h:257
int64_t offset
Definition: StoreIOBuffer.h:58
Range< int64_t > range() const
Definition: StoreIOBuffer.h:42
ETag tag
Definition: TimeOrTag.h:20
time_t time
Definition: TimeOrTag.h:21
int valid
Definition: TimeOrTag.h:22
void httpRequestFree(void *data)
Definition: client_side.cc:499
void clientPackTermBound(String boundary, MemBuf *mb)
put terminating boundary for multiparts to the buffer
Definition: client_side.cc:726
ACLFilledChecklist * clientAclChecklistCreate(const acl_access *acl, ClientHttpRequest *http)
void clientPackRangeHdr(const HttpReplyPointer &rep, const HttpHdrRangeSpec *spec, String boundary, MemBuf *mb)
append a "part" HTTP header (as in a multi-part/range reply) to the buffer
Definition: client_side.cc:733
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
clientStream_status_t
Definition: enums.h:125
@ STREAM_COMPLETE
Definition: enums.h:127
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:132
@ STREAM_NONE
Definition: enums.h:126
@ STREAM_FAILED
Definition: enums.h:137
void fatal(const char *message)
Definition: fatal.cc:28
#define fd_table
Definition: fde.h:189
clientStream_status_t clientStreamStatus(clientStreamNode *thisObject, ClientHttpRequest *http)
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
#define HTTP_REQBUF_SZ
Definition: forward.h:14
@ scOkay
Definition: StatusCode.h:26
@ scPartialContent
Definition: StatusCode.h:32
@ CONTENT_LENGTH
Definition: parse.c:104
#define PRIX64
Definition: types.h:124

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors