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