Create a URL utility class out of the existing URL parsing code This is a small refactoring. We should also consider adopting an open source URL library (I haven't looked at any, yet). Change-Id: I36e973aa16511a63fbcbae089b887c5a0aa3bc78
diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn index 92e842a..1c6667e 100644 --- a/mojo/services/network/BUILD.gn +++ b/mojo/services/network/BUILD.gn
@@ -34,6 +34,8 @@ "network_service_impl.h", "upload_element_reader.cc", "upload_element_reader.h", + "url.cc", + "url.h", "url_loader_impl.cc", "url_loader_impl.h", ]
diff --git a/mojo/services/network/url.cc b/mojo/services/network/url.cc new file mode 100644 index 0000000..9f55708 --- /dev/null +++ b/mojo/services/network/url.cc
@@ -0,0 +1,76 @@ +// Copyright 2016 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.h" + +#include <algorithm> + +namespace mojo { + +URL::URL(const std::string& str) : str_(str) { + parsed_ = Parse(); +} + +URL::~URL() {} + +bool URL::Parse() { + std::string delim("://"); + std::string::const_iterator proto_end = + std::search(str_.begin(), str_.end(), delim.begin(), delim.end()); + if (proto_end == str_.end()) { + return false; + } + proto_.assign(str_.begin(), proto_end); + + std::string::const_iterator host_start = proto_end + delim.length(); + std::string::const_iterator path_start = std::find(host_start, str_.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 != str_.end()) + path_.assign(path_start, str_.end()); + else + path_.assign("/"); + + if (proto_.length() == 0 || host_.length() == 0 || port_.length() == 0 || + path_.length() == 0) + return false; + + return true; +} + +bool URL::IsParsed() { + return parsed_; +} + +std::string& URL::Proto() { + DCHECK(parsed_); + return proto_; +} + +std::string& URL::Host() { + DCHECK(parsed_); + return host_; +} + +std::string& URL::Port() { + DCHECK(parsed_); + return port_; +} + +std::string& URL::Path() { + DCHECK(parsed_); + return path_; +} + +} // namespace mojo
diff --git a/mojo/services/network/url.h b/mojo/services/network/url.h new file mode 100644 index 0000000..683bc43 --- /dev/null +++ b/mojo/services/network/url.h
@@ -0,0 +1,38 @@ +// Copyright 2016 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. + +#ifndef MOJO_SERVICES_NETWORK_URL_H_ +#define MOJO_SERVICES_NETWORK_URL_H_ + +#include <string> + +namespace mojo { + +class URL { + public: + URL(const std::string& str); + ~URL(); + + bool IsParsed(); + + std::string& Proto(); + std::string& Host(); + std::string& Port(); + std::string& Path(); + + private: + const std::string str_; + + bool Parse(); + + bool parsed_; + std::string proto_; + std::string host_; + std::string port_; + std::string path_; +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NETWORK_URL_H_
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc index e7808a0..5c0917c 100644 --- a/mojo/services/network/url_loader_impl.cc +++ b/mojo/services/network/url_loader_impl.cc
@@ -72,41 +72,8 @@ callback.Run(response.Pass()); } -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); + 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; @@ -126,38 +93,42 @@ asio::io_service io_service; bool redirect = false; + std::unique_ptr<URL> url(new URL(url_str)); + if (!url->IsParsed()) { + LOG(ERROR) << "url parse error"; + SendError(net::ERR_INVALID_ARGUMENT); + return; + } + do { - std::string proto, host, port, path; - - if (!ParseURL(url, proto, host, port, path)) { - LOG(ERROR) << "url parse error"; - SendError(net::ERR_INVALID_ARGUMENT); - break; - } - if (redirect) { io_service.reset(); redirect = false; } - if (proto == "https") { + if (url->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); - MojoResult result = c.CreateRequest(host, path, method, + 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(host, port); + c.Start(url->Host(), url->Port()); io_service.run(); if (c.status_code_ == 301 || c.status_code_ == 302) { redirect = true; - url = c.redirect_location_; + url.reset(new URL(c.redirect_location_)); + if (!url->IsParsed()) { + LOG(ERROR) << "url parse error"; + SendError(net::ERR_INVALID_RESPONSE); + break; + } } #else LOG(INFO) << "https is not built-in. " @@ -165,20 +136,25 @@ SendError(net::ERR_INVALID_ARGUMENT); break; #endif - } else if (proto == "http") { + } else if (url->Proto() == "http") { HTTPClient<tcp::socket> c(this, io_service); - MojoResult result = c.CreateRequest(host, path, method, + 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(host, port); + c.Start(url->Host(), url->Port()); io_service.run(); if (c.status_code_ == 301 || c.status_code_ == 302) { redirect = true; - url = c.redirect_location_; + url.reset(new URL(c.redirect_location_)); + if (!url->IsParsed()) { + LOG(ERROR) << "url parse error"; + SendError(net::ERR_INVALID_RESPONSE); + break; + } } } else { // unknown protocol
diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h index 56690c1..222479c 100644 --- a/mojo/services/network/url_loader_impl.h +++ b/mojo/services/network/url_loader_impl.h
@@ -9,6 +9,8 @@ #include "mojo/services/network/interfaces/url_loader.mojom.h" #include "mojo/public/cpp/bindings/binding.h" +#include "mojo/services/network/url.h" + namespace mojo { class URLLoaderImpl : public URLLoader { @@ -32,8 +34,6 @@ void SendError(int error_code); void FollowRedirectInternal(); void SendResponse(URLResponsePtr response); - bool ParseURL(const std::string& url, std::string& scheme, std::string& host, - std::string& port, std::string& path); void StartInternal(URLRequestPtr request); Callback<void(URLResponsePtr)> callback_;