1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "fuchsia/http/url_loader_impl.h"
6
7 #include "base/fuchsia/fuchsia_logging.h"
8 #include "base/message_loop/message_loop_current.h"
9 #include "base/task/post_task.h"
10 #include "fuchsia/base/mem_buffer_util.h"
11 #include "net/base/chunked_upload_data_stream.h"
12 #include "net/base/net_errors.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/url_request/redirect_info.h"
15
16 namespace oldhttp = ::fuchsia::net::oldhttp;
17
18 namespace {
19 // Capacity, in bytes, for buffers used to move data from client requests or
20 // server responses.
21 const size_t kReadCapacity = 1024;
22
23 // The number of active requests. Used for testing.
24 int g_active_requests = 0;
25
26 // Converts |buffer| into a URLBody with the body set to a buffer. Returns
27 // nullptr when an error occurs.
CreateURLBodyFromBuffer(net::GrowableIOBuffer * buffer)28 oldhttp::URLBodyPtr CreateURLBodyFromBuffer(net::GrowableIOBuffer* buffer) {
29 oldhttp::URLBodyPtr body = oldhttp::URLBody::New();
30
31 // The response buffer size is exactly the offset.
32 size_t total_size = buffer->offset();
33
34 body->set_buffer(cr_fuchsia::MemBufferFromString(
35 base::StringPiece(buffer->StartOfBuffer(), total_size),
36 "cr-http-url-body"));
37
38 return body;
39 }
40
NetErrorToHttpError(int net_error)41 int NetErrorToHttpError(int net_error) {
42 // TODO(https://crbug.com/875533): Convert the Chromium //net error to their
43 // Fuchsia counterpart.
44 return net_error;
45 }
46
BuildError(int net_error)47 oldhttp::HttpErrorPtr BuildError(int net_error) {
48 if (net_error == net::OK) {
49 return nullptr;
50 }
51
52 oldhttp::HttpErrorPtr error = oldhttp::HttpError::New();
53 error->code = NetErrorToHttpError(net_error);
54 error->description = net::ErrorToString(net_error);
55 return error;
56 }
57
UploadDataStreamFromZxSocket(zx::socket stream)58 std::unique_ptr<net::UploadDataStream> UploadDataStreamFromZxSocket(
59 zx::socket stream) {
60 // TODO(http://crbug.com/875534): Write a ZxStreamUploadStream class.
61 std::unique_ptr<net::ChunkedUploadDataStream> upload_stream =
62 std::make_unique<net::ChunkedUploadDataStream>(0);
63 char buffer[kReadCapacity];
64 size_t size = 0;
65 zx_status_t result = ZX_OK;
66 while (true) {
67 result = stream.read(0, buffer, kReadCapacity, &size);
68 if (result != ZX_OK) {
69 ZX_DLOG(WARNING, result) << "zx_socket_read";
70 return nullptr;
71 }
72 if (size < kReadCapacity) {
73 upload_stream->AppendData(buffer, size, false);
74 break;
75 }
76 upload_stream->AppendData(buffer, size, true);
77 }
78
79 return upload_stream;
80 }
81
UploadDataStreamFromMemBuffer(fuchsia::mem::Buffer mem_buffer)82 std::unique_ptr<net::UploadDataStream> UploadDataStreamFromMemBuffer(
83 fuchsia::mem::Buffer mem_buffer) {
84 // TODO(http://crbug.com/875534): Write a ZxMemBufferUploadStream class.
85 std::unique_ptr<net::ChunkedUploadDataStream> upload_stream =
86 std::make_unique<net::ChunkedUploadDataStream>(0);
87
88 char buffer[kReadCapacity];
89 size_t size = mem_buffer.size;
90 size_t offset = 0;
91 zx_status_t result = ZX_OK;
92 while (offset != size) {
93 size_t length = std::min(size - offset, kReadCapacity);
94 result = mem_buffer.vmo.read(buffer, offset, length);
95 if (result != ZX_OK) {
96 ZX_DLOG(WARNING, result) << "zx_vmo_read";
97 return nullptr;
98 }
99 upload_stream->AppendData(buffer, length, false);
100 offset += length;
101 }
102
103 return upload_stream;
104 }
105
106 } // namespace
107
URLLoaderImpl(std::unique_ptr<net::URLRequestContext> context,fidl::InterfaceRequest<oldhttp::URLLoader> request)108 URLLoaderImpl::URLLoaderImpl(std::unique_ptr<net::URLRequestContext> context,
109 fidl::InterfaceRequest<oldhttp::URLLoader> request)
110 : binding_(this, std::move(request)),
111 context_(std::move(context)),
112 buffer_(new net::GrowableIOBuffer()),
113 write_watch_(FROM_HERE) {
114 binding_.set_error_handler([this](zx_status_t status) {
115 ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
116 << " URLLoader disconnected.";
117 delete this;
118 });
119 g_active_requests++;
120 }
121
~URLLoaderImpl()122 URLLoaderImpl::~URLLoaderImpl() {
123 g_active_requests--;
124 }
125
GetNumActiveRequestsForTests()126 int URLLoaderImpl::GetNumActiveRequestsForTests() {
127 return g_active_requests;
128 }
129
Start(oldhttp::URLRequest request,Callback callback)130 void URLLoaderImpl::Start(oldhttp::URLRequest request, Callback callback) {
131 if (net_request_) {
132 callback(BuildResponse(net::ERR_IO_PENDING));
133 return;
134 }
135
136 done_callback_ = std::move(callback);
137 net_error_ = net::OK;
138
139 // Create the URLRequest and set this object as the delegate.
140 net_request_ = context_->CreateRequest(GURL(request.url),
141 net::RequestPriority::MEDIUM, this);
142 net_request_->set_method(request.method);
143
144 // Set extra headers.
145 if (request.headers) {
146 for (oldhttp::HttpHeader header : *(request.headers)) {
147 net_request_->SetExtraRequestHeaderByName(header.name, header.value,
148 false);
149 }
150 }
151 if (request.cache_mode == oldhttp::CacheMode::BYPASS_CACHE) {
152 net_request_->SetExtraRequestHeaderByName("Cache-Control", "nocache",
153 false);
154 }
155
156 std::unique_ptr<net::UploadDataStream> upload_stream;
157 // Set body.
158 if (request.body) {
159 if (request.body->is_stream()) {
160 upload_stream =
161 UploadDataStreamFromZxSocket(std::move(request.body->stream()));
162 } else {
163 upload_stream =
164 UploadDataStreamFromMemBuffer(std::move(request.body->buffer()));
165 }
166
167 if (!upload_stream) {
168 std::move(done_callback_)(BuildResponse(net::ERR_ACCESS_DENIED));
169 return;
170 }
171 net_request_->set_upload(std::move(upload_stream));
172 }
173
174 auto_follow_redirects_ = request.auto_follow_redirects;
175 response_body_mode_ = request.response_body_mode;
176
177 // Start the request.
178 net_request_->Start();
179 }
180
FollowRedirect(Callback callback)181 void URLLoaderImpl::FollowRedirect(Callback callback) {
182 if (!net_request_ || auto_follow_redirects_ ||
183 !net_request_->is_redirecting()) {
184 callback(BuildResponse(net::ERR_INVALID_HANDLE));
185 }
186
187 done_callback_ = std::move(callback);
188 net_request_->FollowDeferredRedirect(base::nullopt /* removed_headers */,
189 base::nullopt /* modified_headers */);
190 }
191
QueryStatus(QueryStatusCallback callback)192 void URLLoaderImpl::QueryStatus(QueryStatusCallback callback) {
193 oldhttp::URLLoaderStatus status;
194
195 if (!net_request_) {
196 status.is_loading = false;
197 } else if (net_request_->is_pending() || net_request_->is_redirecting()) {
198 status.is_loading = true;
199 } else {
200 status.is_loading = false;
201 status.error = BuildError(net_error_);
202 }
203
204 callback(std::move(status));
205 }
206
OnReceivedRedirect(net::URLRequest * request,const net::RedirectInfo & redirect_info,bool * defer_redirect)207 void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* request,
208 const net::RedirectInfo& redirect_info,
209 bool* defer_redirect) {
210 DCHECK_EQ(net_request_.get(), request);
211 // Follow redirect depending on policy.
212 *defer_redirect = !auto_follow_redirects_;
213
214 if (!auto_follow_redirects_) {
215 oldhttp::URLResponse response = BuildResponse(net::OK);
216 response.redirect_method = redirect_info.new_method;
217 response.redirect_url = redirect_info.new_url.spec();
218 response.redirect_referrer = redirect_info.new_referrer;
219 std::move(done_callback_)(std::move(response));
220 }
221 }
222
OnAuthRequired(net::URLRequest * request,const net::AuthChallengeInfo & auth_info)223 void URLLoaderImpl::OnAuthRequired(net::URLRequest* request,
224 const net::AuthChallengeInfo& auth_info) {
225 NOTIMPLEMENTED();
226 DCHECK_EQ(net_request_.get(), request);
227 request->CancelAuth();
228 }
229
OnCertificateRequested(net::URLRequest * request,net::SSLCertRequestInfo * cert_request_info)230 void URLLoaderImpl::OnCertificateRequested(
231 net::URLRequest* request,
232 net::SSLCertRequestInfo* cert_request_info) {
233 NOTIMPLEMENTED();
234 DCHECK_EQ(net_request_.get(), request);
235 request->ContinueWithCertificate(nullptr, nullptr);
236 }
237
OnSSLCertificateError(net::URLRequest * request,int net_error,const net::SSLInfo & ssl_info,bool fatal)238 void URLLoaderImpl::OnSSLCertificateError(net::URLRequest* request,
239 int net_error,
240 const net::SSLInfo& ssl_info,
241 bool fatal) {
242 NOTIMPLEMENTED();
243 DCHECK_EQ(net_request_.get(), request);
244 request->Cancel();
245 }
246
OnResponseStarted(net::URLRequest * request,int net_error)247 void URLLoaderImpl::OnResponseStarted(net::URLRequest* request, int net_error) {
248 DCHECK_EQ(net_request_.get(), request);
249 net_error_ = net_error;
250
251 // Return early if the request failed.
252 if (net_error_ != net::OK) {
253 std::move(done_callback_)(BuildResponse(net_error_));
254 return;
255 }
256
257 // In stream mode, call the callback now and write to the socket.
258 if (response_body_mode_ == oldhttp::ResponseBodyMode::STREAM ||
259 response_body_mode_ == oldhttp::ResponseBodyMode::BUFFER_OR_STREAM) {
260 zx::socket read_socket;
261 zx_status_t result = zx::socket::create(0, &read_socket, &write_socket_);
262 if (result != ZX_OK) {
263 ZX_DLOG(WARNING, result) << "zx_socket_create";
264 std::move(done_callback_)(BuildResponse(net::ERR_INSUFFICIENT_RESOURCES));
265 return;
266 }
267 oldhttp::URLResponse response = BuildResponse(net::OK);
268 response.body = oldhttp::URLBody::New();
269 response.body->set_stream(std::move(read_socket));
270 std::move(done_callback_)(std::move(response));
271 }
272
273 // In stream mode, the buffer is used as a temporary buffer to write to the
274 // socket. In buffer mode, it is expanded as more of the response is read.
275 buffer_->SetCapacity(kReadCapacity);
276
277 ReadNextBuffer();
278 }
279
OnReadCompleted(net::URLRequest * request,int bytes_read)280 void URLLoaderImpl::OnReadCompleted(net::URLRequest* request, int bytes_read) {
281 DCHECK_EQ(net_request_.get(), request);
282 if (WriteResponseBytes(bytes_read)) {
283 ReadNextBuffer();
284 }
285 }
286
OnZxHandleSignalled(zx_handle_t handle,zx_signals_t signals)287 void URLLoaderImpl::OnZxHandleSignalled(zx_handle_t handle,
288 zx_signals_t signals) {
289 // We should never have to process signals we didn't ask for.
290 DCHECK((ZX_CHANNEL_WRITABLE | ZX_CHANNEL_PEER_CLOSED) & signals);
291 DCHECK_GT(buffered_bytes_, 0);
292
293 if (signals & ZX_CHANNEL_PEER_CLOSED) {
294 return;
295 }
296
297 if (WriteResponseBytes(buffered_bytes_))
298 ReadNextBuffer();
299 buffered_bytes_ = 0;
300 }
301
ReadNextBuffer()302 void URLLoaderImpl::ReadNextBuffer() {
303 int net_result;
304 do {
305 net_result = net_request_->Read(buffer_.get(), kReadCapacity);
306 if (net_result == net::ERR_IO_PENDING) {
307 return;
308 }
309 } while (WriteResponseBytes(net_result));
310 }
311
WriteResponseBytes(int result)312 bool URLLoaderImpl::WriteResponseBytes(int result) {
313 if (result < 0) {
314 // Signal read error back to the client.
315 if (write_socket_) {
316 DCHECK(response_body_mode_ == oldhttp::ResponseBodyMode::STREAM ||
317 response_body_mode_ ==
318 oldhttp::ResponseBodyMode::BUFFER_OR_STREAM);
319 // There is no need to check the return value of this call as there is no
320 // way to recover from a failed socket close.
321 write_socket_ = zx::socket();
322 } else {
323 DCHECK_EQ(response_body_mode_, oldhttp::ResponseBodyMode::BUFFER);
324 std::move(done_callback_)(BuildResponse(result));
325 }
326 return false;
327 }
328
329 if (result == 0) {
330 // Read complete.
331 if (write_socket_) {
332 DCHECK(response_body_mode_ == oldhttp::ResponseBodyMode::STREAM ||
333 response_body_mode_ ==
334 oldhttp::ResponseBodyMode::BUFFER_OR_STREAM);
335 // In socket mode, attempt to shut down the socket and close it.
336 write_socket_.shutdown(ZX_SOCKET_SHUTDOWN_WRITE);
337 write_socket_ = zx::socket();
338 } else {
339 DCHECK_EQ(response_body_mode_, oldhttp::ResponseBodyMode::BUFFER);
340 // In buffer mode, build the response and call the callback.
341 oldhttp::URLBodyPtr body = CreateURLBodyFromBuffer(buffer_.get());
342 if (body) {
343 oldhttp::URLResponse response = BuildResponse(net::OK);
344 response.body = std::move(body);
345 std::move(done_callback_)(std::move(response));
346 } else {
347 std::move(done_callback_)(
348 BuildResponse(net::ERR_INSUFFICIENT_RESOURCES));
349 }
350 }
351 return false;
352 }
353
354 // Write data to the response buffer or socket.
355 if (write_socket_) {
356 DCHECK(response_body_mode_ == oldhttp::ResponseBodyMode::STREAM ||
357 response_body_mode_ == oldhttp::ResponseBodyMode::BUFFER_OR_STREAM);
358 // In socket mode, attempt to write to the socket.
359 zx_status_t status =
360 write_socket_.write(0, buffer_->data(), result, nullptr);
361 if (status == ZX_ERR_SHOULD_WAIT) {
362 // Wait until the socket is writable again.
363 buffered_bytes_ = result;
364 base::MessageLoopCurrentForIO::Get()->WatchZxHandle(
365 write_socket_.get(), false /* persistent */,
366 ZX_SOCKET_WRITABLE | ZX_SOCKET_PEER_CLOSED, &write_watch_, this);
367 return false;
368 }
369 if (status != ZX_OK) {
370 // Something went wrong, attempt to shut down the socket and close it.
371 ZX_DLOG(WARNING, status) << "zx_socket_write";
372 write_socket_ = zx::socket();
373 return false;
374 }
375 } else {
376 DCHECK_EQ(response_body_mode_, oldhttp::ResponseBodyMode::BUFFER);
377 // In buffer mode, expand the buffer.
378 buffer_->SetCapacity(buffer_->capacity() + result);
379 buffer_->set_offset(buffer_->offset() + result);
380 }
381
382 return true;
383 }
384
BuildResponse(int net_error)385 oldhttp::URLResponse URLLoaderImpl::BuildResponse(int net_error) {
386 oldhttp::URLResponse response;
387
388 response.error = BuildError(net_error);
389 if (response.error) {
390 return response;
391 }
392
393 if (net_request_->url().is_valid()) {
394 response.url = net_request_->url().spec();
395 }
396 response.status_code = net_request_->GetResponseCode();
397
398 net::HttpResponseHeaders* response_headers = net_request_->response_headers();
399 if (response_headers) {
400 response.status_line = response_headers->GetStatusLine();
401 std::string mime_type;
402 if (response_headers->GetMimeType(&mime_type)) {
403 response.mime_type = mime_type;
404 }
405 std::string charset;
406 if (response_headers->GetCharset(&charset)) {
407 response.charset = charset;
408 }
409
410 size_t iter = 0;
411 std::string header_name;
412 std::string header_value;
413 response.headers.emplace();
414 while (response_headers->EnumerateHeaderLines(&iter, &header_name,
415 &header_value)) {
416 oldhttp::HttpHeader header;
417 header.name = header_name;
418 header.value = header_value;
419 response.headers->push_back(header);
420 }
421 }
422
423 return response;
424 }
425