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 |