]> git.ipfire.org Git - thirdparty/squid.git/blame - src/Downloader.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / Downloader.cc
CommitLineData
4b5ea8a6 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
4b5ea8a6
CT
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
55369ae6
AR
9#include "squid.h"
10#include "client_side.h"
55369ae6 11#include "client_side_reply.h"
3945c91d 12#include "client_side_request.h"
cda7024f 13#include "ClientRequestContext.h"
55369ae6 14#include "Downloader.h"
e6834b06 15#include "fatal.h"
55369ae6 16#include "http/one/RequestParser.h"
54fb1cbf 17#include "http/Stream.h"
55369ae6 18
4b5ea8a6
CT
19CBDATA_CLASS_INIT(Downloader);
20
a175ef71
CT
21/// Used to hold and pass the required info and buffers to the
22/// clientStream callbacks
23class DownloaderContext: public RefCountable
24{
4b5ea8a6 25 MEMPROXY_CLASS(DownloaderContext);
a175ef71
CT
26
27public:
28 typedef RefCount<DownloaderContext> Pointer;
29
4b5ea8a6 30 DownloaderContext(Downloader *dl, ClientHttpRequest *h);
a175ef71
CT
31 ~DownloaderContext();
32 void finished();
4b5ea8a6
CT
33
34 CbcPointer<Downloader> downloader;
a175ef71
CT
35 ClientHttpRequest *http;
36 char requestBuffer[HTTP_REQBUF_SZ];
37};
38
4b5ea8a6
CT
39DownloaderContext::DownloaderContext(Downloader *dl, ClientHttpRequest *h):
40 downloader(dl),
41 http(h)
42{
43 debugs(33, 6, "DownloaderContext constructed, this=" << (void*)this);
44}
55369ae6 45
cda7024f
CT
46DownloaderContext::~DownloaderContext()
47{
4b5ea8a6 48 debugs(33, 6, "DownloaderContext destructed, this=" << (void*)this);
cda7024f
CT
49 if (http)
50 finished();
51}
52
53void
54DownloaderContext::finished()
55{
cda7024f 56 delete http;
4b5ea8a6 57 http = nullptr;
cda7024f
CT
58}
59
4cab96c5
CT
60void
61Downloader::CbDialer::print(std::ostream &os) const
62{
63 os << " Http Status:" << status << Raw("body data", object.rawContent(), 64).hex();
cda7024f
CT
64}
65
66Downloader::Downloader(SBuf &url, AsyncCall::Pointer &aCallback, unsigned int level):
55369ae6 67 AsyncJob("Downloader"),
55369ae6 68 url_(url),
4b5ea8a6 69 callback_(aCallback),
4e526b93 70 level_(level)
55369ae6 71{
55369ae6
AR
72}
73
74Downloader::~Downloader()
75{
0860d5b6
CT
76 debugs(33, 6, this);
77}
78
79void
80Downloader::swanSong()
81{
82 debugs(33, 6, this);
83 if (context_) {
84 context_->finished();
85 context_ = nullptr;
86 }
55369ae6
AR
87}
88
55369ae6
AR
89bool
90Downloader::doneAll() const
91{
4b5ea8a6 92 return (!callback_ || callback_->canceled()) && AsyncJob::doneAll();
55369ae6
AR
93}
94
cda7024f
CT
95static void
96downloaderRecipient(clientStreamNode * node, ClientHttpRequest * http,
97 HttpReply * rep, StoreIOBuffer receivedData)
55369ae6 98{
4b5ea8a6 99 debugs(33, 6, MYNAME);
3945c91d 100 /* Test preconditions */
4b5ea8a6 101 assert(node);
cda7024f
CT
102
103 /* TODO: handle this rather than asserting
104 * - it should only ever happen if we cause an abort and
105 * the callback chain loops back to here, so we can simply return.
106 * However, that itself shouldn't happen, so it stays as an assert for now.
107 */
108 assert(cbdataReferenceValid(node));
4b5ea8a6 109 assert(!node->node.next);
cda7024f 110 DownloaderContext::Pointer context = dynamic_cast<DownloaderContext *>(node->data.getRaw());
4b5ea8a6 111 assert(context);
cda7024f 112
4b5ea8a6
CT
113 if (context->downloader.valid())
114 context->downloader->handleReply(node, http, rep, receivedData);
55369ae6
AR
115}
116
cda7024f
CT
117static void
118downloaderDetach(clientStreamNode * node, ClientHttpRequest * http)
55369ae6 119{
4b5ea8a6 120 debugs(33, 5, MYNAME);
cda7024f 121 clientStreamDetach(node, http);
55369ae6
AR
122}
123
4b5ea8a6 124/// Initializes and starts the HTTP GET request to the remote server
cda7024f
CT
125bool
126Downloader::buildRequest()
3945c91d 127{
55369ae6
AR
128 const HttpRequestMethod method = Http::METHOD_GET;
129
4b5ea8a6 130 char *uri = xstrdup(url_.c_str());
524a2eee 131 HttpRequest *const request = HttpRequest::CreateFromUrl(uri, method);
55369ae6 132 if (!request) {
4b5ea8a6
CT
133 debugs(33, 5, "Invalid URI: " << url_);
134 xfree(uri);
cda7024f 135 return false; //earlyError(...)
55369ae6
AR
136 }
137 request->http_ver = Http::ProtocolVersion();
138 request->header.putStr(Http::HdrType::HOST, request->url.host());
139 request->header.putTime(Http::HdrType::DATE, squid_curtime);
cda7024f
CT
140 request->flags.internalClient = true;
141 request->client_addr.setNoAddr();
142#if FOLLOW_X_FORWARDED_FOR
143 request->indirect_client_addr.setNoAddr();
144#endif /* FOLLOW_X_FORWARDED_FOR */
145 request->my_addr.setNoAddr(); /* undefined for internal requests */
146 request->my_addr.port(0);
147 request->downloader = this;
148
4b5ea8a6
CT
149 debugs(11, 2, "HTTP Client Downloader " << this << "/" << id);
150 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
151 request->method << " " << url_ << " " << request->http_ver << "\n" <<
152 "\n----------");
153
154 ClientHttpRequest *const http = new ClientHttpRequest(nullptr);
55369ae6
AR
155 http->request = request;
156 HTTPMSGLOCK(http->request);
157 http->req_sz = 0;
158 http->uri = uri;
4f24bf30 159 setLogUri (http, urlCanonicalClean(request));
55369ae6 160
cda7024f 161 context_ = new DownloaderContext(this, http);
55369ae6 162 StoreIOBuffer tempBuffer;
cda7024f 163 tempBuffer.data = context_->requestBuffer;
55369ae6
AR
164 tempBuffer.length = HTTP_REQBUF_SZ;
165
166 ClientStreamData newServer = new clientReplyContext(http);
cda7024f 167 ClientStreamData newClient = context_.getRaw();
55369ae6 168 clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
cda7024f
CT
169 clientReplyStatus, newServer, downloaderRecipient,
170 downloaderDetach, newClient, tempBuffer);
55369ae6 171
cda7024f
CT
172 // Build a ClientRequestContext to start doCallouts
173 http->calloutContext = new ClientRequestContext(http);
cda7024f
CT
174 http->doCallouts();
175 return true;
55369ae6
AR
176}
177
178void
cda7024f 179Downloader::start()
55369ae6 180{
4b5ea8a6
CT
181 if (!buildRequest())
182 callBack(Http::scInternalServerError);
55369ae6
AR
183}
184
185void
cda7024f 186Downloader::handleReply(clientStreamNode * node, ClientHttpRequest *http, HttpReply *reply, StoreIOBuffer receivedData)
55369ae6 187{
cda7024f 188 DownloaderContext::Pointer callerContext = dynamic_cast<DownloaderContext *>(node->data.getRaw());
4b5ea8a6 189 // TODO: remove the following check:
cda7024f
CT
190 assert(callerContext == context_);
191
4b5ea8a6
CT
192 debugs(33, 4, "Received " << receivedData.length <<
193 " object data, offset: " << receivedData.offset <<
194 " error flag:" << receivedData.flags.error);
55369ae6 195
4b5ea8a6
CT
196 const bool failed = receivedData.flags.error;
197 if (failed) {
198 callBack(Http::scInternalServerError);
55369ae6
AR
199 return;
200 }
201
4b5ea8a6
CT
202 const int64_t existingContent = reply ? reply->content_length : 0;
203 const size_t maxSize = MaxObjectSize > SBuf::maxSize ? SBuf::maxSize : MaxObjectSize;
204 const bool tooLarge = (existingContent > -1 && existingContent > static_cast<int64_t>(maxSize)) ||
205 (maxSize < object_.length()) ||
206 ((maxSize - object_.length()) < receivedData.length);
55369ae6 207
4b5ea8a6
CT
208 if (tooLarge) {
209 callBack(Http::scInternalServerError);
210 return;
55369ae6
AR
211 }
212
c31381d0
CT
213 object_.append(receivedData.data, receivedData.length);
214 http->out.size += receivedData.length;
215 http->out.offset += receivedData.length;
55369ae6 216
4b5ea8a6 217 switch (clientStreamStatus(node, http)) {
cda7024f 218 case STREAM_NONE: {
4b5ea8a6 219 debugs(33, 3, "Get more data");
cda7024f
CT
220 StoreIOBuffer tempBuffer;
221 tempBuffer.offset = http->out.offset;
222 tempBuffer.data = context_->requestBuffer;
223 tempBuffer.length = HTTP_REQBUF_SZ;
4b5ea8a6 224 clientStreamRead(node, http, tempBuffer);
cda7024f 225 }
3945c91d 226 break;
55369ae6 227 case STREAM_COMPLETE:
4b5ea8a6
CT
228 debugs(33, 3, "Object data transfer successfully complete");
229 callBack(Http::scOkay);
55369ae6
AR
230 break;
231 case STREAM_UNPLANNED_COMPLETE:
4b5ea8a6
CT
232 debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
233 callBack(Http::scInternalServerError);
55369ae6
AR
234 break;
235 case STREAM_FAILED:
4b5ea8a6
CT
236 debugs(33, 3, "Object data transfer failed: STREAM_FAILED");
237 callBack(Http::scInternalServerError);
55369ae6
AR
238 break;
239 default:
240 fatal("unreachable code");
241 }
242}
243
244void
245Downloader::downloadFinished()
246{
168d2b30
CT
247 debugs(33, 7, this);
248 Must(done());
55369ae6
AR
249}
250
4b5ea8a6
CT
251/// Schedules for execution the "callback" with parameters the status
252/// and object.
55369ae6 253void
4b5ea8a6 254Downloader::callBack(Http::StatusCode const statusCode)
55369ae6 255{
3945c91d
SM
256 CbDialer *dialer = dynamic_cast<CbDialer*>(callback_->getDialer());
257 Must(dialer);
258 dialer->status = statusCode;
259 if (statusCode == Http::scOkay)
260 dialer->object = object_;
261 ScheduleCallHere(callback_);
262 callback_ = nullptr;
263
0860d5b6
CT
264 // We cannot deleteThis() because we may be called synchronously from
265 // doCallouts() via handleReply() (XXX), and doCallouts() may crash if we
266 // disappear. Instead, schedule an async call now so that later, when the
267 // call firing code discovers a done() job, it deletes us.
3945c91d 268 CallJobHere(33, 7, CbcPointer<Downloader>(this), Downloader, downloadFinished);
55369ae6
AR
269}
270