Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/cpp/webdriver-server/server.cc
2867 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
17
#include "server.h"
18
#include <algorithm>
19
#include <cstdio>
20
#include <cstring>
21
#include <sstream>
22
#include "session.h"
23
#include "errorcodes.h"
24
#include "uri_info.h"
25
#include "logging.h"
26
27
#define SERVER_DEFAULT_PAGE "<html><head><title>WebDriver</title></head><body><p id='main'>This is the initial start page for the WebDriver server.</p></body></html>"
28
#define SERVER_DEFAULT_WHITELIST "127.0.0.1"
29
#define SERVER_DEFAULT_BLACKLIST "-0.0.0.0/0"
30
#define HTML_CONTENT_TYPE "text/html"
31
#define JSON_CONTENT_TYPE "application/json"
32
33
#if defined(WINDOWS)
34
#include <cstdarg>
35
inline int wd_snprintf(char* str, size_t size, const char* format, ...) {
36
va_list args;
37
va_start(args, format);
38
int count = _vscprintf(format, args);
39
if (str != NULL && size > 0) {
40
count = _vsnprintf_s(str, size, _TRUNCATE, format, args);
41
}
42
va_end(args);
43
return count;
44
}
45
#define snprintf wd_snprintf
46
#endif
47
48
namespace webdriver {
49
50
Server::Server(const int port) {
51
this->Initialize(port, "", "", "", "");
52
}
53
54
Server::Server(const int port, const std::string& host) {
55
this->Initialize(port, host, "", "", "");
56
}
57
58
Server::Server(const int port,
59
const std::string& host,
60
const std::string& log_level,
61
const std::string& log_file) {
62
this->Initialize(port, host, log_level, log_file, "");
63
}
64
65
Server::Server(const int port,
66
const std::string& host,
67
const std::string& log_level,
68
const std::string& log_file,
69
const std::string& acl) {
70
this->Initialize(port, host, log_level, log_file, acl);
71
}
72
73
Server::~Server(void) {
74
SessionMap::iterator it = this->sessions_.begin();
75
for (; it != this->sessions_.end(); ++it) {
76
std::string session_id = it->first;
77
this->ShutDownSession(session_id);
78
}
79
}
80
81
void Server::Initialize(const int port,
82
const std::string& host,
83
const std::string& log_level,
84
const std::string& log_file,
85
const std::string& acl) {
86
LOG::Level(log_level);
87
LOG::File(log_file);
88
LOG(INFO) << "Starting WebDriver server on port: '"
89
<< port << "' on host: '" << host << "'";
90
this->port_ = port;
91
this->host_ = host;
92
if (acl.size() > 0) {
93
this->ProcessWhitelist(acl);
94
}
95
this->PopulateCommandRepository();
96
}
97
98
void Server::ProcessWhitelist(const std::string& whitelist) {
99
std::string input_copy = whitelist;
100
while (input_copy.size() > 0) {
101
size_t delimiter_pos = input_copy.find(",");
102
std::string token = input_copy.substr(0, delimiter_pos);
103
if (delimiter_pos == std::string::npos) {
104
input_copy = "";
105
} else {
106
input_copy = input_copy.substr(delimiter_pos + 1);
107
}
108
this->whitelist_.push_back(token);
109
}
110
}
111
112
std::string Server::GetListeningPorts(const bool use_ipv6) {
113
std::string port_format_string = "%s:%d";
114
if (this->host_.size() == 0) {
115
// If the host name is an empty string, then we want to bind
116
// to the local loopback address on both IPv4 and IPv6 if we
117
// can. Using the addresses in the listening port format string
118
// will prevent connection from external IP addresses.
119
port_format_string = "%s127.0.0.1:%d";
120
if (use_ipv6) {
121
port_format_string.append(",[::1]:%d");
122
}
123
} else if (this->whitelist_.size() > 0) {
124
// If there are white-listed IP addresses, we can only use IPv4,
125
// and we don't want the colon in the listening ports string.
126
// Instead, we want to bind to all adapters, and use the access
127
// control list to determine which addresses can connect. So to
128
// remove the host from the format string, when we use printf to
129
// format, the %s will be replaced by an empty string.
130
port_format_string = "%s%d";
131
}
132
int formatted_string_size = snprintf(NULL,
133
0,
134
port_format_string.c_str(),
135
this->host_.c_str(),
136
this->port_,
137
this->port_) + 1;
138
std::vector<char> listening_ports_buffer(formatted_string_size);
139
snprintf(&listening_ports_buffer[0],
140
formatted_string_size,
141
port_format_string.c_str(),
142
this->host_.c_str(),
143
this->port_,
144
this->port_);
145
return &listening_ports_buffer[0];
146
}
147
148
std::string Server::GetAccessControlList() {
149
std::string acl = "";
150
if (this->whitelist_.size() > 0) {
151
acl = SERVER_DEFAULT_BLACKLIST;
152
for (std::vector<std::string>::const_iterator it = this->whitelist_.begin();
153
it < this->whitelist_.end();
154
++it) {
155
acl.append(",+").append(*it);
156
}
157
LOG(DEBUG) << "Civetweb ACL is " << acl;
158
}
159
return acl;
160
}
161
162
void Server::GenerateOptionsList(std::vector<const char*>* options) {
163
std::map<std::string, std::string>::const_iterator it = this->options_.begin();
164
for (; it != this->options_.end(); ++it) {
165
options->push_back(it->first.c_str());
166
options->push_back(it->second.c_str());
167
}
168
options->push_back(NULL);
169
}
170
171
int Server::OnNewHttpRequest(struct mg_connection* conn) {
172
mg_context* context = mg_get_context(conn);
173
Server* current_server = reinterpret_cast<Server*>(mg_get_user_data(context));
174
const mg_request_info* request_info = mg_get_request_info(conn);
175
int handler_result_code = current_server->ProcessRequest(conn, request_info);
176
return handler_result_code;
177
}
178
179
bool Server::Start() {
180
LOG(TRACE) << "Entering Server::Start";
181
182
std::string listening_port_option = this->GetListeningPorts(true);
183
this->options_["listening_ports"] = listening_port_option;
184
185
std::string acl_option = this->GetAccessControlList();
186
if (acl_option.size() > 0) {
187
this->options_["access_control_list"] = acl_option;
188
}
189
190
this->options_["enable_keep_alive"] = "yes";
191
192
std::vector<const char*> options;
193
this->GenerateOptionsList(&options);
194
195
mg_callbacks callbacks = {};
196
callbacks.begin_request = &OnNewHttpRequest;
197
context_ = mg_start(&callbacks, this, &options[0]);
198
if (context_ == NULL) {
199
std::string ipv4_port_option = this->GetListeningPorts(false);
200
if (listening_port_option == ipv4_port_option) {
201
// If the IPv4 and IPv6 versions of the port option string
202
// are equal, then either a host to bind to or an ACL was
203
// specified, so there is no need to retry.
204
LOG(WARN) << "Failed to start Civetweb";
205
return false;
206
} else {
207
// If we fail, a host and ACL aren't specified, we might not
208
// be able to bind to an IPv6 address. Try again to bind to
209
// the IPv4 loopback only.
210
LOG(INFO) << "Failed first attempt to start Civetweb. Attempt start with IPv4 only";
211
this->options_["listening_ports"] = listening_port_option;
212
options.clear();
213
this->GenerateOptionsList(&options);
214
context_ = mg_start(&callbacks, this, &options[0]);
215
if (context_ == NULL) {
216
LOG(WARN) << "Failed to start Civetweb";
217
return false;
218
}
219
}
220
}
221
return true;
222
}
223
224
void Server::Stop() {
225
LOG(TRACE) << "Entering Server::Stop";
226
if (context_) {
227
mg_stop(context_);
228
context_ = NULL;
229
}
230
}
231
232
int Server::ProcessRequest(struct mg_connection* conn,
233
const struct mg_request_info* request_info) {
234
LOG(TRACE) << "Entering Server::ProcessRequest";
235
236
int http_response_code = 0;
237
std::string http_verb = request_info->request_method;
238
std::string request_body = "{}";
239
if (http_verb == "POST") {
240
request_body = this->ReadRequestBody(conn, request_info);
241
}
242
243
LOG(TRACE) << "Process request with:"
244
<< " URI: " << request_info->local_uri
245
<< " HTTP verb: " << http_verb << std::endl
246
<< "body: " << request_body;
247
248
if (strcmp(request_info->local_uri, "/") == 0) {
249
this->SendHttpOk(conn,
250
request_info,
251
SERVER_DEFAULT_PAGE,
252
HTML_CONTENT_TYPE);
253
http_response_code = 0;
254
} else if (strcmp(request_info->local_uri, "/shutdown") == 0) {
255
this->SendHttpOk(conn,
256
request_info,
257
SERVER_DEFAULT_PAGE,
258
HTML_CONTENT_TYPE);
259
http_response_code = 0;
260
this->ShutDown();
261
} else {
262
std::string serialized_response = this->DispatchCommand(request_info->local_uri,
263
http_verb,
264
request_body);
265
http_response_code = this->SendResponseToClient(conn,
266
request_info,
267
serialized_response);
268
}
269
270
return http_response_code;
271
}
272
273
void Server::AddCommand(const std::string& url,
274
const std::string& http_verb,
275
const std::string& command_name) {
276
if (this->commands_.find(url) == this->commands_.end()) {
277
this->commands_[url] = std::shared_ptr<UriInfo>(
278
new UriInfo(url, http_verb, command_name));
279
} else {
280
this->commands_[url]->AddHttpVerb(http_verb, command_name);
281
}
282
}
283
284
void Server::ShutDownSession(const std::string& session_id) {
285
LOG(TRACE) << "Entering Server::ShutDownSession";
286
287
SessionMap::iterator it = this->sessions_.find(session_id);
288
if (it != this->sessions_.end()) {
289
it->second->ShutDown();
290
this->sessions_.erase(session_id);
291
} else {
292
LOG(DEBUG) << "Shutdown session is not found";
293
}
294
}
295
296
std::string Server::ReadRequestBody(struct mg_connection* conn,
297
const struct mg_request_info* request_info) {
298
LOG(TRACE) << "Entering Server::ReadRequestBody";
299
300
std::string request_body = "";
301
int content_length = 0;
302
for (int header_index = 0; header_index < 64; ++header_index) {
303
if (request_info->http_headers[header_index].name == NULL) {
304
break;
305
}
306
std::string header_name(request_info->http_headers[header_index].name);
307
std::transform(header_name.begin(),
308
header_name.end(),
309
header_name.begin(),
310
::tolower);
311
if (header_name.compare("content-length") == 0) {
312
content_length = atoi(request_info->http_headers[header_index].value);
313
break;
314
}
315
}
316
if (content_length != 0) {
317
std::vector<char> buffer(content_length + 1);
318
int bytes_read = 0;
319
while (bytes_read < content_length) {
320
bytes_read += mg_read(conn,
321
&buffer[bytes_read],
322
content_length - bytes_read);
323
}
324
buffer[content_length] = '\0';
325
request_body.append(&buffer[0]);
326
}
327
328
return request_body;
329
}
330
331
std::string Server::DispatchCommand(const std::string& uri,
332
const std::string& http_verb,
333
const std::string& command_body) {
334
LOG(TRACE) << "Entering Server::DispatchCommand";
335
336
std::string session_id = "";
337
std::string locator_parameters = "";
338
std::string serialized_response = "";
339
std::string command = this->LookupCommand(uri,
340
http_verb,
341
&session_id,
342
&locator_parameters);
343
LOG(DEBUG) << "Command: " << http_verb << " " << uri << " " << command_body;
344
345
if (command == webdriver::CommandType::NoCommand) {
346
Response invalid_command_response;
347
if (locator_parameters.size() > 0) {
348
std::string unknown_method_body = "Invalid method requested: ";
349
unknown_method_body.append(http_verb);
350
unknown_method_body.append(" is not a valid HTTP verb for ");
351
unknown_method_body.append(uri);
352
unknown_method_body.append("; acceptable verbs are: ");
353
unknown_method_body.append(locator_parameters);
354
invalid_command_response.SetErrorResponse(ERROR_UNKNOWN_METHOD,
355
unknown_method_body);
356
invalid_command_response.AddAdditionalData("verbs", locator_parameters);
357
} else {
358
std::string unknown_command_body = "Command not found: ";
359
unknown_command_body.append(http_verb);
360
unknown_command_body.append(" ");
361
unknown_command_body.append(uri);
362
invalid_command_response.SetErrorResponse(ERROR_UNKNOWN_COMMAND,
363
unknown_command_body);
364
}
365
serialized_response = invalid_command_response.Serialize();
366
} else if (command == webdriver::CommandType::Status) {
367
// Status command must be handled by the server, not by the session.
368
serialized_response = this->GetStatus();
369
} else if (command == webdriver::CommandType::GetSessionList) {
370
// GetSessionList command must be handled by the server,
371
// not by the session.
372
serialized_response = this->ListSessions();
373
} else {
374
SessionHandle session_handle;
375
if (command != webdriver::CommandType::NewSession &&
376
!this->LookupSession(session_id, &session_handle)) {
377
if (command == webdriver::CommandType::Quit) {
378
// Calling quit on an invalid session should be a no-op.
379
// Hand-code the response for quit on an invalid (already
380
// quit) session.
381
serialized_response.append("{ \"value\" : null }");
382
} else {
383
Response invalid_session_id_response;
384
std::string invalid_session_message = "session ";
385
invalid_session_message.append(session_id);
386
invalid_session_message.append(" does not exist");
387
invalid_session_id_response.SetErrorResponse(ERROR_INVALID_SESSION_ID,
388
invalid_session_message);
389
serialized_response = invalid_session_id_response.Serialize();
390
}
391
} else {
392
if (command == webdriver::CommandType::NewSession &&
393
this->sessions_.size() > 0) {
394
std::string session_exists_message = "Only one session may ";
395
session_exists_message.append("be created at a time, and a ");
396
session_exists_message.append("session already exists.");
397
Response session_exists_response;
398
session_exists_response.SetErrorResponse(ERROR_SESSION_NOT_CREATED,
399
session_exists_message);
400
serialized_response = session_exists_response.Serialize();
401
} else {
402
// Compile the serialized JSON representation of the command by hand.
403
std::string serialized_command = "{ \"name\" : \"" + command + "\"";
404
serialized_command.append(", \"locator\" : ");
405
serialized_command.append(locator_parameters);
406
serialized_command.append(", \"parameters\" : ");
407
serialized_command.append(command_body);
408
serialized_command.append(" }");
409
if (command == webdriver::CommandType::NewSession) {
410
session_handle = this->InitializeSession();
411
}
412
bool session_is_valid = session_handle->ExecuteCommand(
413
serialized_command,
414
&serialized_response);
415
if (command == webdriver::CommandType::NewSession) {
416
Response new_session_response;
417
new_session_response.Deserialize(serialized_response);
418
this->sessions_[new_session_response.GetSessionId()] = session_handle;
419
}
420
if (!session_is_valid) {
421
this->ShutDownSession(session_id);
422
}
423
}
424
}
425
}
426
LOG(DEBUG) << "Response: " << serialized_response;
427
return serialized_response;
428
}
429
430
std::string Server::ListSessions() {
431
LOG(TRACE) << "Entering Server::ListSessions";
432
433
// Manually construct the serialized command for getting
434
// session capabilities.
435
std::string get_caps_command = "{ \"name\" : \"" +
436
webdriver::CommandType::GetSessionCapabilities
437
+ "\"" +
438
", \"locator\" : {}, \"parameters\" : {} }";
439
440
Json::Value sessions(Json::arrayValue);
441
SessionMap::iterator it = this->sessions_.begin();
442
for (; it != this->sessions_.end(); ++it) {
443
// Each element of the GetSessionList command is an object with two
444
// named properties, "id" and "capabilities". We already know the
445
// ID, so we execute the GetSessionCapabilities command on each session
446
// to be able to return the capabilities.
447
Json::Value session_descriptor;
448
session_descriptor["id"] = it->first;
449
450
SessionHandle session = it->second;
451
std::string serialized_session_response;
452
session->ExecuteCommand(get_caps_command, &serialized_session_response);
453
454
Response session_response;
455
session_response.Deserialize(serialized_session_response);
456
session_descriptor["capabilities"] = session_response.value();
457
sessions.append(session_descriptor);
458
}
459
Response response;
460
response.SetSuccessResponse(sessions);
461
return response.Serialize();
462
}
463
464
bool Server::LookupSession(const std::string& session_id,
465
SessionHandle* session_handle) {
466
LOG(TRACE) << "Entering Server::LookupSession";
467
468
SessionMap::iterator it = this->sessions_.find(session_id);
469
if (it == this->sessions_.end()) {
470
return false;
471
}
472
*session_handle = it->second;
473
return true;
474
}
475
476
int Server::SendResponseToClient(struct mg_connection* conn,
477
const struct mg_request_info* request_info,
478
const std::string& serialized_response) {
479
LOG(TRACE) << "Entering Server::SendResponseToClient";
480
481
int return_code = 0;
482
if (serialized_response.size() > 0) {
483
Response response;
484
response.Deserialize(serialized_response);
485
return_code = response.GetHttpResponseCode();
486
if (return_code == 0) {
487
this->SendHttpOk(conn,
488
request_info,
489
serialized_response,
490
HTML_CONTENT_TYPE);
491
return_code = 200;
492
} else if (return_code == 200) {
493
this->SendHttpOk(conn,
494
request_info,
495
serialized_response,
496
JSON_CONTENT_TYPE);
497
} else if (return_code == 303) {
498
std::string location = response.value().asString();
499
response.SetSuccessResponse(response.value());
500
this->SendHttpSeeOther(conn, request_info, location);
501
return_code = 303;
502
} else if (return_code == 400) {
503
this->SendHttpBadRequest(conn, request_info, serialized_response);
504
return_code = 400;
505
} else if (return_code == 404) {
506
this->SendHttpNotFound(conn, request_info, serialized_response);
507
return_code = 404;
508
} else if (return_code == 405) {
509
std::string allowed_verbs = "";
510
Json::Value additional_data = response.additional_data();
511
if (additional_data.isObject() && additional_data.isMember("verbs")) {
512
allowed_verbs = additional_data["verbs"].asString();
513
}
514
this->SendHttpMethodNotAllowed(conn,
515
request_info,
516
allowed_verbs,
517
serialized_response);
518
return_code = 405;
519
} else if (return_code == 501) {
520
this->SendHttpNotImplemented(conn,
521
request_info,
522
"");
523
return_code = 501;
524
} else {
525
this->SendHttpInternalError(conn, request_info, serialized_response);
526
return_code = 500;
527
}
528
}
529
return return_code;
530
}
531
532
// The standard HTTP Status codes are implemented below. Chrome uses
533
// OK, See Other, Not Found, Method Not Allowed, and Internal Error.
534
// Internal Error, HTTP 500, is used as a catch all for any issue
535
// not covered in the JSON protocol.
536
void Server::SendHttpOk(struct mg_connection* connection,
537
const struct mg_request_info* request_info,
538
const std::string& body,
539
const std::string& content_type) {
540
LOG(TRACE) << "Entering Server::SendHttpOk";
541
542
std::string body_to_send = body + "\r\n";
543
544
std::ostringstream out;
545
out << "HTTP/1.1 200 OK\r\n"
546
<< "Content-Length: " << strlen(body_to_send.c_str()) << "\r\n"
547
<< "Content-Type: " << content_type << "; charset=utf-8\r\n"
548
<< "Cache-Control: no-cache\r\n"
549
<< "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n"
550
<< "Accept-Ranges: bytes\r\n\r\n";
551
if (strcmp(request_info->request_method, "HEAD") != 0) {
552
out << body_to_send;
553
}
554
555
mg_write(connection, out.str().c_str(), out.str().size());
556
}
557
558
void Server::SendHttpBadRequest(struct mg_connection* const connection,
559
const struct mg_request_info* request_info,
560
const std::string& body) {
561
LOG(TRACE) << "Entering Server::SendHttpBadRequest";
562
563
std::string body_to_send = body + "\r\n";
564
565
std::ostringstream out;
566
out << "HTTP/1.1 400 Bad Request\r\n"
567
<< "Content-Length: " << strlen(body_to_send.c_str()) << "\r\n"
568
<< "Content-Type: application/json; charset=utf-8\r\n"
569
<< "Cache-Control: no-cache\r\n"
570
<< "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n"
571
<< "Accept-Ranges: bytes\r\n\r\n";
572
if (strcmp(request_info->request_method, "HEAD") != 0) {
573
out << body_to_send;
574
}
575
576
mg_printf(connection, "%s", out.str().c_str());
577
}
578
579
void Server::SendHttpInternalError(struct mg_connection* connection,
580
const struct mg_request_info* request_info,
581
const std::string& body) {
582
LOG(TRACE) << "Entering Server::SendHttpInternalError";
583
584
std::string body_to_send = body + "\r\n";
585
586
std::ostringstream out;
587
out << "HTTP/1.1 500 Internal Server Error\r\n"
588
<< "Content-Length: " << strlen(body_to_send.c_str()) << "\r\n"
589
<< "Content-Type: application/json; charset=utf-8\r\n"
590
<< "Cache-Control: no-cache\r\n"
591
<< "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n"
592
<< "Accept-Ranges: bytes\r\n\r\n";
593
if (strcmp(request_info->request_method, "HEAD") != 0) {
594
out << body_to_send;
595
}
596
597
mg_write(connection, out.str().c_str(), out.str().size());
598
}
599
600
void Server::SendHttpNotFound(struct mg_connection* const connection,
601
const struct mg_request_info* request_info,
602
const std::string& body) {
603
LOG(TRACE) << "Entering Server::SendHttpNotFound";
604
605
std::string body_to_send = body + "\r\n";
606
607
std::ostringstream out;
608
out << "HTTP/1.1 404 Not Found\r\n"
609
<< "Content-Length: " << strlen(body_to_send.c_str()) << "\r\n"
610
<< "Content-Type: application/json; charset=utf-8\r\n"
611
<< "Cache-Control: no-cache\r\n"
612
<< "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n"
613
<< "Accept-Ranges: bytes\r\n\r\n";
614
if (strcmp(request_info->request_method, "HEAD") != 0) {
615
out << body_to_send;
616
}
617
618
mg_printf(connection, "%s", out.str().c_str());
619
}
620
621
void Server::SendHttpMethodNotAllowed(
622
struct mg_connection* connection,
623
const struct mg_request_info* request_info,
624
const std::string& allowed_methods,
625
const std::string& body) {
626
LOG(TRACE) << "Entering Server::SendHttpMethodNotAllowed";
627
628
std::string body_to_send = body + "\r\n";
629
630
std::ostringstream out;
631
out << "HTTP/1.1 405 Method Not Allowed\r\n"
632
<< "Content-Type: text/html\r\n"
633
<< "Content-Length: " << strlen(body_to_send.c_str()) << "\r\n"
634
<< "Allow: " << allowed_methods << "\r\n\r\n";
635
if (strcmp(request_info->request_method, "HEAD") != 0) {
636
out << body_to_send;
637
}
638
639
mg_write(connection, out.str().c_str(), out.str().size());
640
}
641
642
void Server::SendHttpTimeout(struct mg_connection* connection,
643
const struct mg_request_info* request_info,
644
const std::string& body) {
645
LOG(TRACE) << "Entering Server::SendHttpTimeout";
646
647
std::ostringstream out;
648
out << "HTTP/1.1 408 Timeout\r\n\r\n"
649
<< "Content-Length: " << strlen(body.c_str()) << "\r\n"
650
<< "Content-Type: application/json; charset=utf-8\r\n"
651
<< "Cache-Control: no-cache\r\n"
652
<< "Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept\r\n"
653
<< "Accept-Ranges: bytes\r\n\r\n";
654
655
mg_write(connection, out.str().c_str(), out.str().size());
656
}
657
658
void Server::SendHttpNotImplemented(struct mg_connection* connection,
659
const struct mg_request_info* request_info,
660
const std::string& body) {
661
LOG(TRACE) << "Entering Server::SendHttpNotImplemented";
662
663
std::ostringstream out;
664
out << "HTTP/1.1 501 Not Implemented\r\n\r\n";
665
666
mg_write(connection, out.str().c_str(), out.str().size());
667
}
668
669
void Server::SendHttpSeeOther(struct mg_connection* connection,
670
const struct mg_request_info* request_info,
671
const std::string& location) {
672
LOG(TRACE) << "Entering Server::SendHttpSeeOther";
673
674
std::ostringstream out;
675
out << "HTTP/1.1 303 See Other\r\n"
676
<< "Location: " << location << "\r\n"
677
<< "Content-Type: text/html\r\n"
678
<< "Content-Length: 0\r\n\r\n";
679
680
mg_write(connection, out.str().c_str(), out.str().size());
681
}
682
683
std::string Server::LookupCommand(const std::string& uri,
684
const std::string& http_verb,
685
std::string* session_id,
686
std::string* locator) {
687
LOG(TRACE) << "Entering Server::LookupCommand";
688
689
std::string value = webdriver::CommandType::NoCommand;
690
std::vector<std::string> url_fragments;
691
UriInfo::ParseUri(uri, &url_fragments, NULL);
692
UrlMap::const_iterator it = this->commands_.begin();
693
for (; it != this->commands_.end(); ++it) {
694
std::vector<std::string> locator_param_names;
695
std::vector<std::string> locator_param_values;
696
if (it->second->IsUriMatch(url_fragments,
697
&locator_param_names,
698
&locator_param_values)) {
699
if (it->second->HasHttpVerb(http_verb, &value)) {
700
std::string param = this->ConstructLocatorParameterJson(
701
locator_param_names, locator_param_values, session_id);
702
locator->append(param);
703
} else {
704
locator->append(it->second->GetSupportedVerbs());
705
}
706
break;
707
}
708
}
709
return value;
710
}
711
712
std::string Server::ConstructLocatorParameterJson(
713
std::vector<std::string> locator_param_names,
714
std::vector<std::string> locator_param_values,
715
std::string* session_id) {
716
std::string param = "{";
717
size_t param_count = locator_param_names.size();
718
for (unsigned int i = 0; i < param_count; i++) {
719
if (i != 0) {
720
param.append(",");
721
}
722
723
param.append(" \"");
724
param.append(locator_param_names[i]);
725
param.append("\" : \"");
726
param.append(locator_param_values[i]);
727
param.append("\"");
728
if (locator_param_names[i] == "sessionid") {
729
session_id->append(locator_param_values[i]);
730
}
731
}
732
733
param.append(" }");
734
return param;
735
}
736
737
void Server::PopulateCommandRepository() {
738
LOG(TRACE) << "Entering Server::PopulateCommandRepository";
739
740
this->AddCommand("/session", "POST", webdriver::CommandType::NewSession);
741
this->AddCommand("/session/:sessionid", "DELETE", webdriver::CommandType::Quit);
742
this->AddCommand("/status", "GET", webdriver::CommandType::Status);
743
this->AddCommand("/session/:sessionid/timeouts", "GET", webdriver::CommandType::GetTimeouts);
744
this->AddCommand("/session/:sessionid/timeouts", "POST", webdriver::CommandType::SetTimeouts);
745
this->AddCommand("/session/:sessionid/url", "GET", webdriver::CommandType::GetCurrentUrl);
746
this->AddCommand("/session/:sessionid/url", "POST", webdriver::CommandType::Get);
747
this->AddCommand("/session/:sessionid/back", "POST", webdriver::CommandType::GoBack);
748
this->AddCommand("/session/:sessionid/forward", "POST", webdriver::CommandType::GoForward);
749
this->AddCommand("/session/:sessionid/refresh", "POST", webdriver::CommandType::Refresh);
750
this->AddCommand("/session/:sessionid/title", "GET", webdriver::CommandType::GetTitle);
751
this->AddCommand("/session/:sessionid/window", "GET", webdriver::CommandType::GetCurrentWindowHandle);
752
this->AddCommand("/session/:sessionid/window", "POST", webdriver::CommandType::SwitchToWindow);
753
this->AddCommand("/session/:sessionid/window", "DELETE", webdriver::CommandType::CloseWindow);
754
this->AddCommand("/session/:sessionid/window/handles", "GET", webdriver::CommandType::GetWindowHandles);
755
this->AddCommand("/session/:sessionid/window/new", "POST", webdriver::CommandType::NewWindow);
756
this->AddCommand("/session/:sessionid/frame", "POST", webdriver::CommandType::SwitchToFrame);
757
this->AddCommand("/session/:sessionid/frame/parent", "POST", webdriver::CommandType::SwitchToParentFrame);
758
this->AddCommand("/session/:sessionid/window/rect", "GET", webdriver::CommandType::GetWindowRect);
759
this->AddCommand("/session/:sessionid/window/rect", "POST", webdriver::CommandType::SetWindowRect);
760
this->AddCommand("/session/:sessionid/window/maximize", "POST", webdriver::CommandType::MaximizeWindow);
761
this->AddCommand("/session/:sessionid/window/minimize", "POST", webdriver::CommandType::MinimizeWindow);
762
this->AddCommand("/session/:sessionid/window/fullscreen", "POST", webdriver::CommandType::FullscreenWindow);
763
this->AddCommand("/session/:sessionid/element/active", "GET", webdriver::CommandType::GetActiveElement);
764
this->AddCommand("/session/:sessionid/element", "POST", webdriver::CommandType::FindElement);
765
this->AddCommand("/session/:sessionid/elements", "POST", webdriver::CommandType::FindElements);
766
this->AddCommand("/session/:sessionid/element/:id/element", "POST", webdriver::CommandType::FindChildElement);
767
this->AddCommand("/session/:sessionid/element/:id/elements", "POST", webdriver::CommandType::FindChildElements);
768
this->AddCommand("/session/:sessionid/element/:id/selected", "GET", webdriver::CommandType::IsElementSelected);
769
this->AddCommand("/session/:sessionid/element/:id/attribute/:name", "GET", webdriver::CommandType::GetElementAttribute);
770
this->AddCommand("/session/:sessionid/element/:id/property/:name", "GET", webdriver::CommandType::GetElementProperty);
771
this->AddCommand("/session/:sessionid/element/:id/css/:propertyName", "GET", webdriver::CommandType::GetElementValueOfCssProperty);
772
this->AddCommand("/session/:sessionid/element/:id/text", "GET", webdriver::CommandType::GetElementText);
773
this->AddCommand("/session/:sessionid/element/:id/name", "GET", webdriver::CommandType::GetElementTagName);
774
this->AddCommand("/session/:sessionid/element/:id/rect", "GET", webdriver::CommandType::GetElementRect);
775
this->AddCommand("/session/:sessionid/element/:id/enabled", "GET", webdriver::CommandType::IsElementEnabled);
776
this->AddCommand("/session/:sessionid/element/:id/click", "POST", webdriver::CommandType::ClickElement);
777
this->AddCommand("/session/:sessionid/element/:id/clear", "POST", webdriver::CommandType::ClearElement);
778
this->AddCommand("/session/:sessionid/element/:id/value", "POST", webdriver::CommandType::SendKeysToElement);
779
this->AddCommand("/session/:sessionid/source", "GET", webdriver::CommandType::GetPageSource);
780
this->AddCommand("/session/:sessionid/execute/sync", "POST", webdriver::CommandType::ExecuteScript);
781
this->AddCommand("/session/:sessionid/execute/async", "POST", webdriver::CommandType::ExecuteAsyncScript);
782
this->AddCommand("/session/:sessionid/cookie", "GET", webdriver::CommandType::GetAllCookies);
783
this->AddCommand("/session/:sessionid/cookie/:name", "GET", webdriver::CommandType::GetNamedCookie);
784
this->AddCommand("/session/:sessionid/cookie", "POST", webdriver::CommandType::AddCookie);
785
this->AddCommand("/session/:sessionid/cookie", "DELETE", webdriver::CommandType::DeleteAllCookies);
786
this->AddCommand("/session/:sessionid/cookie/:name", "DELETE", webdriver::CommandType::DeleteNamedCookie);
787
this->AddCommand("/session/:sessionid/actions", "POST", webdriver::CommandType::Actions);
788
this->AddCommand("/session/:sessionid/actions", "DELETE", webdriver::CommandType::ReleaseActions);
789
this->AddCommand("/session/:sessionid/alert/dismiss", "POST", webdriver::CommandType::DismissAlert);
790
this->AddCommand("/session/:sessionid/alert/accept", "POST", webdriver::CommandType::AcceptAlert);
791
this->AddCommand("/session/:sessionid/alert/text", "GET", webdriver::CommandType::GetAlertText);
792
this->AddCommand("/session/:sessionid/alert/text", "POST", webdriver::CommandType::SendKeysToAlert);
793
this->AddCommand("/session/:sessionid/screenshot", "GET", webdriver::CommandType::Screenshot);
794
this->AddCommand("/session/:sessionid/element/:id/screenshot", "GET", webdriver::CommandType::ElementScreenshot);
795
796
// Additional commands required to be supported, but not defined
797
// in the specification.
798
this->AddCommand("/session/:sessionid/alert/credentials", "POST", webdriver::CommandType::SetAlertCredentials);
799
this->AddCommand("/session/:sessionid/element/:id/displayed", "GET", webdriver::CommandType::IsElementDisplayed);
800
this->AddCommand("/session/:sessionid/element/:id/equals/:other", "GET", webdriver::CommandType::ElementEquals);
801
this->AddCommand("/sessions", "GET", webdriver::CommandType::GetSessionList);
802
this->AddCommand("/session/:sessionid", "GET", webdriver::CommandType::GetSessionCapabilities);
803
804
this->AddCommand("/session/:sessionid/ime/available_engines", "GET", webdriver::CommandType::ListAvailableImeEngines);
805
this->AddCommand("/session/:sessionid/ime/active_engines", "GET", webdriver::CommandType::GetActiveImeEngine);
806
this->AddCommand("/session/:sessionid/ime/activated", "GET", webdriver::CommandType::IsImeActivated);
807
this->AddCommand("/session/:sessionid/ime/activate", "POST", webdriver::CommandType::ActivateImeEngine);
808
this->AddCommand("/session/:sessionid/ime/deactivate", "POST", webdriver::CommandType::DeactivateImeEngine);
809
}
810
811
} // namespace webdriver
812
813