blob: ded1a21d06ca0ffe73a6c994e4c4463255b57e9c [file] [log] [blame] [edit]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "mojo/services/network/url_loader_impl.h"
#include "mojo/services/network/http_client.h"
#include "mojo/services/network/net_errors.h"
#include "mojo/services/network/net_adapters.h"
#include "url/gurl.h"
#include <istream>
#include <ostream>
#include <string>
#include <vector>
#include <memory>
namespace mojo {
URLLoaderImpl::URLLoaderImpl(InterfaceRequest<URLLoader> request)
: binding_(this, request.Pass())
{
binding_.set_connection_error_handler([this]() { OnConnectionError(); });
}
URLLoaderImpl::~URLLoaderImpl() {
}
void URLLoaderImpl::Cleanup() {
delete this;
}
void URLLoaderImpl::Start(URLRequestPtr request,
const Callback<void(URLResponsePtr)>& callback) {
callback_ = callback;
StartInternal(request.Pass());
}
void URLLoaderImpl::FollowRedirect(
const Callback<void(URLResponsePtr)>& callback) {
NOTIMPLEMENTED();
callback_ = callback;
SendError(net::ERR_NOT_IMPLEMENTED);
}
void URLLoaderImpl::QueryStatus(
const Callback<void(URLLoaderStatusPtr)>& callback) {
URLLoaderStatusPtr status(URLLoaderStatus::New());
NOTIMPLEMENTED();
status->error = MakeNetworkError(net::ERR_NOT_IMPLEMENTED);
callback.Run(status.Pass());
}
void URLLoaderImpl::OnConnectionError() {
/* TODO(toshik) */
binding_.Close();
}
void URLLoaderImpl::SendError(int error_code) {
URLResponsePtr response(URLResponse::New());
response->error = MakeNetworkError(error_code);
SendResponse(response.Pass());
}
void URLLoaderImpl::FollowRedirectInternal() {
/* TODO(toshik) */
}
void URLLoaderImpl::SendResponse(URLResponsePtr response) {
Callback<void(URLResponsePtr)> callback;
std::swap(callback_, callback);
callback.Run(response.Pass());
}
void URLLoaderImpl::StartInternal(URLRequestPtr request) {
std::string url_str(request->url);
std::string method(request->method);
std::map<std::string, std::string> extra_headers;
std::vector<std::unique_ptr<UploadElementReader>> element_readers;
if (request->headers) {
for (size_t i = 0; i < request->headers.size(); ++i)
extra_headers[request->headers[i]->name] = request->headers[i]->value;
}
if (request->body) {
for (size_t i = 0; i < request->body.size(); ++i)
element_readers.push_back(
std::unique_ptr<UploadElementReader>(
new UploadElementReader(request->body[i].Pass())));
}
asio::io_service io_service;
bool redirect = false;
GURL url(url_str);
if (!url.is_valid()) {
LOG(ERROR) << "url parse error";
SendError(net::ERR_INVALID_ARGUMENT);
return;
}
do {
if (redirect) {
io_service.reset();
redirect = false;
}
if (url.SchemeIs("https")) {
#ifdef NETWORK_SERVICE_USE_HTTPS
asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.set_default_verify_paths();
HTTPClient<asio::ssl::stream<tcp::socket>> c(this, io_service, ctx);
MojoResult result = c.CreateRequest(url.host(), url.path(), method,
extra_headers, element_readers);
if (result != MOJO_RESULT_OK) {
SendError(net::ERR_INVALID_ARGUMENT);
break;
}
c.Start(url.host(), url.has_port() ? url.port() : "https");
io_service.run();
if (c.status_code_ == 301 || c.status_code_ == 302) {
redirect = true;
url = GURL(c.redirect_location_);
if (!url.is_valid()) {
LOG(ERROR) << "url parse error";
SendError(net::ERR_INVALID_RESPONSE);
break;
}
}
#else
LOG(INFO) << "https is not built-in. "
"please build with NETWORK_SERVICE_USE_HTTPS";
SendError(net::ERR_INVALID_ARGUMENT);
break;
#endif
} else if (url.SchemeIs("http")) {
HTTPClient<tcp::socket> c(this, io_service);
MojoResult result = c.CreateRequest(url.host(), url.path(), method,
extra_headers, element_readers);
if (result != MOJO_RESULT_OK) {
SendError(net::ERR_INVALID_ARGUMENT);
break;
}
c.Start(url.host(), url.has_port() ? url.port() : "http");
io_service.run();
if (c.status_code_ == 301 || c.status_code_ == 302) {
redirect = true;
url = GURL(c.redirect_location_);
if (!url.is_valid()) {
LOG(ERROR) << "url parse error";
SendError(net::ERR_INVALID_RESPONSE);
break;
}
}
} else {
// unknown protocol
LOG(ERROR) << "unknown protocol";
SendError(net::ERR_INVALID_ARGUMENT);
break;
}
} while (redirect);
}
} // namespace mojo