OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/test/webdriver/webdriver_dispatch.h" | 5 #include "chrome/test/webdriver/webdriver_dispatch.h" |
6 | 6 |
7 #include <sstream> | 7 #include <sstream> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
| 11 #include "base/command_line.h" |
11 #include "base/format_macros.h" | 12 #include "base/format_macros.h" |
12 #include "base/json/json_reader.h" | 13 #include "base/json/json_reader.h" |
13 #include "base/logging.h" | 14 #include "base/logging.h" |
14 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
15 #include "base/message_loop_proxy.h" | 16 #include "base/message_loop_proxy.h" |
16 #include "base/string_split.h" | 17 #include "base/string_split.h" |
17 #include "base/string_util.h" | 18 #include "base/string_util.h" |
18 #include "base/stringprintf.h" | 19 #include "base/stringprintf.h" |
19 #include "base/synchronization/waitable_event.h" | 20 #include "base/synchronization/waitable_event.h" |
20 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
21 #include "base/threading/platform_thread.h" | 22 #include "base/threading/platform_thread.h" |
22 #include "base/threading/thread.h" | 23 #include "base/threading/thread.h" |
23 #include "chrome/common/chrome_version_info.h" | 24 #include "chrome/common/chrome_version_info.h" |
24 #include "chrome/test/webdriver/commands/command.h" | 25 #include "chrome/test/webdriver/commands/command.h" |
25 #include "chrome/test/webdriver/http_response.h" | 26 #include "chrome/test/webdriver/http_response.h" |
26 #include "chrome/test/webdriver/webdriver_logging.h" | 27 #include "chrome/test/webdriver/webdriver_logging.h" |
27 #include "chrome/test/webdriver/webdriver_session_manager.h" | 28 #include "chrome/test/webdriver/webdriver_session_manager.h" |
| 29 #include "chrome/test/webdriver/webdriver_switches.h" |
28 #include "chrome/test/webdriver/webdriver_util.h" | 30 #include "chrome/test/webdriver/webdriver_util.h" |
29 | 31 |
30 namespace webdriver { | 32 namespace webdriver { |
31 | 33 |
32 namespace { | 34 namespace { |
33 | 35 |
34 // Maximum safe size of HTTP response message. Any larger than this, | 36 // Maximum safe size of HTTP response message. Any larger than this, |
35 // the message may not be transferred at all. | 37 // the message may not be transferred at all. |
36 const size_t kMaxHttpMessageSize = 1024 * 1024 * 16; // 16MB | 38 const size_t kMaxHttpMessageSize = 1024 * 1024 * 16; // 16MB |
37 | 39 |
38 bool ForbidsMessageBody(const std::string& request_method, | |
39 const HttpResponse& response) { | |
40 return request_method == "HEAD" || | |
41 response.status() == HttpResponse::kNoContent || | |
42 response.status() == HttpResponse::kNotModified || | |
43 (response.status() >= 100 && response.status() < 200); | |
44 } | |
45 | |
46 void ReadRequestBody(const struct mg_request_info* const request_info, | 40 void ReadRequestBody(const struct mg_request_info* const request_info, |
47 struct mg_connection* const connection, | 41 struct mg_connection* const connection, |
48 std::string* request_body) { | 42 std::string* request_body) { |
49 int content_length = 0; | 43 int content_length = 0; |
50 // 64 maximum header count hard-coded in mongoose.h | 44 // 64 maximum header count hard-coded in mongoose.h |
51 for (int header_index = 0; header_index < 64; ++header_index) { | 45 for (int header_index = 0; header_index < 64; ++header_index) { |
52 if (request_info->http_headers[header_index].name == NULL) { | 46 if (request_info->http_headers[header_index].name == NULL) { |
53 break; | 47 break; |
54 } | 48 } |
55 if (strcmp(request_info->http_headers[header_index].name, | 49 if (strcmp(request_info->http_headers[header_index].name, |
56 "Content-Length") == 0) { | 50 "Content-Length") == 0) { |
57 content_length = atoi(request_info->http_headers[header_index].value); | 51 content_length = atoi(request_info->http_headers[header_index].value); |
58 break; | 52 break; |
59 } | 53 } |
60 } | 54 } |
61 if (content_length > 0) { | 55 if (content_length > 0) { |
62 request_body->resize(content_length); | 56 request_body->resize(content_length); |
63 int bytes_read = 0; | 57 int bytes_read = 0; |
64 while (bytes_read < content_length) { | 58 while (bytes_read < content_length) { |
65 bytes_read += mg_read(connection, | 59 bytes_read += mg_read(connection, |
66 &(*request_body)[bytes_read], | 60 &(*request_body)[bytes_read], |
67 content_length - bytes_read); | 61 content_length - bytes_read); |
68 } | 62 } |
69 } | 63 } |
70 } | 64 } |
71 | 65 |
| 66 void WriteHttpResponse(struct mg_connection* connection, |
| 67 const HttpResponse& response) { |
| 68 HttpResponse modified_response(response); |
| 69 if (!CommandLine::ForCurrentProcess()->HasSwitch(kEnableKeepAlive)) |
| 70 modified_response.AddHeader("connection", "close"); |
| 71 std::string data; |
| 72 modified_response.GetData(&data); |
| 73 mg_write(connection, data.data(), data.length()); |
| 74 } |
| 75 |
72 void DispatchCommand(Command* const command, | 76 void DispatchCommand(Command* const command, |
73 const std::string& method, | 77 const std::string& method, |
74 Response* response) { | 78 Response* response) { |
75 if (!command->Init(response)) | 79 if (!command->Init(response)) |
76 return; | 80 return; |
77 | 81 |
78 if (method == "POST") { | 82 if (method == "POST") { |
79 command->ExecutePost(response); | 83 command->ExecutePost(response); |
80 } else if (method == "GET") { | 84 } else if (method == "GET") { |
81 command->ExecuteGet(response); | 85 command->ExecuteGet(response); |
82 } else if (method == "DELETE") { | 86 } else if (method == "DELETE") { |
83 command->ExecuteDelete(response); | 87 command->ExecuteDelete(response); |
84 } else { | 88 } else { |
85 NOTREACHED(); | 89 NOTREACHED(); |
86 } | 90 } |
87 command->Finish(response); | 91 command->Finish(response); |
88 } | 92 } |
89 | 93 |
90 void SendOkWithBody(struct mg_connection* connection, | 94 void SendOkWithBody(struct mg_connection* connection, |
91 const std::string& content) { | 95 const std::string& content) { |
92 const char* response_fmt = "HTTP/1.1 200 OK\r\n" | 96 HttpResponse response; |
93 "Content-Length:%d\r\n\r\n" | 97 response.set_body(content); |
94 "%s"; | 98 WriteHttpResponse(connection, response); |
95 std::string response = base::StringPrintf( | |
96 response_fmt, content.length(), content.c_str()); | |
97 mg_write(connection, response.data(), response.length()); | |
98 } | 99 } |
99 | 100 |
100 void Shutdown(struct mg_connection* connection, | 101 void Shutdown(struct mg_connection* connection, |
101 const struct mg_request_info* request_info, | 102 const struct mg_request_info* request_info, |
102 void* user_data) { | 103 void* user_data) { |
103 base::WaitableEvent* shutdown_event = | 104 base::WaitableEvent* shutdown_event = |
104 reinterpret_cast<base::WaitableEvent*>(user_data); | 105 reinterpret_cast<base::WaitableEvent*>(user_data); |
105 mg_printf(connection, "HTTP/1.1 200 OK\r\n\r\n\r\n"); | 106 WriteHttpResponse(connection, HttpResponse()); |
106 shutdown_event->Signal(); | 107 shutdown_event->Signal(); |
107 } | 108 } |
108 | 109 |
109 void SendStatus(struct mg_connection* connection, | 110 void SendStatus(struct mg_connection* connection, |
110 const struct mg_request_info* request_info, | 111 const struct mg_request_info* request_info, |
111 void* user_data) { | 112 void* user_data) { |
112 chrome::VersionInfo version_info; | 113 chrome::VersionInfo version_info; |
113 DictionaryValue* build_info = new DictionaryValue; | 114 DictionaryValue* build_info = new DictionaryValue; |
114 build_info->SetString("time", | 115 build_info->SetString("time", |
115 base::StringPrintf("%s %s PST", __DATE__, __TIME__)); | 116 base::StringPrintf("%s %s PST", __DATE__, __TIME__)); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 | 155 |
155 void SimulateHang(struct mg_connection* connection, | 156 void SimulateHang(struct mg_connection* connection, |
156 const struct mg_request_info* request_info, | 157 const struct mg_request_info* request_info, |
157 void* user_data) { | 158 void* user_data) { |
158 base::PlatformThread::Sleep(base::TimeDelta::FromMinutes(5)); | 159 base::PlatformThread::Sleep(base::TimeDelta::FromMinutes(5)); |
159 } | 160 } |
160 | 161 |
161 void SendNoContentResponse(struct mg_connection* connection, | 162 void SendNoContentResponse(struct mg_connection* connection, |
162 const struct mg_request_info* request_info, | 163 const struct mg_request_info* request_info, |
163 void* user_data) { | 164 void* user_data) { |
164 std::string response = "HTTP/1.1 204 No Content\r\n" | 165 WriteHttpResponse(connection, HttpResponse(HttpResponse::kNoContent)); |
165 "Content-Length:0\r\n" | |
166 "\r\n"; | |
167 mg_write(connection, response.data(), response.length()); | |
168 } | 166 } |
169 | 167 |
170 void SendForbidden(struct mg_connection* connection, | 168 void SendForbidden(struct mg_connection* connection, |
171 const struct mg_request_info* request_info, | 169 const struct mg_request_info* request_info, |
172 void* user_data) { | 170 void* user_data) { |
173 mg_printf(connection, "HTTP/1.1 403 Forbidden\r\n\r\n"); | 171 WriteHttpResponse(connection, HttpResponse(HttpResponse::kForbidden)); |
174 } | 172 } |
175 | 173 |
176 void SendNotImplementedError(struct mg_connection* connection, | 174 void SendNotImplementedError(struct mg_connection* connection, |
177 const struct mg_request_info* request_info, | 175 const struct mg_request_info* request_info, |
178 void* user_data) { | 176 void* user_data) { |
179 // Send a well-formed WebDriver JSON error response to ensure clients | 177 // Send a well-formed WebDriver JSON error response to ensure clients |
180 // handle it correctly. | 178 // handle it correctly. |
181 std::string body = base::StringPrintf( | 179 std::string body = base::StringPrintf( |
182 "{\"status\":%d,\"value\":{\"message\":" | 180 "{\"status\":%d,\"value\":{\"message\":" |
183 "\"Command has not been implemented yet: %s %s\"}}", | 181 "\"Command has not been implemented yet: %s %s\"}}", |
184 kUnknownCommand, request_info->request_method, request_info->uri); | 182 kUnknownCommand, request_info->request_method, request_info->uri); |
185 | 183 |
186 std::string header = base::StringPrintf( | 184 HttpResponse response(HttpResponse::kNotImplemented); |
187 "HTTP/1.1 501 Not Implemented\r\n" | 185 response.AddHeader("Content-Type", "application/json"); |
188 "Content-Type:application/json\r\n" | 186 response.set_body(body); |
189 "Content-Length:%" PRIuS "\r\n" | 187 WriteHttpResponse(connection, response); |
190 "\r\n", body.length()); | |
191 | |
192 mg_write(connection, header.data(), header.length()); | |
193 mg_write(connection, body.data(), body.length()); | |
194 } | 188 } |
195 | 189 |
196 } // namespace | 190 } // namespace |
197 | 191 |
198 namespace internal { | 192 namespace internal { |
199 | 193 |
200 void PrepareHttpResponse(const Response& command_response, | 194 void PrepareHttpResponse(const Response& command_response, |
201 HttpResponse* const http_response) { | 195 HttpResponse* const http_response) { |
202 ErrorCode status = command_response.GetStatus(); | 196 ErrorCode status = command_response.GetStatus(); |
203 switch (status) { | 197 switch (status) { |
204 case kSuccess: | 198 case kSuccess: |
205 http_response->set_status(HttpResponse::kOk); | 199 http_response->set_status(HttpResponse::kOk); |
206 break; | 200 break; |
207 | 201 |
208 // TODO(jleyba): kSeeOther, kBadRequest, kSessionNotFound, | 202 // TODO(jleyba): kSeeOther, kBadRequest, kSessionNotFound, |
209 // and kMethodNotAllowed should be detected before creating | 203 // and kMethodNotAllowed should be detected before creating |
210 // a command_response, and should thus not need conversion. | 204 // a command_response, and should thus not need conversion. |
211 case kSeeOther: { | 205 case kSeeOther: { |
212 const Value* const value = command_response.GetValue(); | 206 const Value* const value = command_response.GetValue(); |
213 std::string location; | 207 std::string location; |
214 if (!value->GetAsString(&location)) { | 208 if (!value->GetAsString(&location)) { |
215 // This should never happen. | 209 // This should never happen. |
216 http_response->set_status(HttpResponse::kInternalServerError); | 210 http_response->set_status(HttpResponse::kInternalServerError); |
217 http_response->SetBody("Unable to set 'Location' header: response " | 211 http_response->set_body("Unable to set 'Location' header: response " |
218 "value is not a string: " + | 212 "value is not a string: " + |
219 command_response.ToJSON()); | 213 command_response.ToJSON()); |
220 return; | 214 return; |
221 } | 215 } |
222 http_response->AddHeader("Location", location); | 216 http_response->AddHeader("Location", location); |
223 http_response->set_status(HttpResponse::kSeeOther); | 217 http_response->set_status(HttpResponse::kSeeOther); |
224 break; | 218 break; |
225 } | 219 } |
226 | 220 |
227 case kBadRequest: | 221 case kBadRequest: |
228 case kSessionNotFound: | 222 case kSessionNotFound: |
229 http_response->set_status(status); | 223 http_response->set_status(status); |
230 break; | 224 break; |
231 | 225 |
232 case kMethodNotAllowed: { | 226 case kMethodNotAllowed: { |
233 const Value* const value = command_response.GetValue(); | 227 const Value* const value = command_response.GetValue(); |
234 if (!value->IsType(Value::TYPE_LIST)) { | 228 if (!value->IsType(Value::TYPE_LIST)) { |
235 // This should never happen. | 229 // This should never happen. |
236 http_response->set_status(HttpResponse::kInternalServerError); | 230 http_response->set_status(HttpResponse::kInternalServerError); |
237 http_response->SetBody( | 231 http_response->set_body( |
238 "Unable to set 'Allow' header: response value was " | 232 "Unable to set 'Allow' header: response value was " |
239 "not a list of strings: " + command_response.ToJSON()); | 233 "not a list of strings: " + command_response.ToJSON()); |
240 return; | 234 return; |
241 } | 235 } |
242 | 236 |
243 const ListValue* const list_value = | 237 const ListValue* const list_value = |
244 static_cast<const ListValue* const>(value); | 238 static_cast<const ListValue* const>(value); |
245 std::vector<std::string> allowed_methods; | 239 std::vector<std::string> allowed_methods; |
246 for (size_t i = 0; i < list_value->GetSize(); ++i) { | 240 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
247 std::string method; | 241 std::string method; |
248 if (list_value->GetString(i, &method)) { | 242 if (list_value->GetString(i, &method)) { |
249 allowed_methods.push_back(method); | 243 allowed_methods.push_back(method); |
250 } else { | 244 } else { |
251 // This should never happen. | 245 // This should never happen. |
252 http_response->set_status(HttpResponse::kInternalServerError); | 246 http_response->set_status(HttpResponse::kInternalServerError); |
253 http_response->SetBody( | 247 http_response->set_body( |
254 "Unable to set 'Allow' header: response value was " | 248 "Unable to set 'Allow' header: response value was " |
255 "not a list of strings: " + command_response.ToJSON()); | 249 "not a list of strings: " + command_response.ToJSON()); |
256 return; | 250 return; |
257 } | 251 } |
258 } | 252 } |
259 http_response->AddHeader("Allow", JoinString(allowed_methods, ',')); | 253 http_response->AddHeader("Allow", JoinString(allowed_methods, ',')); |
260 http_response->set_status(HttpResponse::kMethodNotAllowed); | 254 http_response->set_status(HttpResponse::kMethodNotAllowed); |
261 break; | 255 break; |
262 } | 256 } |
263 | 257 |
264 // All other errors should be treated as generic 500s. The client | 258 // All other errors should be treated as generic 500s. The client |
265 // will be responsible for inspecting the message body for details. | 259 // will be responsible for inspecting the message body for details. |
266 case kInternalServerError: | 260 case kInternalServerError: |
267 default: | 261 default: |
268 http_response->set_status(HttpResponse::kInternalServerError); | 262 http_response->set_status(HttpResponse::kInternalServerError); |
269 break; | 263 break; |
270 } | 264 } |
271 | 265 |
272 http_response->SetMimeType("application/json; charset=utf-8"); | 266 http_response->SetMimeType("application/json; charset=utf-8"); |
273 http_response->SetBody(command_response.ToJSON()); | 267 http_response->set_body(command_response.ToJSON()); |
274 } | 268 } |
275 | 269 |
276 void SendResponse(struct mg_connection* const connection, | 270 void SendResponse(struct mg_connection* const connection, |
277 const std::string& request_method, | 271 const std::string& request_method, |
278 const Response& response) { | 272 const Response& response) { |
279 HttpResponse http_response; | 273 HttpResponse http_response; |
280 PrepareHttpResponse(response, &http_response); | 274 PrepareHttpResponse(response, &http_response); |
281 | 275 WriteHttpResponse(connection, http_response); |
282 std::string message_header = base::StringPrintf("HTTP/1.1 %d %s\r\n", | |
283 http_response.status(), http_response.GetReasonPhrase().c_str()); | |
284 | |
285 typedef HttpResponse::HeaderMap::const_iterator HeaderIter; | |
286 for (HeaderIter header = http_response.headers()->begin(); | |
287 header != http_response.headers()->end(); | |
288 ++header) { | |
289 message_header.append(base::StringPrintf("%s:%s\r\n", | |
290 header->first.c_str(), header->second.c_str())); | |
291 } | |
292 message_header.append("\r\n"); | |
293 | |
294 mg_write(connection, message_header.data(), message_header.length()); | |
295 if (!ForbidsMessageBody(request_method, http_response)) | |
296 mg_write(connection, http_response.data(), http_response.length()); | |
297 } | 276 } |
298 | 277 |
299 bool ParseRequestInfo(const struct mg_request_info* const request_info, | 278 bool ParseRequestInfo(const struct mg_request_info* const request_info, |
300 struct mg_connection* const connection, | 279 struct mg_connection* const connection, |
301 std::string* method, | 280 std::string* method, |
302 std::vector<std::string>* path_segments, | 281 std::vector<std::string>* path_segments, |
303 DictionaryValue** parameters, | 282 DictionaryValue** parameters, |
304 Response* const response) { | 283 Response* const response) { |
305 *method = request_info->request_method; | 284 *method = request_info->request_method; |
306 if (*method == "HEAD") | 285 if (*method == "HEAD") |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 ++callback) { | 397 ++callback) { |
419 if (MatchPattern(request_info->uri, callback->uri_regex_)) { | 398 if (MatchPattern(request_info->uri, callback->uri_regex_)) { |
420 callback->func_(connection, request_info, callback->user_data_); | 399 callback->func_(connection, request_info, callback->user_data_); |
421 return true; | 400 return true; |
422 } | 401 } |
423 } | 402 } |
424 return false; | 403 return false; |
425 } | 404 } |
426 | 405 |
427 } // namespace webdriver | 406 } // namespace webdriver |
OLD | NEW |