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.
5 #import "ios/web/public/test/http_server.h"
7 #import <Foundation/Foundation.h>
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/sys_string_conversions.h"
14 #import "net/base/mac/url_conversions.h"
15 #import "ios/third_party/gcdwebserver/src/GCDWebServer/Core/GCDWebServer.h"
16 #import "ios/third_party/gcdwebserver/src/GCDWebServer/Core/GCDWebServerResponse.h"
17 #import "ios/third_party/gcdwebserver/src/GCDWebServer/Requests/GCDWebServerDataRequest.h"
23 // The default port on which the GCDWebServer is brought up on.
24 const NSUInteger kDefaultPort = 8080;
26 // Converts a GCDWebServerDataRequest (received from the GCDWebServer servlet)
27 // to a request object that the ResponseProvider expects.
28 web::ResponseProvider::Request ResponseProviderRequestFromGCDWebServerRequest(
29 GCDWebServerDataRequest* request) {
30 GURL url(net::GURLWithNSURL(request.URL));
31 std::string method(base::SysNSStringToUTF8(request.method));
32 base::scoped_nsobject<NSString> body(
33 [[NSString alloc] initWithData:request.data
34 encoding:NSUTF8StringEncoding]);
35 __block net::HttpRequestHeaders headers;
36 [[request headers] enumerateKeysAndObjectsUsingBlock:^(NSString* header_key,
37 NSString* header_value,
39 headers.SetHeader(base::SysNSStringToUTF8(header_key),
40 base::SysNSStringToUTF8(header_value));
42 return web::ResponseProvider::Request(url, method,
43 base::SysNSStringToUTF8(body), headers);
52 RefCountedResponseProviderWrapper::RefCountedResponseProviderWrapper(
53 ResponseProvider* response_provider) {
54 response_provider_.reset(response_provider);
57 RefCountedResponseProviderWrapper::~RefCountedResponseProviderWrapper() {}
60 HttpServer& HttpServer::GetSharedInstance() {
61 static web::test::HttpServer* shared_instance = nullptr;
62 static dispatch_once_t once;
63 dispatch_once(&once, ^{
64 shared_instance = new HttpServer();
66 return *shared_instance;
70 HttpServer& HttpServer::GetSharedInstanceWithResponseProviders(
71 const ProviderList& response_providers) {
72 DCHECK([NSThread isMainThread]);
73 HttpServer& server = HttpServer::GetSharedInstance();
74 for (const auto& response_provider : response_providers) {
75 server.AddResponseProvider(response_provider);
80 void HttpServer::InitHttpServer() {
81 DCHECK(gcd_web_server_);
82 // Note: This block is called from an arbitrary GCD thread.
84 ^GCDWebServerResponse*(GCDWebServerDataRequest* request) {
85 ResponseProvider::Request provider_request =
86 ResponseProviderRequestFromGCDWebServerRequest(request);
87 scoped_refptr<RefCountedResponseProviderWrapper>
88 ref_counted_response_provider = GetResponseProviderForRequest(
91 if (!ref_counted_response_provider) {
92 return [GCDWebServerResponse response];
94 ResponseProvider* response_provider =
95 ref_counted_response_provider->GetResponseProvider();
96 if (!response_provider) {
97 return [GCDWebServerResponse response];
100 return response_provider->GetGCDWebServerResponse(provider_request);
102 [gcd_web_server_ removeAllHandlers];
103 // Register a servlet for all HTTP GET, POST methods.
104 [gcd_web_server_ addDefaultHandlerForMethod:@"GET"
105 requestClass:[GCDWebServerDataRequest class]
106 processBlock:process_request];
107 [gcd_web_server_ addDefaultHandlerForMethod:@"POST"
108 requestClass:[GCDWebServerDataRequest class]
109 processBlock:process_request];
112 HttpServer::HttpServer() : port_(0) {
113 gcd_web_server_.reset([[GCDWebServer alloc] init]);
117 HttpServer::~HttpServer() {
120 bool HttpServer::StartOnPort(NSUInteger port) {
121 DCHECK([NSThread isMainThread]);
122 DCHECK(!IsRunning()) << "The server is already running."
123 << " Please stop it before starting it again.";
124 BOOL success = [gcd_web_server_ startWithPort:port bonjourName:@""];
131 void HttpServer::StartOrDie() {
132 DCHECK([NSThread isMainThread]);
133 StartOnPort(kDefaultPort);
137 void HttpServer::Stop() {
138 DCHECK([NSThread isMainThread]);
139 DCHECK(IsRunning()) << "Cannot stop an already stopped server.";
140 RemoveAllResponseProviders();
141 [gcd_web_server_ stop];
145 bool HttpServer::IsRunning() const {
146 DCHECK([NSThread isMainThread]);
147 return [gcd_web_server_ isRunning];
151 GURL HttpServer::MakeUrl(const std::string &url) {
152 return HttpServer::GetSharedInstance().MakeUrlForHttpServer(url);
155 GURL HttpServer::MakeUrlForHttpServer(const std::string& url) const {
157 DCHECK(result.is_valid());
158 const std::string kLocalhostHost = std::string("localhost");
159 if (result.port() == base::IntToString(GetPort()) &&
160 result.host() == kLocalhostHost) {
164 GURL::Replacements replacements;
165 replacements.SetHostStr(kLocalhostHost);
167 const std::string port = std::string(
168 base::IntToString(static_cast<int>(GetPort())));
169 replacements.SetPortStr(port);
171 // It is necessary to prepend the host of the input URL so that URLs such
172 // as http://origin/foo, http://destination/foo can be disamgiguated.
173 const std::string new_path = std::string(result.host() + result.path());
174 replacements.SetPathStr(new_path);
176 return result.ReplaceComponents(replacements);
179 scoped_refptr<RefCountedResponseProviderWrapper>
180 HttpServer::GetResponseProviderForRequest(
181 const web::ResponseProvider::Request& request) {
182 base::AutoLock autolock(provider_list_lock_);
183 scoped_refptr<RefCountedResponseProviderWrapper> result;
184 for (const auto& ref_counted_response_provider : providers_) {
185 ResponseProvider* response_provider =
186 ref_counted_response_provider.get()->GetResponseProvider();
187 if (response_provider->CanHandleRequest(request)) {
189 "No more than one response provider can handle the same request.";
190 result = ref_counted_response_provider;
196 void HttpServer::AddResponseProvider(ResponseProvider* response_provider) {
197 DCHECK([NSThread isMainThread]);
198 DCHECK(IsRunning()) << "Can add a response provider only when the server is "
200 base::AutoLock autolock(provider_list_lock_);
201 scoped_refptr<RefCountedResponseProviderWrapper>
202 ref_counted_response_provider(
203 new RefCountedResponseProviderWrapper(response_provider));
204 providers_.push_back(ref_counted_response_provider);
207 void HttpServer::RemoveResponseProvider(ResponseProvider* response_provider) {
208 DCHECK([NSThread isMainThread]);
209 base::AutoLock autolock(provider_list_lock_);
210 auto found_iter = providers_.end();
211 for (auto it = providers_.begin(); it != providers_.end(); ++it) {
212 if ((*it)->GetResponseProvider() == response_provider) {
217 if (found_iter != providers_.end()) {
218 providers_.erase(found_iter);
222 void HttpServer::RemoveAllResponseProviders() {
223 DCHECK([NSThread isMainThread]);
224 base::AutoLock autolock(provider_list_lock_);
228 void HttpServer::SetPort(NSUInteger port) {
229 base::AutoLock autolock(port_lock_);
233 NSUInteger HttpServer::GetPort() const {
234 base::AutoLock autolock(port_lock_);