2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
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.
10 #include "client_side.h"
11 #include "client_side_request.h"
12 #include "client_side_reply.h"
13 #include "ClientRequestContext.h"
14 #include "Downloader.h"
15 #include "http/one/RequestParser.h"
16 #include "http/Stream.h"
18 CBDATA_CLASS_INIT(Downloader
);
20 /// Used to hold and pass the required info and buffers to the
21 /// clientStream callbacks
22 class DownloaderContext
: public RefCountable
24 MEMPROXY_CLASS(DownloaderContext
);
27 typedef RefCount
<DownloaderContext
> Pointer
;
29 DownloaderContext(Downloader
*dl
, ClientHttpRequest
*h
);
33 CbcPointer
<Downloader
> downloader
;
34 ClientHttpRequest
*http
;
35 char requestBuffer
[HTTP_REQBUF_SZ
];
38 DownloaderContext::DownloaderContext(Downloader
*dl
, ClientHttpRequest
*h
):
42 debugs(33, 6, "DownloaderContext constructed, this=" << (void*)this);
45 DownloaderContext::~DownloaderContext()
47 debugs(33, 6, "DownloaderContext destructed, this=" << (void*)this);
53 DownloaderContext::finished()
59 Downloader::Downloader(SBuf
&url
, AsyncCall::Pointer
&aCallback
, unsigned int level
):
60 AsyncJob("Downloader"),
67 Downloader::~Downloader()
72 Downloader::doneAll() const
74 return (!callback_
|| callback_
->canceled()) && AsyncJob::doneAll();
78 downloaderRecipient(clientStreamNode
* node
, ClientHttpRequest
* http
,
79 HttpReply
* rep
, StoreIOBuffer receivedData
)
81 debugs(33, 6, MYNAME
);
82 /* Test preconditions */
85 /* TODO: handle this rather than asserting
86 * - it should only ever happen if we cause an abort and
87 * the callback chain loops back to here, so we can simply return.
88 * However, that itself shouldn't happen, so it stays as an assert for now.
90 assert(cbdataReferenceValid(node
));
91 assert(!node
->node
.next
);
92 DownloaderContext::Pointer context
= dynamic_cast<DownloaderContext
*>(node
->data
.getRaw());
95 if (context
->downloader
.valid())
96 context
->downloader
->handleReply(node
, http
, rep
, receivedData
);
100 downloaderDetach(clientStreamNode
* node
, ClientHttpRequest
* http
)
102 debugs(33, 5, MYNAME
);
103 clientStreamDetach(node
, http
);
106 /// Initializes and starts the HTTP GET request to the remote server
108 Downloader::buildRequest()
110 const HttpRequestMethod method
= Http::METHOD_GET
;
112 char *uri
= xstrdup(url_
.c_str());
113 HttpRequest
*const request
= HttpRequest::CreateFromUrl(uri
, method
);
115 debugs(33, 5, "Invalid URI: " << url_
);
117 return false; //earlyError(...)
119 request
->http_ver
= Http::ProtocolVersion();
120 request
->header
.putStr(Http::HdrType::HOST
, request
->url
.host());
121 request
->header
.putTime(Http::HdrType::DATE
, squid_curtime
);
122 request
->flags
.internalClient
= true;
123 request
->client_addr
.setNoAddr();
124 #if FOLLOW_X_FORWARDED_FOR
125 request
->indirect_client_addr
.setNoAddr();
126 #endif /* FOLLOW_X_FORWARDED_FOR */
127 request
->my_addr
.setNoAddr(); /* undefined for internal requests */
128 request
->my_addr
.port(0);
129 request
->downloader
= this;
131 debugs(11, 2, "HTTP Client Downloader " << this << "/" << id
);
132 debugs(11, 2, "HTTP Client REQUEST:\n---------\n" <<
133 request
->method
<< " " << url_
<< " " << request
->http_ver
<< "\n" <<
136 ClientHttpRequest
*const http
= new ClientHttpRequest(nullptr);
137 http
->request
= request
;
138 HTTPMSGLOCK(http
->request
);
142 context_
= new DownloaderContext(this, http
);
143 StoreIOBuffer tempBuffer
;
144 tempBuffer
.data
= context_
->requestBuffer
;
145 tempBuffer
.length
= HTTP_REQBUF_SZ
;
147 ClientStreamData newServer
= new clientReplyContext(http
);
148 ClientStreamData newClient
= context_
.getRaw();
149 clientStreamInit(&http
->client_stream
, clientGetMoreData
, clientReplyDetach
,
150 clientReplyStatus
, newServer
, downloaderRecipient
,
151 downloaderDetach
, newClient
, tempBuffer
);
153 // Build a ClientRequestContext to start doCallouts
154 http
->calloutContext
= new ClientRequestContext(http
);
163 callBack(Http::scInternalServerError
);
167 Downloader::handleReply(clientStreamNode
* node
, ClientHttpRequest
*http
, HttpReply
*reply
, StoreIOBuffer receivedData
)
169 DownloaderContext::Pointer callerContext
= dynamic_cast<DownloaderContext
*>(node
->data
.getRaw());
170 // TODO: remove the following check:
171 assert(callerContext
== context_
);
173 debugs(33, 4, "Received " << receivedData
.length
<<
174 " object data, offset: " << receivedData
.offset
<<
175 " error flag:" << receivedData
.flags
.error
);
177 const bool failed
= receivedData
.flags
.error
;
179 callBack(Http::scInternalServerError
);
183 const int64_t existingContent
= reply
? reply
->content_length
: 0;
184 const size_t maxSize
= MaxObjectSize
> SBuf::maxSize
? SBuf::maxSize
: MaxObjectSize
;
185 const bool tooLarge
= (existingContent
> -1 && existingContent
> static_cast<int64_t>(maxSize
)) ||
186 (maxSize
< object_
.length()) ||
187 ((maxSize
- object_
.length()) < receivedData
.length
);
190 callBack(Http::scInternalServerError
);
194 if (receivedData
.length
) {
195 object_
.append(receivedData
.data
, receivedData
.length
);
196 http
->out
.size
+= receivedData
.length
;
197 http
->out
.offset
+= receivedData
.length
;
200 switch (clientStreamStatus(node
, http
)) {
202 debugs(33, 3, "Get more data");
203 StoreIOBuffer tempBuffer
;
204 tempBuffer
.offset
= http
->out
.offset
;
205 tempBuffer
.data
= context_
->requestBuffer
;
206 tempBuffer
.length
= HTTP_REQBUF_SZ
;
207 clientStreamRead(node
, http
, tempBuffer
);
210 case STREAM_COMPLETE
:
211 debugs(33, 3, "Object data transfer successfully complete");
212 callBack(Http::scOkay
);
214 case STREAM_UNPLANNED_COMPLETE
:
215 debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE");
216 callBack(Http::scInternalServerError
);
219 debugs(33, 3, "Object data transfer failed: STREAM_FAILED");
220 callBack(Http::scInternalServerError
);
223 fatal("unreachable code");
228 Downloader::downloadFinished()
231 // We cannot delay http destruction until refcounting deletes
232 // DownloaderContext. The http object destruction will cause
233 // clientStream cleanup and will release the refcount to context_
234 // object hold by clientStream structures.
235 context_
->finished();
240 /// Schedules for execution the "callback" with parameters the status
243 Downloader::callBack(Http::StatusCode
const statusCode
)
245 CbDialer
*dialer
= dynamic_cast<CbDialer
*>(callback_
->getDialer());
247 dialer
->status
= statusCode
;
248 if (statusCode
== Http::scOkay
)
249 dialer
->object
= object_
;
250 ScheduleCallHere(callback_
);
253 // Calling deleteThis method here to finish Downloader
254 // may result to squid crash.
255 // This method called by handleReply method which maybe called
256 // by ClientHttpRequest::doCallouts. The doCallouts after this object
257 // deleted, may operate on non valid objects.
258 // Schedule an async call here just to force squid to delete this object.
259 CallJobHere(33, 7, CbcPointer
<Downloader
>(this), Downloader
, downloadFinished
);