]>
Commit | Line | Data |
---|---|---|
55369ae6 AR |
1 | #include "squid.h" |
2 | #include "client_side.h" | |
3 | #include "client_side_request.h" | |
4 | #include "client_side_reply.h" | |
5 | #include "Downloader.h" | |
6 | #include "http/one/RequestParser.h" | |
54fb1cbf | 7 | #include "http/Stream.h" |
55369ae6 AR |
8 | |
9 | CBDATA_CLASS_INIT(Downloader); | |
10 | ||
4e526b93 | 11 | Downloader::Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level): |
55369ae6 AR |
12 | AsyncJob("Downloader"), |
13 | ConnStateData(xact), | |
14 | url_(url), | |
15 | callback(aCallback), | |
4e526b93 CT |
16 | status(Http::scNone), |
17 | level_(level) | |
55369ae6 | 18 | { |
7b4984f7 | 19 | transferProtocol = AnyP::ProtocolVersion(AnyP::PROTO_HTTP,1,1); |
55369ae6 AR |
20 | } |
21 | ||
22 | Downloader::~Downloader() | |
23 | { | |
24 | debugs(33 , 2, "Downloader Finished"); | |
25 | } | |
26 | ||
27 | void | |
28 | Downloader::callException(const std::exception &e) | |
29 | { | |
30 | debugs(33 , 2, "Downloader caught:" << e.what()); | |
31 | AsyncJob::callException(e); | |
32 | } | |
33 | ||
34 | bool | |
35 | Downloader::doneAll() const | |
36 | { | |
37 | return (!callback || callback->canceled()) && AsyncJob::doneAll(); | |
38 | } | |
39 | ||
40 | void | |
41 | Downloader::start() | |
42 | { | |
43 | BodyProducer::start(); | |
44 | HttpControlMsgSink::start(); | |
54fb1cbf | 45 | if (Http::Stream *context = parseOneRequest()) { |
55369ae6 AR |
46 | context->registerWithConn(); |
47 | processParsedRequest(context); | |
48 | ||
49 | /**/ | |
50 | if (context->flags.deferred) { | |
d3b1bee6 | 51 | if (context != context->http->getConn()->pipeline.front().getRaw()) |
55369ae6 AR |
52 | context->deferRecipientForLater(context->deferredparams.node, context->deferredparams.rep, context->deferredparams.queuedBuffer); |
53 | else | |
54 | context->http->getConn()->handleReply(context->deferredparams.rep, context->deferredparams.queuedBuffer); | |
55 | } | |
56 | /**/ | |
57 | ||
58 | } | |
59 | ||
60 | } | |
61 | ||
62 | void | |
63 | Downloader::noteMoreBodySpaceAvailable(BodyPipe::Pointer) | |
64 | { | |
65 | // This method required only if we need to support uploading data to server | |
66 | // Currently only GET requests are supported | |
67 | assert(0); | |
68 | } | |
69 | ||
70 | void | |
71 | Downloader::noteBodyConsumerAborted(BodyPipe::Pointer) | |
72 | { | |
73 | // This method required only if we need to support uploading data to server | |
74 | // Currently only GET requests are supported | |
75 | assert(0); | |
76 | } | |
77 | ||
54fb1cbf | 78 | Http::Stream * |
55369ae6 AR |
79 | Downloader::parseOneRequest() |
80 | { | |
81 | const HttpRequestMethod method = Http::METHOD_GET; | |
82 | ||
83 | char *uri = strdup(url_.c_str()); | |
524a2eee | 84 | HttpRequest *const request = HttpRequest::CreateFromUrl(uri, method); |
55369ae6 AR |
85 | if (!request) { |
86 | debugs(33, 5, "Invalid FTP URL: " << uri); | |
87 | safe_free(uri); | |
88 | return NULL; //earlyError(...) | |
89 | } | |
90 | request->http_ver = Http::ProtocolVersion(); | |
91 | request->header.putStr(Http::HdrType::HOST, request->url.host()); | |
92 | request->header.putTime(Http::HdrType::DATE, squid_curtime); | |
93 | ||
94 | ClientHttpRequest *const http = new ClientHttpRequest(this); | |
95 | http->request = request; | |
96 | HTTPMSGLOCK(http->request); | |
97 | http->req_sz = 0; | |
98 | http->uri = uri; | |
99 | ||
54fb1cbf | 100 | Http::Stream *const context = new Http::Stream(NULL, http); |
55369ae6 AR |
101 | StoreIOBuffer tempBuffer; |
102 | tempBuffer.data = context->reqbuf; | |
103 | tempBuffer.length = HTTP_REQBUF_SZ; | |
104 | ||
105 | ClientStreamData newServer = new clientReplyContext(http); | |
106 | ClientStreamData newClient = context; | |
107 | clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach, | |
108 | clientReplyStatus, newServer, clientSocketRecipient, | |
109 | clientSocketDetach, newClient, tempBuffer); | |
110 | ||
111 | context->flags.parsed_ok = 1; | |
112 | return context; | |
113 | } | |
114 | ||
115 | void | |
54fb1cbf | 116 | Downloader::processParsedRequest(Http::Stream *context) |
55369ae6 AR |
117 | { |
118 | Must(context != NULL); | |
d3b1bee6 | 119 | Must(pipeline.nrequests == 1); |
55369ae6 AR |
120 | |
121 | ClientHttpRequest *const http = context->http; | |
122 | assert(http != NULL); | |
123 | ||
124 | debugs(33, 4, "forwarding request to server side"); | |
125 | assert(http->storeEntry() == NULL); | |
126 | clientProcessRequest(this, Http1::RequestParserPointer(), context); | |
127 | } | |
128 | ||
129 | time_t | |
130 | Downloader::idleTimeout() const | |
131 | { | |
132 | // No need to be implemented for connection-less ConnStateData object. | |
133 | assert(0); | |
134 | return 0; | |
135 | } | |
136 | ||
137 | void | |
d3b1bee6 | 138 | Downloader::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) |
55369ae6 AR |
139 | { |
140 | } | |
141 | ||
142 | void | |
143 | Downloader::handleReply(HttpReply *reply, StoreIOBuffer receivedData) | |
144 | { | |
54fb1cbf | 145 | Http::StreamPointer context = pipeline.front(); |
55369ae6 | 146 | bool existingContent = reply ? reply->content_length : 0; |
d3b1bee6 | 147 | bool exceedSize = (context->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) || |
6cae08c9 | 148 | ((object.length() + receivedData.length) > MaxObjectSize); |
55369ae6 AR |
149 | |
150 | if (exceedSize) { | |
151 | status = Http::scInternalServerError; | |
152 | callBack(); | |
153 | return; | |
154 | } | |
155 | ||
156 | debugs(33, 4, "Received " << receivedData.length << | |
157 | " object data, offset: " << receivedData.offset << | |
158 | " error flag:" << receivedData.flags.error); | |
159 | ||
160 | if (receivedData.length > 0) { | |
161 | object.append(receivedData.data, receivedData.length); | |
d3b1bee6 CT |
162 | context->http->out.size += receivedData.length; |
163 | context->noteSentBodyBytes(receivedData.length); | |
55369ae6 AR |
164 | } |
165 | ||
d3b1bee6 | 166 | switch (context->socketState()) { |
55369ae6 AR |
167 | case STREAM_NONE: |
168 | debugs(33, 3, "Get more data"); | |
d3b1bee6 | 169 | context->pullData(); |
55369ae6 AR |
170 | break; |
171 | case STREAM_COMPLETE: | |
172 | debugs(33, 3, "Object data transfer successfully complete"); | |
173 | status = Http::scOkay; | |
174 | callBack(); | |
175 | break; | |
176 | case STREAM_UNPLANNED_COMPLETE: | |
177 | debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE"); | |
178 | status = Http::scInternalServerError; | |
179 | callBack(); | |
180 | break; | |
181 | case STREAM_FAILED: | |
182 | debugs(33, 3, "Object data transfer failed: STREAM_FAILED"); | |
183 | status = Http::scInternalServerError; | |
184 | callBack(); | |
185 | break; | |
186 | default: | |
187 | fatal("unreachable code"); | |
188 | } | |
189 | } | |
190 | ||
191 | void | |
192 | Downloader::downloadFinished() | |
193 | { | |
194 | debugs(33, 3, "fake call, to just delete the Downloader"); | |
195 | ||
196 | // Not really needed. Squid will delete this object because "doneAll" is true. | |
197 | //deleteThis("completed"); | |
198 | } | |
199 | ||
200 | void | |
201 | Downloader::callBack() | |
202 | { | |
203 | CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer()); | |
204 | Must(dialer); | |
205 | dialer->status = status; | |
206 | if (status == Http::scOkay) | |
207 | dialer->object = object; | |
208 | ScheduleCallHere(callback); | |
209 | callback = NULL; | |
210 | // Calling deleteThis method here to finish Downloader | |
211 | // may result to squid crash. | |
212 | // This method called by handleReply method which maybe called | |
213 | // by ClientHttpRequest::doCallouts. The doCallouts after this object deleted | |
214 | // may operate on non valid objects. | |
215 | // Schedule a fake call here just to force squid to delete this object | |
216 | CallJobHere(33, 7, CbcPointer<Downloader>(this), Downloader, downloadFinished); | |
217 | } | |
218 | ||
219 | bool | |
220 | Downloader::isOpen() const | |
221 | { | |
222 | return cbdataReferenceValid(this) && // XXX: checking "this" in a method | |
223 | callback != NULL; | |
224 | } |