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 "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 
52 void
54 {
55  assert(!connRegistered_);
56  assert(getConn());
57  connRegistered_ = true;
58  getConn()->add(this);
59 }
60 
61 bool
63 {
64  return http->out.size == 0;
65 }
66 
67 void
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 
109 void
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 
125 bool
127 {
128  return http->multipartRangeRequest();
129 }
130 
131 int64_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 
188 bool
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 
266 void
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 
314 void
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 
337 size_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 
360 void
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 
379 static 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
416 void
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 
524 void
526 {
527  if (http) {
528  http->updateError(error);
529  http->al->cache.code.err.update(lte);
530  }
531 }
532 
533 void
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;
544  conn->pipeline.popMe(Http::StreamPointer(this));
545 }
546 
548 void
549 Http::Stream::initiateClose(const char *reason)
550 {
551  debugs(33, 4, clientConnection << " because " << reason);
552  getConn()->stopSending(reason); // closes ASAP
553 }
554 
555 void
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 
566 void
568 {
569  reply = rep;
570  if (http->request->range)
571  buildRangeHeader(rep);
572 }
573 
578 void
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 
594 void
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 
655 void
657 {
658  clientConnection->close();
659 }
660 
void fatal(const char *message)
Definition: fatal.cc:28
Definition: parse.c:104
char * buf
Definition: MemBuf.h:134
ConnStateData * getConn() const
Definition: Stream.cc:517
clientStream_status_t clientStreamStatus(clientStreamNode *thisObject, ClientHttpRequest *http)
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
Range< int64_t > range() const
Definition: StoreIOBuffer.h:42
Definition: ETag.h:17
void sendStartOfMessage(HttpReply *, StoreIOBuffer bodyData)
send an HTTP reply message headers and maybe some initial payload
Definition: Stream.cc:267
HttpHeader header
Definition: Message.h:74
void registerWithConn()
register this stream with the Server
Definition: Stream.cc:53
void buildRangeHeader(HttpReply *)
add Range headers (if any) to the given HTTP reply message
Definition: Stream.cc:417
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
void finished()
cleanup when the transaction has finished. may destroy 'this'
Definition: Stream.cc:534
@ STREAM_NONE
Definition: enums.h:121
int64_t getRangeOffsetLimit()
Definition: HttpRequest.cc:594
char reqbuf[HTTP_REQBUF_SZ]
Definition: Stream.h:137
void error(char *format,...)
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
int64_t offset
Definition: StoreIOBuffer.h:58
void httpRequestFree(void *data)
Definition: client_side.cc:475
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
clientStream_status_t socketState()
Adapt stream status to account for Range cases.
Definition: Stream.cc:211
const char * str
quoted-string
Definition: ETag.h:20
~Stream() override
Definition: Stream.cc:40
bool etagIsStrongEqual(const ETag &tag1, const ETag &tag2)
whether etags are strong-equal
Definition: ETag.cc:49
int64_t debt() const
Http::StatusLine sline
Definition: HttpReply.h:56
int64_t objectLen() const
Definition: Store.h:253
@ STREAM_COMPLETE
Definition: enums.h:122
int64_t getNextRangeOffset() const
Definition: Stream.cc:132
#define SQUIDSTRINGPH
Definition: SquidString.h:21
time_t time
Definition: TimeOrTag.h:21
bool multipartRangeRequest() const
Definition: Stream.cc:126
void httpHeaderPutStrf(HttpHeader *hdr, Http::HdrType id, const char *fmt,...)
S size() const
Definition: Range.h:61
Pipeline pipeline
set of requests waiting to be serviced
Definition: Server.h:118
void writeComplete(size_t size)
update stream state after a write, may initiate more I/O
Definition: Stream.cc:68
void popMe(const Http::StreamPointer &)
deregister the front request from the pipeline
Definition: Pipeline.cc:52
@ STREAM_FAILED
Definition: enums.h:132
@ CONTENT_LENGTH
void updateReply(const HttpReply::Pointer &)
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
a transaction problem
Definition: Error.h:27
static void Reset()
forgets the current context, setting it to nil/unknown
Definition: CodeContext.cc:77
void noteIoError(const Error &, const LogTagsErrors &)
update state to reflect I/O error
Definition: Stream.cc:525
Definition: Range.h:18
TimeOrTag getTimeOrTag(Http::HdrType id) const
Definition: HttpHeader.cc:1330
size_t lengthToSend(Range< int64_t > const &available) const
Definition: Stream.cc:338
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
bool canPackMoreRanges() const
Definition: Stream.cc:189
int size
Definition: ModDevPoll.cc:69
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
void prepareReply(HttpReply *)
Definition: Stream.cc:567
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
MemBuf * pack() const
Definition: HttpReply.cc:112
Definition: MemBuf.h:23
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:127
clientStreamNode * getClientReplyContext() const
Definition: Stream.cc:511
@ scPartialContent
Definition: StatusCode.h:33
int delById(Http::HdrType id)
Definition: HttpHeader.cc:666
void set(const AnyP::ProtocolVersion &newVersion, Http::StatusCode newStatus, const char *newReason=nullptr)
Definition: StatusLine.cc:35
Stream(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq)
construct with HTTP/1.x details
Definition: Stream.cc:24
int weak
true if it is a weak validator
Definition: ETag.h:21
const HttpHdrRangeSpec * currentSpec() const
void packChunk(const StoreIOBuffer &bodyData, MemBuf &)
Definition: Stream.cc:579
ClientHttpRequest * http
Definition: Stream.h:135
#define assert(EX)
Definition: assert.h:17
static bool clientIfRangeMatch(ClientHttpRequest *http, HttpReply *rep)
Definition: Stream.cc:380
int valid
Definition: TimeOrTag.h:22
HttpHdrRange * range
Definition: HttpRequest.h:143
clientStreamNode * node
Definition: Stream.h:154
ETag tag
Definition: TimeOrTag.h:20
static const int64_t UnknownPosition
int64_t content_length
Definition: Message.h:83
void clientAclChecklistFill(ACLFilledChecklist &checklist, ClientHttpRequest *http)
clientStreamNode * getTail() const
Definition: Stream.cc:502
ETag getETag(Http::HdrType id) const
Definition: HttpHeader.cc:1317
#define fd_table
Definition: fde.h:189
void doClose()
Definition: Stream.cc:656
AnyP::ProtocolVersion version
breakdown of protocol version label: (HTTP/ICY) and (0.9/1.0/1.1)
Definition: StatusLine.h:65
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
const HttpHdrContRange * contentRange() const
Definition: HttpReply.cc:345
int has(Http::HdrType id) const
Definition: HttpHeader.cc:937
static MessageDelayPools * Instance()
clientStream_status_t
Definition: enums.h:120
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:709
#define Must(condition)
Definition: TextException.h:75
#define DBG_IMPORTANT
Definition: Stream.h:38
bool startOfOutput() const
whether the reply has started being sent
Definition: Stream.cc:62
void pullData()
get more data to send
Definition: Stream.cc:110
struct Http::Stream::@65 flags
void deferRecipientForLater(clientStreamNode *, HttpReply *, StoreIOBuffer receivedData)
Definition: Stream.cc:556
#define HTTP_REQBUF_SZ
Definition: forward.h:14
void packRange(StoreIOBuffer const &, MemBuf *)
Definition: Stream.cc:595
#define PRIX64
Definition: types.h:124
void kick()
try to make progress on a transaction or read more I/O
Definition: client_side.cc:894
StoreEntry * storeEntry() const
@ scOkay
Definition: StatusCode.h:27
void sendBody(StoreIOBuffer bodyData)
send some HTTP reply message payload
Definition: Stream.cc:315
bool modifiedSince(const time_t ims, const int imslen=-1) const
Definition: store.cc:1836
DeferredParams deferredparams
Definition: Stream.h:159
void noteSentBodyBytes(size_t)
Definition: Stream.cc:361
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
const A & min(A const &lhs, A const &rhs)
void clientPackTermBound(String boundary, MemBuf *mb)
put terminating boundary for multiparts to the buffer
Definition: client_side.cc:702
void initiateClose(const char *reason)
terminate due to a send/write error (may continue reading)
Definition: Stream.cc:549
void putInt64(Http::HdrType id, int64_t number)
Definition: HttpHeader.cc:977
HttpRequest *const request
C start
Definition: Range.h:24

 

Introduction

Documentation

Support

Miscellaneous