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