| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #import "ios/web/public/test/http_server.h" | |
| 6 | |
| 7 #import <Foundation/Foundation.h> | |
| 8 | |
| 9 #include <string> | |
| 10 | |
| 11 #include "base/logging.h" | |
| 12 #include "base/memory/ref_counted.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/strings/sys_string_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/GCDWebServerData
Request.h" | |
| 18 #import "net/base/mac/url_conversions.h" | |
| 19 | |
| 20 #include "url/gurl.h" | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // The default port on which the GCDWebServer is brought up on. | |
| 25 const NSUInteger kDefaultPort = 8080; | |
| 26 | |
| 27 // Converts a GCDWebServerDataRequest (received from the GCDWebServer servlet) | |
| 28 // to a request object that the ResponseProvider expects. | |
| 29 web::ResponseProvider::Request ResponseProviderRequestFromGCDWebServerRequest( | |
| 30 GCDWebServerDataRequest* request) { | |
| 31 GURL url(net::GURLWithNSURL(request.URL)); | |
| 32 std::string method(base::SysNSStringToUTF8(request.method)); | |
| 33 base::scoped_nsobject<NSString> body( | |
| 34 [[NSString alloc] initWithData:request.data | |
| 35 encoding:NSUTF8StringEncoding]); | |
| 36 __block net::HttpRequestHeaders headers; | |
| 37 [[request headers] enumerateKeysAndObjectsUsingBlock:^(NSString* header_key, | |
| 38 NSString* header_value, | |
| 39 BOOL*) { | |
| 40 headers.SetHeader(base::SysNSStringToUTF8(header_key), | |
| 41 base::SysNSStringToUTF8(header_value)); | |
| 42 }]; | |
| 43 return web::ResponseProvider::Request(url, method, | |
| 44 base::SysNSStringToUTF8(body), headers); | |
| 45 } | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 namespace web { | |
| 50 namespace test { | |
| 51 | |
| 52 RefCountedResponseProviderWrapper::RefCountedResponseProviderWrapper( | |
| 53 std::unique_ptr<ResponseProvider> response_provider) | |
| 54 : response_provider_(std::move(response_provider)) {} | |
| 55 | |
| 56 RefCountedResponseProviderWrapper::~RefCountedResponseProviderWrapper() {} | |
| 57 | |
| 58 // static | |
| 59 HttpServer& HttpServer::GetSharedInstance() { | |
| 60 static web::test::HttpServer* shared_instance = nullptr; | |
| 61 static dispatch_once_t once; | |
| 62 dispatch_once(&once, ^{ | |
| 63 shared_instance = new HttpServer(); | |
| 64 }); | |
| 65 return *shared_instance; | |
| 66 } | |
| 67 | |
| 68 // static | |
| 69 HttpServer& HttpServer::GetSharedInstanceWithResponseProviders( | |
| 70 ProviderList response_providers) { | |
| 71 DCHECK([NSThread isMainThread]); | |
| 72 HttpServer& server = HttpServer::GetSharedInstance(); | |
| 73 // Use non-const reference as the response_provider ownership is transfered. | |
| 74 for (std::unique_ptr<ResponseProvider>& provider : response_providers) | |
| 75 server.AddResponseProvider(std::move(provider)); | |
| 76 return server; | |
| 77 } | |
| 78 | |
| 79 void HttpServer::InitHttpServer() { | |
| 80 DCHECK(gcd_web_server_); | |
| 81 // Note: This block is called from an arbitrary GCD thread. | |
| 82 id process_request = | |
| 83 ^GCDWebServerResponse*(GCDWebServerDataRequest* request) { | |
| 84 // Relax the cross-thread access restriction to non-thread-safe RefCount. | |
| 85 // TODO(crbug.com/707010): Remove ScopedAllowCrossThreadRefCountAccess. | |
| 86 base::ScopedAllowCrossThreadRefCountAccess | |
| 87 allow_cross_thread_ref_count_access; | |
| 88 | |
| 89 ResponseProvider::Request provider_request = | |
| 90 ResponseProviderRequestFromGCDWebServerRequest(request); | |
| 91 scoped_refptr<RefCountedResponseProviderWrapper> | |
| 92 ref_counted_response_provider = GetResponseProviderForRequest( | |
| 93 provider_request); | |
| 94 | |
| 95 if (!ref_counted_response_provider) { | |
| 96 return [GCDWebServerResponse response]; | |
| 97 } | |
| 98 ResponseProvider* response_provider = | |
| 99 ref_counted_response_provider->GetResponseProvider(); | |
| 100 if (!response_provider) { | |
| 101 return [GCDWebServerResponse response]; | |
| 102 } | |
| 103 | |
| 104 return response_provider->GetGCDWebServerResponse(provider_request); | |
| 105 }; | |
| 106 [gcd_web_server_ removeAllHandlers]; | |
| 107 // Register a servlet for all HTTP GET, POST methods. | |
| 108 [gcd_web_server_ addDefaultHandlerForMethod:@"GET" | |
| 109 requestClass:[GCDWebServerDataRequest class] | |
| 110 processBlock:process_request]; | |
| 111 [gcd_web_server_ addDefaultHandlerForMethod:@"POST" | |
| 112 requestClass:[GCDWebServerDataRequest class] | |
| 113 processBlock:process_request]; | |
| 114 } | |
| 115 | |
| 116 HttpServer::HttpServer() : port_(0) { | |
| 117 gcd_web_server_.reset([[GCDWebServer alloc] init]); | |
| 118 InitHttpServer(); | |
| 119 } | |
| 120 | |
| 121 HttpServer::~HttpServer() { | |
| 122 } | |
| 123 | |
| 124 bool HttpServer::StartOnPort(NSUInteger port) { | |
| 125 DCHECK([NSThread isMainThread]); | |
| 126 DCHECK(!IsRunning()) << "The server is already running." | |
| 127 << " Please stop it before starting it again."; | |
| 128 BOOL success = [gcd_web_server_ startWithPort:port bonjourName:@""]; | |
| 129 if (success) { | |
| 130 SetPort(port); | |
| 131 } | |
| 132 return success; | |
| 133 } | |
| 134 | |
| 135 void HttpServer::StartOrDie() { | |
| 136 DCHECK([NSThread isMainThread]); | |
| 137 StartOnPort(kDefaultPort); | |
| 138 CHECK(IsRunning()); | |
| 139 } | |
| 140 | |
| 141 void HttpServer::Stop() { | |
| 142 DCHECK([NSThread isMainThread]); | |
| 143 DCHECK(IsRunning()) << "Cannot stop an already stopped server."; | |
| 144 RemoveAllResponseProviders(); | |
| 145 [gcd_web_server_ stop]; | |
| 146 SetPort(0); | |
| 147 } | |
| 148 | |
| 149 bool HttpServer::IsRunning() const { | |
| 150 DCHECK([NSThread isMainThread]); | |
| 151 return [gcd_web_server_ isRunning]; | |
| 152 } | |
| 153 | |
| 154 NSUInteger HttpServer::GetPort() const { | |
| 155 base::AutoLock autolock(port_lock_); | |
| 156 return port_; | |
| 157 } | |
| 158 | |
| 159 // static | |
| 160 GURL HttpServer::MakeUrl(const std::string &url) { | |
| 161 return HttpServer::GetSharedInstance().MakeUrlForHttpServer(url); | |
| 162 } | |
| 163 | |
| 164 GURL HttpServer::MakeUrlForHttpServer(const std::string& url) const { | |
| 165 GURL result(url); | |
| 166 DCHECK(result.is_valid()); | |
| 167 const std::string kLocalhostHost = std::string("localhost"); | |
| 168 if (result.port() == base::IntToString(GetPort()) && | |
| 169 result.host() == kLocalhostHost) { | |
| 170 return result; | |
| 171 } | |
| 172 | |
| 173 GURL::Replacements replacements; | |
| 174 replacements.SetHostStr(kLocalhostHost); | |
| 175 | |
| 176 const std::string port = std::string( | |
| 177 base::IntToString(static_cast<int>(GetPort()))); | |
| 178 replacements.SetPortStr(port); | |
| 179 | |
| 180 // It is necessary to prepend the host of the input URL so that URLs such | |
| 181 // as http://origin/foo, http://destination/foo can be disamgiguated. | |
| 182 const std::string new_path = std::string(result.host() + result.path()); | |
| 183 replacements.SetPathStr(new_path); | |
| 184 | |
| 185 return result.ReplaceComponents(replacements); | |
| 186 } | |
| 187 | |
| 188 scoped_refptr<RefCountedResponseProviderWrapper> | |
| 189 HttpServer::GetResponseProviderForRequest( | |
| 190 const web::ResponseProvider::Request& request) { | |
| 191 base::AutoLock autolock(provider_list_lock_); | |
| 192 // Relax the cross-thread access restriction to non-thread-safe RefCount. | |
| 193 // The lock above protects non-thread-safe RefCount in HTTPServer. | |
| 194 base::ScopedAllowCrossThreadRefCountAccess | |
| 195 allow_cross_thread_ref_count_access; | |
| 196 scoped_refptr<RefCountedResponseProviderWrapper> result; | |
| 197 for (const auto& ref_counted_response_provider : providers_) { | |
| 198 ResponseProvider* response_provider = | |
| 199 ref_counted_response_provider.get()->GetResponseProvider(); | |
| 200 if (response_provider->CanHandleRequest(request)) { | |
| 201 DCHECK(!result) << | |
| 202 "No more than one response provider can handle the same request."; | |
| 203 result = ref_counted_response_provider; | |
| 204 } | |
| 205 } | |
| 206 return result; | |
| 207 } | |
| 208 | |
| 209 void HttpServer::AddResponseProvider( | |
| 210 std::unique_ptr<ResponseProvider> response_provider) { | |
| 211 DCHECK([NSThread isMainThread]); | |
| 212 DCHECK(IsRunning()) << "Can add a response provider only when the server is " | |
| 213 << "running."; | |
| 214 base::AutoLock autolock(provider_list_lock_); | |
| 215 // Relax the cross-thread access restriction to non-thread-safe RefCount. | |
| 216 // The lock above protects non-thread-safe RefCount in HTTPServer. | |
| 217 base::ScopedAllowCrossThreadRefCountAccess | |
| 218 allow_cross_thread_ref_count_access; | |
| 219 scoped_refptr<RefCountedResponseProviderWrapper> | |
| 220 ref_counted_response_provider( | |
| 221 new RefCountedResponseProviderWrapper(std::move(response_provider))); | |
| 222 providers_.push_back(ref_counted_response_provider); | |
| 223 } | |
| 224 | |
| 225 void HttpServer::RemoveResponseProvider(ResponseProvider* response_provider) { | |
| 226 DCHECK([NSThread isMainThread]); | |
| 227 base::AutoLock autolock(provider_list_lock_); | |
| 228 // Relax the cross-thread access restriction to non-thread-safe RefCount. | |
| 229 // The lock above protects non-thread-safe RefCount in HTTPServer. | |
| 230 base::ScopedAllowCrossThreadRefCountAccess | |
| 231 allow_cross_thread_ref_count_access; | |
| 232 auto found_iter = providers_.end(); | |
| 233 for (auto it = providers_.begin(); it != providers_.end(); ++it) { | |
| 234 if ((*it)->GetResponseProvider() == response_provider) { | |
| 235 found_iter = it; | |
| 236 break; | |
| 237 } | |
| 238 } | |
| 239 if (found_iter != providers_.end()) { | |
| 240 providers_.erase(found_iter); | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 void HttpServer::RemoveAllResponseProviders() { | |
| 245 DCHECK([NSThread isMainThread]); | |
| 246 base::AutoLock autolock(provider_list_lock_); | |
| 247 // Relax the cross-thread access restriction to non-thread-safe RefCount. | |
| 248 // The lock above protects non-thread-safe RefCount in HTTPServer. | |
| 249 base::ScopedAllowCrossThreadRefCountAccess | |
| 250 allow_cross_thread_ref_count_access; | |
| 251 providers_.clear(); | |
| 252 } | |
| 253 | |
| 254 void HttpServer::SetPort(NSUInteger port) { | |
| 255 base::AutoLock autolock(port_lock_); | |
| 256 port_ = port; | |
| 257 } | |
| 258 | |
| 259 } // namespace test | |
| 260 } // namespace web | |
| OLD | NEW |