Squid Web Cache master
Loading...
Searching...
No Matches
Downloader.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 "base/Raw.h"
11#include "client_side.h"
12#include "client_side_reply.h"
13#include "client_side_request.h"
15#include "clientStream.h"
16#include "Downloader.h"
17#include "fatal.h"
19#include "http/Stream.h"
20
22
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
55void
57{
58 delete http;
59 http = nullptr;
60}
61
62std::ostream &
63operator <<(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
71Downloader::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
85void
87{
88 debugs(33, 6, this);
89
90 if (callback_) // job-ending emergencies like handleStopRequest() or callException()
92
93 if (context_) {
95 context_ = nullptr;
96 }
97}
98
99bool
101{
102 return (!callback_ || callback_->canceled()) && AsyncJob::doneAll();
103}
104
105static 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
127static void
133
135bool
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
184void
190
191void
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
250void
252{
253 debugs(33, 7, this);
254 Must(done());
255}
256
259void
261{
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
#define ScheduleCallHere(call)
Definition AsyncCall.h:166
#define CallJobHere(debugSection, debugLevel, job, Class, method)
static void downloaderDetach(clientStreamNode *node, ClientHttpRequest *http)
std::ostream & operator<<(std::ostream &os, const DownloaderAnswer &answer)
Definition Downloader.cc:63
static void downloaderRecipient(clientStreamNode *node, ClientHttpRequest *http, HttpReply *rep, StoreIOBuffer receivedData)
#define RefCountable
The locking interface for use on Reference-Counted classes.
Definition Lock.h:66
time_t squid_curtime
#define Must(condition)
#define assert(EX)
Definition assert.h:17
int cbdataReferenceValid(const void *p)
Definition cbdata.cc:270
#define CBDATA_CLASS_INIT(type)
Definition cbdata.h:325
a smart AsyncCall pointer for delivery of future results
virtual bool doneAll() const
whether positive goal has been reached
Definition AsyncJob.cc:112
bool done() const
the job is destroyed in callEnd() when done()
Definition AsyncJob.cc:106
Cbc * valid() const
was set and is valid
Definition CbcPointer.h:41
struct ClientHttpRequest::Out out
void initRequest(HttpRequest *)
size_t req_sz
raw request size on input, not current request size
ClientRequestContext * calloutContext
download result
Definition Downloader.h:28
Http::StatusCode outcome
Definition Downloader.h:36
MEMPROXY_CLASS(DownloaderContext)
DownloaderContext(Downloader *dl, ClientHttpRequest *h)
Definition Downloader.cc:41
char requestBuffer[HTTP_REQBUF_SZ]
Definition Downloader.cc:38
~DownloaderContext() override
Definition Downloader.cc:48
ClientHttpRequest * http
Definition Downloader.cc:37
CbcPointer< Downloader > downloader
Definition Downloader.cc:36
RefCount< DownloaderContext > Pointer
Definition Downloader.cc:30
void callBack(Http::StatusCode const status)
AsyncCallback< Answer > callback_
answer destination
Definition Downloader.h:80
~Downloader() override
Definition Downloader.cc:80
void downloadFinished()
delays destruction to protect doCallouts()
bool doneAll() const override
whether positive goal has been reached
void start() override
called by AsyncStart; do not call directly
SBuf url_
the url to download
Definition Downloader.h:77
static const size_t MaxObjectSize
The maximum allowed object size.
Definition Downloader.h:75
Downloader(const SBuf &url, const AsyncCallback< Answer > &, const MasterXactionPointer &, unsigned int level=0)
Definition Downloader.cc:71
DownloaderContextPointer context_
Pointer to an object that stores the clientStream required info.
Definition Downloader.h:87
MasterXactionPointer masterXaction_
download transaction context
Definition Downloader.h:84
bool buildRequest()
Initializes and starts the HTTP GET request to the remote server.
SBuf object_
the object body data
Definition Downloader.h:82
void swanSong() override
Definition Downloader.cc:86
void handleReply(clientStreamNode *, ClientHttpRequest *, HttpReply *, StoreIOBuffer)
static HttpRequest * FromUrl(const SBuf &url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
int64_t content_length
Definition Message.h:83
C * getRaw() const
Definition RefCount.h:89
Definition SBuf.h:94
const char * c_str()
Definition SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition SBuf.h:419
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
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
struct StoreIOBuffer::@123 flags
CSS clientReplyStatus
CSD clientReplyDetach
CSR clientGetMoreData
#define MYNAME
Definition Stream.h:219
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
@ 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
clientStream_status_t clientStreamStatus(clientStreamNode *thisObject, ClientHttpRequest *http)
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
void clientStreamInit(dlink_list *list, CSR *func, CSD *rdetach, CSS *readstatus, const ClientStreamData &readdata, CSCB *callback, CSD *cdetach, const ClientStreamData &callbackdata, StoreIOBuffer tailBuffer)
void clientStreamDetach(clientStreamNode *thisObject, ClientHttpRequest *http)
#define HTTP_REQBUF_SZ
Definition forward.h:14
StatusCode
Definition StatusCode.h:20
@ scInternalServerError
Definition StatusCode.h:73
@ scOkay
Definition StatusCode.h:27
@ METHOD_GET
Definition MethodType.h:25
AnyP::ProtocolVersion ProtocolVersion()
#define xstrdup
uint64_t size
Response header and body bytes written to the client connection.
Definition parse.c:104
struct node * next
Definition parse.c:105