blob: bbba16842ff88f13159db1ac802daa903ae4eb6b [file] [log] [blame]
// 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 "mojo/services/network/url_loader_impl.h"
#include "mojo/services/network/http_client.h"
#include "mojo/services/network/network_error.h"
#include <memory>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
namespace mojo {
NetworkErrorPtr MakeNetworkError(int error_code) {
NetworkErrorPtr error = NetworkError::New();
error->code = error_code;
return error.Pass();
}
URLLoaderImpl::URLLoaderImpl(InterfaceRequest<URLLoader> request)
: binding_(this, request.Pass()), responded_(false)
{
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) {
callback_ = callback;
}
void URLLoaderImpl::QueryStatus(
const Callback<void(URLLoaderStatusPtr)>& callback) {
URLLoaderStatusPtr status(URLLoaderStatus::New());
/* TODO(toshik): fill status */
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());
responded_ = true;
}
bool URLLoaderImpl::ParseURL(const std::string& url, std::string& proto,
std::string& host, std::string& port,
std::string& path) {
std::string delim("://");
std::string::const_iterator proto_end =
std::search(url.begin(), url.end(), delim.begin(), delim.end());
if (proto_end == url.end()) {
return false;
}
proto.assign(url.begin(), proto_end);
std::string::const_iterator host_start = proto_end + delim.length();
std::string::const_iterator path_start = std::find(host_start, url.end(), '/');
std::string::const_iterator host_end = std::find(host_start, path_start, ':');
host.assign(host_start, host_end);
if (host_end != path_start)
port.assign(host_end + 1, path_start);
else
port = proto;
if (path_start != url.end())
path.assign(path_start, url.end());
else
path.assign("/");
if (proto.length() == 0 || host.length() == 0 || port.length() == 0 ||
path.length() == 0)
return false;
return true;
}
void URLLoaderImpl::StartInternal(URLRequestPtr request) {
std::string url(request->url);
asio::io_service io_service;
bool redirect = false;
int error_code = ERR_UNEXPECTED;
do {
std::string proto, host, port, path;
if (!ParseURL(url, proto, host, port, path)) {
std::cout << "url parse error" << std::endl;
error_code = ERR_INVALID_ARGUMENT;
break;
}
if (redirect) {
io_service.reset();
redirect = false;
}
if (proto == "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, host, port, path);
io_service.run();
if (c.status_code_ == 301 || c.status_code_ == 302) {
redirect = true;
url = c.redirect_location_;
}
#else
std::cout << "https is not built-in. "
"please build with NETWORK_SERVICE_USE_HTTPS" << std::endl;
error_code = ERR_INVALID_ARGUMENT;
break;
#endif
} else if (proto == "http") {
HTTPClient<tcp::socket> c(this, io_service, host, port, path);
io_service.run();
if (c.status_code_ == 301 || c.status_code_ == 302) {
redirect = true;
url = c.redirect_location_;
std::cout << "Redirecting to: " << url << std::endl;
}
} else {
// unknown protocol
error_code = ERR_INVALID_ARGUMENT;
break;
}
} while (redirect);
if (!responded_)
SendError(error_code);
}
} // namespace mojo