1// Copyright 2014 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 "mojo/services/html_viewer/weburlloader_impl.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/strings/string_util.h"
10#include "base/thread_task_runner_handle.h"
11#include "mojo/common/common_type_converters.h"
12#include "mojo/services/html_viewer/blink_url_request_type_converters.h"
13#include "mojo/services/public/interfaces/network/network_service.mojom.h"
14#include "net/base/net_errors.h"
15#include "third_party/WebKit/public/platform/WebURLError.h"
16#include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
17#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
18#include "third_party/WebKit/public/platform/WebURLResponse.h"
19
20namespace mojo {
21namespace {
22
23static blink::WebURLResponse::HTTPVersion StatusLineToHTTPVersion(
24    const String& status_line) {
25  if (status_line.is_null())
26    return blink::WebURLResponse::HTTP_0_9;
27
28  if (StartsWithASCII(status_line, "HTTP/1.0", true))
29    return blink::WebURLResponse::HTTP_1_0;
30
31  if (StartsWithASCII(status_line, "HTTP/1.1", true))
32    return blink::WebURLResponse::HTTP_1_1;
33
34  return blink::WebURLResponse::Unknown;
35}
36
37blink::WebURLResponse ToWebURLResponse(const URLResponsePtr& url_response) {
38  blink::WebURLResponse result;
39  result.initialize();
40  result.setURL(GURL(url_response->url));
41  result.setMIMEType(blink::WebString::fromUTF8(url_response->mime_type));
42  result.setTextEncodingName(blink::WebString::fromUTF8(url_response->charset));
43  result.setHTTPVersion(StatusLineToHTTPVersion(url_response->status_line));
44  result.setHTTPStatusCode(url_response->status_code);
45
46  // TODO(darin): Initialize timing properly.
47  blink::WebURLLoadTiming timing;
48  timing.initialize();
49  result.setLoadTiming(timing);
50
51  for (size_t i = 0; i < url_response->headers.size(); ++i) {
52    const std::string& header_line = url_response->headers[i];
53    size_t first_colon = header_line.find(":");
54
55    if (first_colon == std::string::npos || first_colon == 0)
56      continue;
57
58    std::string value;
59    TrimWhitespaceASCII(header_line.substr(first_colon + 1),
60                        base::TRIM_LEADING,
61                        &value);
62    result.setHTTPHeaderField(
63        blink::WebString::fromUTF8(header_line.substr(0, first_colon)),
64        blink::WebString::fromUTF8(value));
65  }
66
67  return result;
68}
69
70}  // namespace
71
72WebURLRequestExtraData::WebURLRequestExtraData() {
73}
74
75WebURLRequestExtraData::~WebURLRequestExtraData() {
76}
77
78WebURLLoaderImpl::WebURLLoaderImpl(NetworkService* network_service)
79    : client_(NULL),
80      weak_factory_(this) {
81  network_service->CreateURLLoader(Get(&url_loader_));
82}
83
84WebURLLoaderImpl::~WebURLLoaderImpl() {
85}
86
87void WebURLLoaderImpl::loadSynchronously(
88    const blink::WebURLRequest& request,
89    blink::WebURLResponse& response,
90    blink::WebURLError& error,
91    blink::WebData& data) {
92  NOTIMPLEMENTED();
93}
94
95void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request,
96                                          blink::WebURLLoaderClient* client) {
97  client_ = client;
98  url_ = request.url();
99
100  URLRequestPtr url_request = URLRequest::From(request);
101  url_request->auto_follow_redirects = false;
102
103  if (request.extraData()) {
104    WebURLRequestExtraData* extra_data =
105        static_cast<WebURLRequestExtraData*>(request.extraData());
106    base::ThreadTaskRunnerHandle::Get()->PostTask(
107        FROM_HERE,
108        base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
109                   weak_factory_.GetWeakPtr(),
110                   base::Passed(&extra_data->synthetic_response)));
111  } else {
112    url_loader_->Start(url_request.Pass(),
113                       base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
114                                  weak_factory_.GetWeakPtr()));
115  }
116}
117
118void WebURLLoaderImpl::cancel() {
119  url_loader_.reset();
120  response_body_stream_.reset();
121
122  URLResponsePtr failed_response(URLResponse::New());
123  failed_response->url = String::From(url_);
124  failed_response->error = NetworkError::New();
125  failed_response->error->code = net::ERR_ABORTED;
126
127  base::ThreadTaskRunnerHandle::Get()->PostTask(
128      FROM_HERE,
129      base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
130                 weak_factory_.GetWeakPtr(),
131                 base::Passed(&failed_response)));
132}
133
134void WebURLLoaderImpl::setDefersLoading(bool defers_loading) {
135  NOTIMPLEMENTED();
136}
137
138void WebURLLoaderImpl::OnReceivedResponse(URLResponsePtr url_response) {
139  url_ = GURL(url_response->url);
140
141  if (url_response->error) {
142    OnReceivedError(url_response.Pass());
143  } else if (url_response->redirect_url) {
144    OnReceivedRedirect(url_response.Pass());
145  } else {
146    base::WeakPtr<WebURLLoaderImpl> self(weak_factory_.GetWeakPtr());
147    client_->didReceiveResponse(this, ToWebURLResponse(url_response));
148
149    // We may have been deleted during didReceiveResponse.
150    if (!self)
151      return;
152
153    // Start streaming data
154    response_body_stream_ = url_response->body.Pass();
155    ReadMore();
156  }
157}
158
159void WebURLLoaderImpl::OnReceivedError(URLResponsePtr url_response) {
160  blink::WebURLError web_error;
161  web_error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
162  web_error.reason = url_response->error->code;
163  web_error.unreachableURL = GURL(url_response->url);
164  web_error.staleCopyInCache = false;
165  web_error.isCancellation =
166      url_response->error->code == net::ERR_ABORTED ? true : false;
167
168  client_->didFail(this, web_error);
169}
170
171void WebURLLoaderImpl::OnReceivedRedirect(URLResponsePtr url_response) {
172  blink::WebURLRequest new_request;
173  new_request.initialize();
174  new_request.setURL(GURL(url_response->redirect_url));
175  new_request.setHTTPMethod(
176      blink::WebString::fromUTF8(url_response->redirect_method));
177
178  client_->willSendRequest(this, new_request, ToWebURLResponse(url_response));
179  // TODO(darin): Check if new_request was rejected.
180
181  url_loader_->FollowRedirect(
182      base::Bind(&WebURLLoaderImpl::OnReceivedResponse,
183                 weak_factory_.GetWeakPtr()));
184}
185
186void WebURLLoaderImpl::ReadMore() {
187  const void* buf;
188  uint32_t buf_size;
189  MojoResult rv = BeginReadDataRaw(response_body_stream_.get(),
190                                   &buf,
191                                   &buf_size,
192                                   MOJO_READ_DATA_FLAG_NONE);
193  if (rv == MOJO_RESULT_OK) {
194    client_->didReceiveData(this, static_cast<const char*>(buf), buf_size, -1);
195    EndReadDataRaw(response_body_stream_.get(), buf_size);
196    WaitToReadMore();
197  } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
198    WaitToReadMore();
199  } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
200    // We reached end-of-file.
201    double finish_time = base::Time::Now().ToDoubleT();
202    client_->didFinishLoading(
203        this,
204        finish_time,
205        blink::WebURLLoaderClient::kUnknownEncodedDataLength);
206  } else {
207    // TODO(darin): Oops!
208  }
209}
210
211void WebURLLoaderImpl::WaitToReadMore() {
212  handle_watcher_.Start(
213      response_body_stream_.get(),
214      MOJO_HANDLE_SIGNAL_READABLE,
215      MOJO_DEADLINE_INDEFINITE,
216      base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady,
217                 weak_factory_.GetWeakPtr()));
218}
219
220void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
221  ReadMore();
222}
223
224}  // namespace mojo
225