Downloader.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 "base/Raw.h"
11 #include "client_side.h"
12 #include "client_side_reply.h"
13 #include "client_side_request.h"
14 #include "ClientRequestContext.h"
15 #include "clientStream.h"
16 #include "Downloader.h"
17 #include "fatal.h"
18 #include "http/one/RequestParser.h"
19 #include "http/Stream.h"
20 
22 
26 {
28 
29 public:
31 
33  ~DownloaderContext() override;
34  void finished();
35 
39 };
40 
42  downloader(dl),
43  http(h)
44 {
45  debugs(33, 6, "DownloaderContext constructed, this=" << (void*)this);
46 }
47 
49 {
50  debugs(33, 6, "DownloaderContext destructed, this=" << (void*)this);
51  if (http)
52  finished();
53 }
54 
55 void
57 {
58  delete http;
59  http = nullptr;
60 }
61 
62 std::ostream &
63 operator <<(std::ostream &os, const DownloaderAnswer &answer)
64 {
65  os << "outcome=" << answer.outcome;
66  if (answer.outcome == Http::scOkay)
67  os << ", resource.size=" << answer.resource.length();
68  return os;
69 }
70 
71 Downloader::Downloader(const SBuf &url, const AsyncCallback<Answer> &cb, const MasterXactionPointer &mx, const unsigned int level):
72  AsyncJob("Downloader"),
73  url_(url),
74  callback_(cb),
75  level_(level),
76  masterXaction_(mx)
77 {
78 }
79 
81 {
82  debugs(33, 6, this);
83 }
84 
85 void
87 {
88  debugs(33, 6, this);
89 
90  if (callback_) // job-ending emergencies like handleStopRequest() or callException()
92 
93  if (context_) {
94  context_->finished();
95  context_ = nullptr;
96  }
97 }
98 
99 bool
101 {
102  return (!callback_ || callback_->canceled()) && AsyncJob::doneAll();
103 }
104 
105 static void
107  HttpReply * rep, StoreIOBuffer receivedData)
108 {
109  debugs(33, 6, MYNAME);
110  /* Test preconditions */
111  assert(node);
112 
113  /* TODO: handle this rather than asserting
114  * - it should only ever happen if we cause an abort and
115  * the callback chain loops back to here, so we can simply return.
116  * However, that itself shouldn't happen, so it stays as an assert for now.
117  */
119  assert(!node->node.next);
120  DownloaderContext::Pointer context = dynamic_cast<DownloaderContext *>(node->data.getRaw());
121  assert(context);
122 
123  if (context->downloader.valid())
124  context->downloader->handleReply(node, http, rep, receivedData);
125 }
126 
127 static void
129 {
130  debugs(33, 5, MYNAME);
131  clientStreamDetach(node, http);
132 }
133 
135 bool
137 {
138  const HttpRequestMethod method = Http::METHOD_GET;
139 
140  const auto request = HttpRequest::FromUrl(url_, masterXaction_, method);
141  if (!request) {
142  debugs(33, 5, "Invalid URI: " << url_);
143  return false; //earlyError(...)
144  }
145  request->http_ver = Http::ProtocolVersion();
146  request->header.putStr(Http::HdrType::HOST, request->url.host());
147  request->header.putTime(Http::HdrType::DATE, squid_curtime);
148  request->client_addr.setNoAddr();
149 #if FOLLOW_X_FORWARDED_FOR
150  request->indirect_client_addr.setNoAddr();
151 #endif /* FOLLOW_X_FORWARDED_FOR */
152  request->my_addr.setNoAddr(); /* undefined for internal requests */
153  request->my_addr.port(0);
154  request->downloader = this;
155 
156  debugs(11, 2, "HTTP Client Downloader " << this << "/" << id);
157  debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
158  request->method << " " << url_ << " " << request->http_ver << "\n" <<
159  "\n----------");
160 
161  ClientHttpRequest *const http = new ClientHttpRequest(nullptr);
162  http->initRequest(request);
163  http->req_sz = 0;
164  // XXX: performance regression. c_str() reallocates
165  http->uri = xstrdup(url_.c_str());
166 
167  context_ = new DownloaderContext(this, http);
168  StoreIOBuffer tempBuffer;
169  tempBuffer.data = context_->requestBuffer;
170  tempBuffer.length = HTTP_REQBUF_SZ;
171 
172  ClientStreamData newServer = new clientReplyContext(http);
173  ClientStreamData newClient = context_.getRaw();
176  downloaderDetach, newClient, tempBuffer);
177 
178  // Build a ClientRequestContext to start doCallouts
179  http->calloutContext = new ClientRequestContext(http);
180  http->doCallouts();
181  return true;
182 }
183 
184 void
186 {
187  if (!buildRequest())
189 }
190 
191 void
193 {
194  DownloaderContext::Pointer callerContext = dynamic_cast<DownloaderContext *>(node->data.getRaw());
195  // TODO: remove the following check:
196  assert(callerContext == context_);
197 
198  debugs(33, 4, "Received " << receivedData.length <<
199  " object data, offset: " << receivedData.offset <<
200  " error flag:" << receivedData.flags.error);
201 
202  const bool failed = receivedData.flags.error;
203  if (failed) {
205  return;
206  }
207 
208  const int64_t existingContent = reply ? reply->content_length : 0;
209  const size_t maxSize = MaxObjectSize > SBuf::maxSize ? SBuf::maxSize : MaxObjectSize;
210  const bool tooLarge = (existingContent > -1 && existingContent > static_cast<int64_t>(maxSize)) ||
211  (maxSize < object_.length()) ||
212  ((maxSize - object_.length()) < receivedData.length);
213 
214  if (tooLarge) {
216  return;
217  }
218 
219  object_.append(receivedData.data, receivedData.length);
220  http->out.size += receivedData.length;
221  http->out.offset += receivedData.length;
222 
223  switch (clientStreamStatus(node, http)) {
224  case STREAM_NONE: {
225  debugs(33, 3, "Get more data");
226  StoreIOBuffer tempBuffer;
227  tempBuffer.offset = http->out.offset;
228  tempBuffer.data = context_->requestBuffer;
229  tempBuffer.length = HTTP_REQBUF_SZ;
230  clientStreamRead(node, http, tempBuffer);
231  }
232  break;
233  case STREAM_COMPLETE:
234  debugs(33, 3, "Object data transfer successfully complete");
236  break;
238  debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
240  break;
241  case STREAM_FAILED:
242  debugs(33, 3, "Object data transfer failed: STREAM_FAILED");
244  break;
245  default:
246  fatal("unreachable code");
247  }
248 }
249 
250 void
252 {
253  debugs(33, 7, this);
254  Must(done());
255 }
256 
259 void
261 {
262  assert(callback_);
263  auto &answer = callback_.answer();
264  answer.outcome = statusCode;
265  if (statusCode == Http::scOkay)
266  answer.resource = object_;
267  ScheduleCallHere(callback_.release());
268 
269  // We cannot deleteThis() because we may be called synchronously from
270  // doCallouts() via handleReply() (XXX), and doCallouts() may crash if we
271  // disappear. Instead, schedule an async call now so that later, when the
272  // call firing code discovers a done() job, it deletes us.
274 }
275 
void fatal(const char *message)
Definition: fatal.cc:28
Definition: parse.c:104
static HttpRequest * FromUrl(const SBuf &url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:517
clientStream_status_t clientStreamStatus(clientStreamNode *thisObject, ClientHttpRequest *http)
struct StoreIOBuffer::@130 flags
void initRequest(HttpRequest *)
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
struct node * next
Definition: parse.c:105
@ STREAM_NONE
Definition: enums.h:121
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
Downloader(const SBuf &url, const AsyncCallback< Answer > &, const MasterXactionPointer &, unsigned int level=0)
Definition: Downloader.cc:71
CSS clientReplyStatus
Definition: client_side.h:530
void start() override
called by AsyncStart; do not call directly
Definition: Downloader.cc:185
MasterXactionPointer masterXaction_
download transaction context
Definition: Downloader.h:84
int64_t offset
Definition: StoreIOBuffer.h:58
ClientHttpRequest * http
Definition: Downloader.cc:37
Definition: SBuf.h:93
a smart AsyncCall pointer for delivery of future results
#define xstrdup
void swanSong() override
Definition: Downloader.cc:86
struct ClientHttpRequest::Out out
C * getRaw() const
Definition: RefCount.h:89
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:270
@ STREAM_COMPLETE
Definition: enums.h:122
StatusCode
Definition: StatusCode.h:20
static const size_type maxSize
Maximum size of a SBuf. By design it MUST be < MAX(size_type)/2. Currently 256Mb.
Definition: SBuf.h:103
@ STREAM_FAILED
Definition: enums.h:132
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
char requestBuffer[HTTP_REQBUF_SZ]
Definition: Downloader.cc:38
void downloadFinished()
delays destruction to protect doCallouts()
Definition: Downloader.cc:251
Http::StatusCode outcome
Definition: Downloader.h:36
CbcPointer< Downloader > downloader
Definition: Downloader.cc:36
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:112
AsyncCallback< Answer > callback_
answer destination
Definition: Downloader.h:80
unsigned error
Definition: StoreIOBuffer.h:55
@ STREAM_UNPLANNED_COMPLETE
Definition: enums.h:127
CSR clientGetMoreData
Definition: client_side.h:529
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:59
#define assert(EX)
Definition: assert.h:17
SBuf url_
the url to download
Definition: Downloader.h:77
static void downloaderDetach(clientStreamNode *node, ClientHttpRequest *http)
Definition: Downloader.cc:128
@ scInternalServerError
Definition: StatusCode.h:73
const char * c_str()
Definition: SBuf.cc:516
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
void handleReply(clientStreamNode *, ClientHttpRequest *, HttpReply *, StoreIOBuffer)
Definition: Downloader.cc:192
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
time_t squid_curtime
Definition: stub_libtime.cc:20
uint64_t size
Response header and body bytes written to the client connection.
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
bool done() const
the job is destroyed in callEnd() when done()
Definition: AsyncJob.cc:106
int64_t content_length
Definition: Message.h:83
std::ostream & operator<<(std::ostream &os, const DownloaderAnswer &answer)
Definition: Downloader.cc:63
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, const ClientStreamData &readdata, CSCB *callback, CSD *cdetach, const ClientStreamData &callbackdata, StoreIOBuffer tailBuffer)
void callBack(Http::StatusCode const status)
Definition: Downloader.cc:260
static void downloaderRecipient(clientStreamNode *node, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
Definition: Downloader.cc:106
~DownloaderContext() override
Definition: Downloader.cc:48
~Downloader() override
Definition: Downloader.cc:80
bool buildRequest()
Initializes and starts the HTTP GET request to the remote server.
Definition: Downloader.cc:136
#define Must(condition)
Definition: TextException.h:75
size_t req_sz
raw request size on input, not current request size
DownloaderContextPointer context_
Pointer to an object that stores the clientStream required info.
Definition: Downloader.h:87
#define MYNAME
Definition: Stream.h:219
MEMPROXY_CLASS(DownloaderContext)
static const size_t MaxObjectSize
The maximum allowed object size.
Definition: Downloader.h:75
#define HTTP_REQBUF_SZ
Definition: forward.h:14
ClientRequestContext * calloutContext
bool doneAll() const override
whether positive goal has been reached
Definition: Downloader.cc:100
@ scOkay
Definition: StatusCode.h:27
@ METHOD_GET
Definition: MethodType.h:25
DownloaderContext(Downloader *dl, ClientHttpRequest *h)
Definition: Downloader.cc:41
CSD clientReplyDetach
Definition: client_side.h:531
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
RefCount< DownloaderContext > Pointer
Definition: Downloader.cc:30
download result
Definition: Downloader.h:28
SBuf object_
the object body data
Definition: Downloader.h:82
AnyP::ProtocolVersion ProtocolVersion(unsigned int aMajor, unsigned int aMinor)
HTTP version label information.

 

Introduction

Documentation

Support

Miscellaneous