Skip to content

Commit fe013b5

Browse files
committed
Improving cross-platform compatibility of webdriver-server C++ code.
This commit makes the common webdriver-server C++ code more easily cross-platform. It does this mainly by removing use of std::tr1::regex, which has issues compiling under gcc. In its place, we now use a custom URL matching algorithm for matching URLs in the WebDriver JSON Wire Protocol. While this matching algorithm may be a source of future errors, a nice side effect of it is a 3% performance increase in server execution times over using regular expressions. Additionally, this commit includes an alias for snprintf, which throws buffer overrun warnings when used with Visual Studio. It also contains some header #include rearranging and linting of the C++ code.
1 parent 03bd468 commit fe013b5

File tree

8 files changed

+264
-88
lines changed

8 files changed

+264
-88
lines changed

cpp/webdriver-server/precompile.h

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
#endif
2929

3030
#define WINDOWS
31-
32-
#include <windows.h>
3331
#else
3432
// TODO(JimEvans): Add non-Windows includes here
3533
#endif

cpp/webdriver-server/server.cc

+60-75
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,33 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
#include <regex>
1514
#include "server.h"
15+
#include <cstdio>
16+
#include <cstring>
17+
#include <sstream>
18+
#include "session.h"
19+
#include "uri_info.h"
1620
#include "logging.h"
1721

1822
#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>"
1923
#define HTML_CONTENT_TYPE "text/html"
2024
#define JSON_CONTENT_TYPE "application/json"
2125

26+
#if defined(WINDOWS)
27+
#include <cstdarg>
28+
inline int wd_snprintf(char* str, size_t size, const char* format, ...) {
29+
va_list args;
30+
va_start(args, format);
31+
int count = _vscprintf(format, args);
32+
if (str != NULL && size > 0) {
33+
count = _vsnprintf_s(str, size, _TRUNCATE, format, args);
34+
}
35+
va_end(args);
36+
return count;
37+
}
38+
#define snprintf wd_snprintf
39+
#endif
40+
2241
namespace webdriver {
2342

2443
Server::Server(const int port) {
@@ -50,7 +69,8 @@ void Server::Initialize(const int port,
5069
const std::string& log_file) {
5170
LOG::Level(log_level);
5271
LOG::File(log_file);
53-
LOG(INFO) << "Starting WebDriver server on port: '" << port << "' on host: '" << host << "'";
72+
LOG(INFO) << "Starting WebDriver server on port: '"
73+
<< port << "' on host: '" << host << "'";
5474
this->port_ = port;
5575
this->host_ = host;
5676
this->PopulateCommandRepository();
@@ -74,19 +94,20 @@ bool Server::Start() {
7494
// empty string.
7595
port_format_string = "%s%d";
7696
}
77-
int formatted_string_size = _scprintf(port_format_string.c_str(),
78-
this->host_.c_str(),
79-
this->port_);
80-
char* listening_ports_buffer = new char[formatted_string_size + 1];
81-
_snprintf_s(listening_ports_buffer,
82-
formatted_string_size + 1,
83-
formatted_string_size,
84-
port_format_string.c_str(),
85-
this->host_.c_str(),
86-
this->port_);
97+
int formatted_string_size = snprintf(NULL,
98+
0,
99+
port_format_string.c_str(),
100+
this->host_.c_str(),
101+
this->port_) + 1;
102+
char* listening_ports_buffer = new char[formatted_string_size];
103+
snprintf(listening_ports_buffer,
104+
formatted_string_size,
105+
port_format_string.c_str(),
106+
this->host_.c_str(),
107+
this->port_);
87108

88109
std::string acl = "-0.0.0.0/0,+127.0.0.1";
89-
LOG(DEBUG) << "Mongoose ACL is " << acl;
110+
LOG(DEBUG) << "Civetweb ACL is " << acl;
90111

91112
const char* options[] = { "listening_ports", listening_ports_buffer,
92113
"access_control_list", acl.c_str(),
@@ -96,7 +117,7 @@ bool Server::Start() {
96117
callbacks.begin_request = &OnNewHttpRequest;
97118
context_ = mg_start(&callbacks, this, options);
98119
if (context_ == NULL) {
99-
LOG(WARN) << "Failed to start Mongoose";
120+
LOG(WARN) << "Failed to start Civetweb";
100121
return false;
101122
}
102123
return true;
@@ -114,7 +135,7 @@ int Server::ProcessRequest(struct mg_connection* conn,
114135
const struct mg_request_info* request_info) {
115136
LOG(TRACE) << "Entering Server::ProcessRequest";
116137

117-
int http_response_code = NULL;
138+
int http_response_code = 0;
118139
std::string http_verb = request_info->request_method;
119140
std::string request_body = "{}";
120141
if (http_verb == "POST") {
@@ -154,7 +175,12 @@ int Server::ProcessRequest(struct mg_connection* conn,
154175
void Server::AddCommand(const std::string& url,
155176
const std::string& http_verb,
156177
const std::string& command_name) {
157-
this->commands_[url][http_verb] = command_name;
178+
if (this->commands_.find(url) == this->commands_.end()) {
179+
this->commands_[url] = std::tr1::shared_ptr<UriInfo>(
180+
new UriInfo(url, http_verb, command_name));
181+
} else {
182+
this->commands_[url]->AddHttpVerb(http_verb, command_name);
183+
}
158184
}
159185

160186
void Server::ShutDownSession(const std::string& session_id) {
@@ -242,7 +268,7 @@ std::string Server::DispatchCommand(const std::string& uri,
242268
// not by the session.
243269
serialized_response = this->ListSessions();
244270
} else {
245-
SessionHandle session_handle = NULL;
271+
SessionHandle session_handle;
246272
if (command != webdriver::CommandType::NewSession &&
247273
!this->LookupSession(session_id, &session_handle)) {
248274
if (command == webdriver::CommandType::Quit) {
@@ -297,7 +323,9 @@ std::string Server::ListSessions() {
297323

298324
// Manually construct the serialized command for getting
299325
// session capabilities.
300-
std::string get_caps_command = "{ \"name\" : \"" + webdriver::CommandType::GetSessionCapabilities + "\"" +
326+
std::string get_caps_command = "{ \"name\" : \"" +
327+
webdriver::CommandType::GetSessionCapabilities
328+
+ "\"" +
301329
", \"locator\" : {}, \"parameters\" : {} }";
302330

303331
Json::Value sessions(Json::arrayValue);
@@ -513,37 +541,32 @@ std::string Server::LookupCommand(const std::string& uri,
513541
LOG(TRACE) << "Entering Server::LookupCommand";
514542

515543
std::string value = webdriver::CommandType::NoCommand;
544+
std::vector<std::string> url_fragments;
545+
UriInfo::ParseUri(uri, &url_fragments, NULL);
516546
UrlMap::const_iterator it = this->commands_.begin();
517547
for (; it != this->commands_.end(); ++it) {
518-
std::string url_candidate = it->first;
519548
std::vector<std::string> locator_param_names;
520549
std::vector<std::string> locator_param_values;
521-
if (this->IsCommandMatch(uri, url_candidate, &locator_param_names, &locator_param_values)) {
522-
VerbMap::const_iterator verb_iterator = it->second.find(http_verb);
523-
if (verb_iterator != it->second.end()) {
524-
value = verb_iterator->second;
525-
std::string param = this->ConstructLocatorParameterJson(locator_param_names,
526-
locator_param_values,
527-
session_id);
550+
if (it->second->IsUriMatch(url_fragments,
551+
&locator_param_names,
552+
&locator_param_values)) {
553+
if (it->second->HasHttpVerb(http_verb, &value)) {
554+
std::string param = this->ConstructLocatorParameterJson(
555+
locator_param_names, locator_param_values, session_id);
528556
locator->append(param);
529-
break;
530557
} else {
531-
verb_iterator = it->second.begin();
532-
for (; verb_iterator != it->second.end(); ++verb_iterator) {
533-
if (locator->size() != 0) {
534-
locator->append(",");
535-
}
536-
locator->append(verb_iterator->first);
537-
}
558+
locator->append(it->second->GetSupportedVerbs());
538559
}
560+
break;
539561
}
540562
}
541563
return value;
542564
}
543565

544-
std::string Server::ConstructLocatorParameterJson(std::vector<std::string> locator_param_names,
545-
std::vector<std::string> locator_param_values,
546-
std::string* session_id) {
566+
std::string Server::ConstructLocatorParameterJson(
567+
std::vector<std::string> locator_param_names,
568+
std::vector<std::string> locator_param_values,
569+
std::string* session_id) {
547570
std::string param = "{";
548571
size_t param_count = locator_param_names.size();
549572
for (unsigned int i = 0; i < param_count; i++) {
@@ -565,44 +588,6 @@ std::string Server::ConstructLocatorParameterJson(std::vector<std::string> locat
565588
return param;
566589
}
567590

568-
bool Server::IsCommandMatch(const std::string& uri,
569-
const std::string& url_template,
570-
std::vector<std::string>* locator_param_names,
571-
std::vector<std::string>* locator_param_values) {
572-
std::string url_match_regex = url_template;
573-
size_t param_start_pos = url_match_regex.find_first_of(":");
574-
while (param_start_pos != std::string::npos) {
575-
size_t param_len = std::string::npos;
576-
size_t param_end_pos = url_match_regex.find_first_of("/", param_start_pos);
577-
if (param_end_pos != std::string::npos) {
578-
param_len = param_end_pos - param_start_pos;
579-
}
580-
581-
// Skip the colon
582-
std::string param_name = url_match_regex.substr(param_start_pos + 1,
583-
param_len - 1);
584-
locator_param_names->push_back(param_name);
585-
if (param_name == "sessionid" || param_name == "id") {
586-
url_match_regex.replace(param_start_pos, param_len, "([0-9a-fA-F-]+)");
587-
} else {
588-
url_match_regex.replace(param_start_pos, param_len, "([^/]+)");
589-
}
590-
param_start_pos = url_match_regex.find_first_of(":");
591-
}
592-
593-
std::tr1::regex matcher("^" + url_match_regex + "$");
594-
std::tr1::match_results<std::string::const_iterator> matches;
595-
if (std::tr1::regex_search(uri, matches, matcher)) {
596-
size_t param_count = locator_param_names->size();
597-
for (unsigned int i = 0; i < param_count; i++) {
598-
std::string locator_param_value = matches[i + 1].str();
599-
locator_param_values->push_back(locator_param_value);
600-
}
601-
return true;
602-
}
603-
return false;
604-
}
605-
606591
void Server::PopulateCommandRepository() {
607592
LOG(TRACE) << "Entering Server::PopulateCommandRepository";
608593

cpp/webdriver-server/server.h

+12-8
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,25 @@
1717
#ifndef WEBDRIVER_SERVER_SERVER_H_
1818
#define WEBDRIVER_SERVER_SERVER_H_
1919

20-
#include <vector>
2120
#include <map>
2221
#include <string>
22+
#include <vector>
23+
#if defined(_WIN32)
24+
#include <memory>
25+
#else
26+
#include <tr1/memory>
27+
#endif
2328
#include "civetweb.h"
2429
#include "command_types.h"
2530
#include "response.h"
26-
#include "session.h"
2731

2832
namespace webdriver {
2933

34+
class UriInfo;
35+
class Session;
36+
37+
typedef std::tr1::shared_ptr<Session> SessionHandle;
38+
3039
class Server {
3140
public:
3241
explicit Server(const int port);
@@ -57,8 +66,7 @@ class Server {
5766

5867
private:
5968
typedef std::map<std::string, SessionHandle> SessionMap;
60-
typedef std::map<std::string, std::string> VerbMap;
61-
typedef std::map<std::string, VerbMap> UrlMap;
69+
typedef std::map<std::string, std::tr1::shared_ptr<UriInfo> > UrlMap;
6270

6371
void Initialize(const int port,
6472
const std::string& host,
@@ -82,10 +90,6 @@ class Server {
8290
const struct mg_request_info* request_info,
8391
const std::string& serialized_response);
8492
void PopulateCommandRepository(void);
85-
bool IsCommandMatch(const std::string& uri,
86-
const std::string& url_template,
87-
std::vector<std::string>* locator_param_names,
88-
std::vector<std::string>* locator_param_values);
8993
std::string ConstructLocatorParameterJson(std::vector<std::string> locator_param_names,
9094
std::vector<std::string> locator_param_values,
9195
std::string* session_id);

cpp/webdriver-server/session.h

-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#ifndef WEBDRIVER_SERVER_SESSION_H_
1818
#define WEBDRIVER_SERVER_SESSION_H_
1919

20-
#include <memory>
2120
#include <string>
2221

2322
namespace webdriver {
@@ -44,8 +43,6 @@ class Session {
4443
DISALLOW_COPY_AND_ASSIGN(Session);
4544
};
4645

47-
typedef std::tr1::shared_ptr<Session> SessionHandle;
48-
4946
} // namespace webdriver
5047

5148
#endif // WEBDRIVER_SERVER_SESSION_H_

0 commit comments

Comments
 (0)