Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Side by Side Diff: chrome/test/webdriver/webdriver_dispatch.cc

Issue 10827362: Fix ChromeDriver flakiness with the C# webdriver bindings. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « chrome/test/webdriver/http_response_unittest.cc ('k') | chrome/test/webdriver/webdriver_dispatch_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698