Upstream WebController/WebStateImpl in ios/web/
[chromium-blink-merge.git] / ios / web / public / test / http_server.mm
blobd54b58c5f3d1f30b4f53ff7d6aefe74d3a550e35
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>
9 #include <string>
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"
19 #include "url/gurl.h"
21 namespace {
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,
38                                                          BOOL*) {
39       headers.SetHeader(base::SysNSStringToUTF8(header_key),
40                         base::SysNSStringToUTF8(header_value));
41   }];
42   return web::ResponseProvider::Request(url, method,
43                                         base::SysNSStringToUTF8(body), headers);
46 }  // namespace
48 namespace web {
49 namespace test {
52 RefCountedResponseProviderWrapper::RefCountedResponseProviderWrapper(
53     ResponseProvider* response_provider) {
54   response_provider_.reset(response_provider);
57 RefCountedResponseProviderWrapper::~RefCountedResponseProviderWrapper() {}
59 // static
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();
65   });
66   return *shared_instance;
69 // static
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);
76   }
77   return server;
80 void HttpServer::InitHttpServer() {
81   DCHECK(gcd_web_server_);
82   // Note: This block is called from an arbitrary GCD thread.
83   id process_request =
84       ^GCDWebServerResponse*(GCDWebServerDataRequest* request) {
85       ResponseProvider::Request provider_request =
86           ResponseProviderRequestFromGCDWebServerRequest(request);
87       scoped_refptr<RefCountedResponseProviderWrapper>
88           ref_counted_response_provider = GetResponseProviderForRequest(
89               provider_request);
91       if (!ref_counted_response_provider) {
92         return [GCDWebServerResponse response];
93       }
94       ResponseProvider* response_provider =
95           ref_counted_response_provider->GetResponseProvider();
96       if (!response_provider) {
97         return [GCDWebServerResponse response];
98       }
100       return response_provider->GetGCDWebServerResponse(provider_request);
101   };
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]);
114   InitHttpServer();
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:@""];
125   if (success) {
126     SetPort(port);
127   }
128   return success;
131 void HttpServer::StartOrDie() {
132   DCHECK([NSThread isMainThread]);
133   StartOnPort(kDefaultPort);
134   CHECK(IsRunning());
137 void HttpServer::Stop() {
138   DCHECK([NSThread isMainThread]);
139   DCHECK(IsRunning()) << "Cannot stop an already stopped server.";
140   RemoveAllResponseProviders();
141   [gcd_web_server_ stop];
142   SetPort(0);
145 bool HttpServer::IsRunning() const {
146   DCHECK([NSThread isMainThread]);
147   return [gcd_web_server_ isRunning];
150 // static
151 GURL HttpServer::MakeUrl(const std::string &url) {
152   return HttpServer::GetSharedInstance().MakeUrlForHttpServer(url);
155 GURL HttpServer::MakeUrlForHttpServer(const std::string& url) const {
156   GURL result(url);
157   DCHECK(result.is_valid());
158   const std::string kLocalhostHost = std::string("localhost");
159   if (result.port() == base::IntToString(GetPort()) &&
160       result.host() == kLocalhostHost) {
161     return result;
162   }
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)) {
188       DCHECK(!result) <<
189           "No more than one response provider can handle the same request.";
190       result = ref_counted_response_provider;
191     }
192   }
193   return result;
196 void HttpServer::AddResponseProvider(ResponseProvider* response_provider) {
197   DCHECK([NSThread isMainThread]);
198   DCHECK(IsRunning()) << "Can add a response provider only when the server is "
199                       << "running.";
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) {
213       found_iter = it;
214       break;
215     }
216   }
217   if (found_iter != providers_.end()) {
218     providers_.erase(found_iter);
219   }
222 void HttpServer::RemoveAllResponseProviders() {
223   DCHECK([NSThread isMainThread]);
224   base::AutoLock autolock(provider_list_lock_);
225   providers_.clear();
228 void HttpServer::SetPort(NSUInteger port) {
229   base::AutoLock autolock(port_lock_);
230   port_ = port;
233 NSUInteger HttpServer::GetPort() const {
234   base::AutoLock autolock(port_lock_);
235   return port_;
238 } // namespace test
239 } // namespace web