2015-04-13 12:09:26 -04:00
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the "License");
2013-01-11 22:18:32 +01:00
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
# include "server.h"
2020-04-24 15:43:04 -05:00
# include <algorithm>
2014-09-11 12:52:08 -04:00
# include <cstdio>
# include <cstring>
# include <sstream>
# include "session.h"
2018-07-05 09:07:20 -07:00
# include "errorcodes.h"
2014-09-11 12:52:08 -04:00
# include "uri_info.h"
2013-01-11 22:18:32 +01:00
# include "logging.h"
# 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>"
2015-10-30 15:47:54 -04:00
# define SERVER_DEFAULT_WHITELIST "127.0.0.1"
# define SERVER_DEFAULT_BLACKLIST "-0.0.0.0 / 0"
2013-01-11 22:18:32 +01:00
# define HTML_CONTENT_TYPE "text / html"
# define JSON_CONTENT_TYPE "application / json"
2014-09-11 12:52:08 -04:00
# if defined(WINDOWS)
# include <cstdarg>
inline int wd_snprintf ( char * str , size_t size , const char * format , . . . ) {
va_list args ;
va_start ( args , format ) ;
int count = _vscprintf ( format , args ) ;
if ( str ! = NULL & & size > 0 ) {
count = _vsnprintf_s ( str , size , _TRUNCATE , format , args ) ;
}
va_end ( args ) ;
return count ;
}
# define snprintf wd_snprintf
# endif
2013-01-11 22:18:32 +01:00
namespace webdriver {
Server : : Server ( const int port ) {
2018-02-16 07:41:40 -08:00
this - > Initialize ( port , " " , " " , " " , " " ) ;
2013-01-11 22:18:32 +01:00
}
Server : : Server ( const int port , const std : : string & host ) {
2018-02-16 07:41:40 -08:00
this - > Initialize ( port , host , " " , " " , " " ) ;
2013-01-11 22:18:32 +01:00
}
Server : : Server ( const int port ,
const std : : string & host ,
const std : : string & log_level ,
const std : : string & log_file ) {
2018-02-16 07:41:40 -08:00
this - > Initialize ( port , host , log_level , log_file , " " ) ;
2015-10-30 15:47:54 -04:00
}
Server : : Server ( const int port ,
const std : : string & host ,
const std : : string & log_level ,
const std : : string & log_file ,
const std : : string & acl ) {
this - > Initialize ( port , host , log_level , log_file , acl ) ;
2013-01-11 22:18:32 +01:00
}
Server : : ~ Server ( void ) {
SessionMap : : iterator it = this - > sessions_ . begin ( ) ;
for ( ; it ! = this - > sessions_ . end ( ) ; + + it ) {
std : : string session_id = it - > first ;
this - > ShutDownSession ( session_id ) ;
}
}
void Server : : Initialize ( const int port ,
const std : : string & host ,
const std : : string & log_level ,
2015-10-30 15:47:54 -04:00
const std : : string & log_file ,
const std : : string & acl ) {
2013-01-11 22:18:32 +01:00
LOG : : Level ( log_level ) ;
LOG : : File ( log_file ) ;
2014-09-11 12:52:08 -04:00
LOG ( INFO ) < < " Starting WebDriver server on port: ' "
< < port < < " ' on host: ' " < < host < < " ' " ;
2013-01-11 22:18:32 +01:00
this - > port_ = port ;
this - > host_ = host ;
2015-10-30 15:47:54 -04:00
if ( acl . size ( ) > 0 ) {
this - > ProcessWhitelist ( acl ) ;
}
2013-01-11 22:18:32 +01:00
this - > PopulateCommandRepository ( ) ;
}
2015-10-30 15:47:54 -04:00
void Server : : ProcessWhitelist ( const std : : string & whitelist ) {
std : : string input_copy = whitelist ;
while ( input_copy . size ( ) > 0 ) {
size_t delimiter_pos = input_copy . find ( " , " ) ;
std : : string token = input_copy . substr ( 0 , delimiter_pos ) ;
if ( delimiter_pos = = std : : string : : npos ) {
input_copy = " " ;
} else {
input_copy = input_copy . substr ( delimiter_pos + 1 ) ;
}
this - > whitelist_ . push_back ( token ) ;
}
}
2018-02-16 07:41:40 -08:00
std : : string Server : : GetListeningPorts ( const bool use_ipv6 ) {
2013-01-11 22:18:32 +01:00
std : : string port_format_string = " %s:%d " ;
if ( this - > host_ . size ( ) = = 0 ) {
2018-02-16 07:41:40 -08:00
// If the host name is an empty string, then we want to bind
// to the local loopback address on both IPv4 and IPv6 if we
// can. Using the addresses in the listening port format string
// will prevent connection from external IP addresses.
port_format_string = " %s127.0.0.1:%d " ;
if ( use_ipv6 ) {
port_format_string . append ( " ,[::1]:%d " ) ;
}
} else if ( this - > whitelist_ . size ( ) > 0 ) {
// If there are white-listed IP addresses, we can only use IPv4,
// and we don't want the colon in the listening ports string.
// Instead, we want to bind to all adapters, and use the access
// control list to determine which addresses can connect. So to
// remove the host from the format string, when we use printf to
// format, the %s will be replaced by an empty string.
2013-01-11 22:18:32 +01:00
port_format_string = " %s%d " ;
}
2014-09-11 12:52:08 -04:00
int formatted_string_size = snprintf ( NULL ,
0 ,
port_format_string . c_str ( ) ,
this - > host_ . c_str ( ) ,
2018-02-16 07:41:40 -08:00
this - > port_ ,
2014-09-11 12:52:08 -04:00
this - > port_ ) + 1 ;
2018-02-16 07:41:40 -08:00
std : : vector < char > listening_ports_buffer ( formatted_string_size ) ;
snprintf ( & listening_ports_buffer [ 0 ] ,
2014-09-11 12:52:08 -04:00
formatted_string_size ,
port_format_string . c_str ( ) ,
this - > host_ . c_str ( ) ,
2018-02-16 07:41:40 -08:00
this - > port_ ,
2014-09-11 12:52:08 -04:00
this - > port_ ) ;
2018-02-16 07:41:40 -08:00
return & listening_ports_buffer [ 0 ] ;
}
std : : string Server : : GetAccessControlList ( ) {
std : : string acl = " " ;
if ( this - > whitelist_ . size ( ) > 0 ) {
acl = SERVER_DEFAULT_BLACKLIST ;
for ( std : : vector < std : : string > : : const_iterator it = this - > whitelist_ . begin ( ) ;
it < this - > whitelist_ . end ( ) ;
+ + it ) {
acl . append ( " ,+ " ) . append ( * it ) ;
}
LOG ( DEBUG ) < < " Civetweb ACL is " < < acl ;
}
return acl ;
}
void Server : : GenerateOptionsList ( std : : vector < const char * > * options ) {
std : : map < std : : string , std : : string > : : const_iterator it = this - > options_ . begin ( ) ;
for ( ; it ! = this - > options_ . end ( ) ; + + it ) {
options - > push_back ( it - > first . c_str ( ) ) ;
options - > push_back ( it - > second . c_str ( ) ) ;
}
options - > push_back ( NULL ) ;
}
int Server : : OnNewHttpRequest ( struct mg_connection * conn ) {
mg_context * context = mg_get_context ( conn ) ;
Server * current_server = reinterpret_cast < Server * > ( mg_get_user_data ( context ) ) ;
const mg_request_info * request_info = mg_get_request_info ( conn ) ;
int handler_result_code = current_server - > ProcessRequest ( conn , request_info ) ;
return handler_result_code ;
}
2013-01-11 22:18:32 +01:00
2018-02-16 07:41:40 -08:00
bool Server : : Start ( ) {
LOG ( TRACE ) < < " Entering Server::Start " ;
std : : string listening_port_option = this - > GetListeningPorts ( true ) ;
this - > options_ [ " listening_ports " ] = listening_port_option ;
std : : string acl_option = this - > GetAccessControlList ( ) ;
if ( acl_option . size ( ) > 0 ) {
this - > options_ [ " access_control_list " ] = acl_option ;
2015-10-30 15:47:54 -04:00
}
2013-01-11 22:18:32 +01:00
2018-02-16 07:41:40 -08:00
this - > options_ [ " enable_keep_alive " ] = " yes " ;
std : : vector < const char * > options ;
this - > GenerateOptionsList ( & options ) ;
2014-08-19 21:15:00 +00:00
mg_callbacks callbacks = { } ;
callbacks . begin_request = & OnNewHttpRequest ;
2018-02-16 07:41:40 -08:00
context_ = mg_start ( & callbacks , this , & options [ 0 ] ) ;
2013-01-11 22:18:32 +01:00
if ( context_ = = NULL ) {
2018-02-16 07:41:40 -08:00
std : : string ipv4_port_option = this - > GetListeningPorts ( false ) ;
if ( listening_port_option = = ipv4_port_option ) {
// If the IPv4 and IPv6 versions of the port option string
// are equal, then either a host to bind to or an ACL was
// specified, so there is no need to retry.
LOG ( WARN ) < < " Failed to start Civetweb " ;
return false ;
} else {
// If we fail, a host and ACL aren't specified, we might not
// be able to bind to an IPv6 address. Try again to bind to
// the IPv4 loopback only.
LOG ( INFO ) < < " Failed first attempt to start Civetweb. Attempt start with IPv4 only " ;
this - > options_ [ " listening_ports " ] = listening_port_option ;
options . clear ( ) ;
this - > GenerateOptionsList ( & options ) ;
context_ = mg_start ( & callbacks , this , & options [ 0 ] ) ;
if ( context_ = = NULL ) {
LOG ( WARN ) < < " Failed to start Civetweb " ;
return false ;
}
}
2013-01-11 22:18:32 +01:00
}
return true ;
}
void Server : : Stop ( ) {
LOG ( TRACE ) < < " Entering Server::Stop " ;
if ( context_ ) {
mg_stop ( context_ ) ;
context_ = NULL ;
}
}
int Server : : ProcessRequest ( struct mg_connection * conn ,
const struct mg_request_info * request_info ) {
LOG ( TRACE ) < < " Entering Server::ProcessRequest " ;
2014-09-11 12:52:08 -04:00
int http_response_code = 0 ;
2013-01-11 22:18:32 +01:00
std : : string http_verb = request_info - > request_method ;
std : : string request_body = " {} " ;
if ( http_verb = = " POST " ) {
request_body = this - > ReadRequestBody ( conn , request_info ) ;
}
LOG ( TRACE ) < < " Process request with: "
2018-02-16 07:41:40 -08:00
< < " URI: " < < request_info - > local_uri
2013-01-11 22:18:32 +01:00
< < " HTTP verb: " < < http_verb < < std : : endl
< < " body: " < < request_body ;
2018-02-16 07:41:40 -08:00
if ( strcmp ( request_info - > local_uri , " / " ) = = 0 ) {
2013-01-11 22:18:32 +01:00
this - > SendHttpOk ( conn ,
request_info ,
SERVER_DEFAULT_PAGE ,
HTML_CONTENT_TYPE ) ;
2017-02-22 14:13:25 -08:00
http_response_code = 0 ;
2018-02-16 07:41:40 -08:00
} else if ( strcmp ( request_info - > local_uri , " /shutdown " ) = = 0 ) {
2013-01-11 22:18:32 +01:00
this - > SendHttpOk ( conn ,
request_info ,
SERVER_DEFAULT_PAGE ,
HTML_CONTENT_TYPE ) ;
2017-02-22 14:13:25 -08:00
http_response_code = 0 ;
2013-01-11 22:18:32 +01:00
this - > ShutDown ( ) ;
} else {
2018-02-16 07:41:40 -08:00
std : : string serialized_response = this - > DispatchCommand ( request_info - > local_uri ,
2019-01-10 17:20:12 -08:00
http_verb ,
request_body ) ;
2013-01-11 22:18:32 +01:00
http_response_code = this - > SendResponseToClient ( conn ,
request_info ,
serialized_response ) ;
}
return http_response_code ;
}
2013-02-04 17:51:42 -05:00
void Server : : AddCommand ( const std : : string & url ,
const std : : string & http_verb ,
const std : : string & command_name ) {
2014-09-11 12:52:08 -04:00
if ( this - > commands_ . find ( url ) = = this - > commands_ . end ( ) ) {
2018-01-30 08:32:07 -08:00
this - > commands_ [ url ] = std : : shared_ptr < UriInfo > (
2014-09-11 12:52:08 -04:00
new UriInfo ( url , http_verb , command_name ) ) ;
} else {
this - > commands_ [ url ] - > AddHttpVerb ( http_verb , command_name ) ;
}
2013-02-04 17:51:42 -05:00
}
2013-01-11 22:18:32 +01:00
void Server : : ShutDownSession ( const std : : string & session_id ) {
LOG ( TRACE ) < < " Entering Server::ShutDownSession " ;
SessionMap : : iterator it = this - > sessions_ . find ( session_id ) ;
if ( it ! = this - > sessions_ . end ( ) ) {
it - > second - > ShutDown ( ) ;
this - > sessions_ . erase ( session_id ) ;
} else {
LOG ( DEBUG ) < < " Shutdown session is not found " ;
}
}
std : : string Server : : ReadRequestBody ( struct mg_connection * conn ,
const struct mg_request_info * request_info ) {
LOG ( TRACE ) < < " Entering Server::ReadRequestBody " ;
std : : string request_body = " " ;
int content_length = 0 ;
for ( int header_index = 0 ; header_index < 64 ; + + header_index ) {
if ( request_info - > http_headers [ header_index ] . name = = NULL ) {
break ;
}
2020-04-24 15:43:04 -05:00
std : : string header_name ( request_info - > http_headers [ header_index ] . name ) ;
std : : transform ( header_name . begin ( ) ,
header_name . end ( ) ,
header_name . begin ( ) ,
: : tolower ) ;
if ( header_name . compare ( " content-length " ) = = 0 ) {
2013-01-11 22:18:32 +01:00
content_length = atoi ( request_info - > http_headers [ header_index ] . value ) ;
break ;
}
}
2018-08-20 11:20:48 -07:00
if ( content_length ! = 0 ) {
2013-01-11 22:18:32 +01:00
std : : vector < char > buffer ( content_length + 1 ) ;
int bytes_read = 0 ;
while ( bytes_read < content_length ) {
bytes_read + = mg_read ( conn ,
& buffer [ bytes_read ] ,
content_length - bytes_read ) ;
}
buffer [ content_length ] = ' \0 ' ;
request_body . append ( & buffer [ 0 ] ) ;
}
return request_body ;
}
std : : string Server : : DispatchCommand ( const std : : string & uri ,
const std : : string & http_verb ,
const std : : string & command_body ) {
LOG ( TRACE ) < < " Entering Server::DispatchCommand " ;
std : : string session_id = " " ;
std : : string locator_parameters = " " ;
std : : string serialized_response = " " ;
std : : string command = this - > LookupCommand ( uri ,
http_verb ,
& session_id ,
& locator_parameters ) ;
LOG ( DEBUG ) < < " Command: " < < http_verb < < " " < < uri < < " " < < command_body ;
if ( command = = webdriver : : CommandType : : NoCommand ) {
2019-01-10 17:20:12 -08:00
Response invalid_command_response ;
if ( locator_parameters . size ( ) > 0 ) {
std : : string unknown_method_body = " Invalid method requested: " ;
unknown_method_body . append ( http_verb ) ;
unknown_method_body . append ( " is not a valid HTTP verb for " ) ;
unknown_method_body . append ( uri ) ;
unknown_method_body . append ( " ; acceptable verbs are: " ) ;
unknown_method_body . append ( locator_parameters ) ;
invalid_command_response . SetErrorResponse ( ERROR_UNKNOWN_METHOD ,
unknown_method_body ) ;
invalid_command_response . AddAdditionalData ( " verbs " , locator_parameters ) ;
} else {
std : : string unknown_command_body = " Command not found: " ;
unknown_command_body . append ( http_verb ) ;
unknown_command_body . append ( " " ) ;
unknown_command_body . append ( uri ) ;
invalid_command_response . SetErrorResponse ( ERROR_UNKNOWN_COMMAND ,
unknown_command_body ) ;
}
serialized_response = invalid_command_response . Serialize ( ) ;
2013-01-11 22:18:32 +01:00
} else if ( command = = webdriver : : CommandType : : Status ) {
// Status command must be handled by the server, not by the session.
serialized_response = this - > GetStatus ( ) ;
} else if ( command = = webdriver : : CommandType : : GetSessionList ) {
// GetSessionList command must be handled by the server,
// not by the session.
serialized_response = this - > ListSessions ( ) ;
} else {
2014-09-11 12:52:08 -04:00
SessionHandle session_handle ;
2014-07-01 11:42:36 -04:00
if ( command ! = webdriver : : CommandType : : NewSession & &
! this - > LookupSession ( session_id , & session_handle ) ) {
2013-01-11 22:18:32 +01:00
if ( command = = webdriver : : CommandType : : Quit ) {
// Calling quit on an invalid session should be a no-op.
// Hand-code the response for quit on an invalid (already
// quit) session.
2017-02-22 14:13:25 -08:00
serialized_response . append ( " { \" value \" : null } " ) ;
2013-01-11 22:18:32 +01:00
} else {
2019-01-10 17:20:12 -08:00
Response invalid_session_id_response ;
std : : string invalid_session_message = " session " ;
invalid_session_message . append ( session_id ) ;
invalid_session_message . append ( " does not exist " ) ;
invalid_session_id_response . SetErrorResponse ( ERROR_INVALID_SESSION_ID ,
invalid_session_message ) ;
serialized_response = invalid_session_id_response . Serialize ( ) ;
2013-01-11 22:18:32 +01:00
}
} else {
2018-03-07 10:13:33 -08:00
if ( command = = webdriver : : CommandType : : NewSession & &
this - > sessions_ . size ( ) > 0 ) {
2019-01-10 17:20:12 -08:00
std : : string session_exists_message = " Only one session may " ;
session_exists_message . append ( " be created at a time, and a " ) ;
session_exists_message . append ( " session already exists. " ) ;
Response session_exists_response ;
session_exists_response . SetErrorResponse ( ERROR_SESSION_NOT_CREATED ,
session_exists_message ) ;
serialized_response = session_exists_response . Serialize ( ) ;
2018-03-07 10:13:33 -08:00
} else {
// Compile the serialized JSON representation of the command by hand.
std : : string serialized_command = " { \" name \" : \" " + command + " \" " ;
serialized_command . append ( " , \" locator \" : " ) ;
serialized_command . append ( locator_parameters ) ;
serialized_command . append ( " , \" parameters \" : " ) ;
serialized_command . append ( command_body ) ;
serialized_command . append ( " } " ) ;
if ( command = = webdriver : : CommandType : : NewSession ) {
session_handle = this - > InitializeSession ( ) ;
}
bool session_is_valid = session_handle - > ExecuteCommand (
serialized_command ,
& serialized_response ) ;
if ( command = = webdriver : : CommandType : : NewSession ) {
Response new_session_response ;
new_session_response . Deserialize ( serialized_response ) ;
this - > sessions_ [ new_session_response . GetSessionId ( ) ] = session_handle ;
}
if ( ! session_is_valid ) {
this - > ShutDownSession ( session_id ) ;
}
2013-01-11 22:18:32 +01:00
}
}
}
LOG ( DEBUG ) < < " Response: " < < serialized_response ;
return serialized_response ;
}
std : : string Server : : ListSessions ( ) {
LOG ( TRACE ) < < " Entering Server::ListSessions " ;
// Manually construct the serialized command for getting
// session capabilities.
2014-09-11 12:52:08 -04:00
std : : string get_caps_command = " { \" name \" : \" " +
webdriver : : CommandType : : GetSessionCapabilities
+ " \" " +
2013-01-11 22:18:32 +01:00
" , \" locator \" : {}, \" parameters \" : {} } " ;
Json : : Value sessions ( Json : : arrayValue ) ;
SessionMap : : iterator it = this - > sessions_ . begin ( ) ;
for ( ; it ! = this - > sessions_ . end ( ) ; + + it ) {
// Each element of the GetSessionList command is an object with two
// named properties, "id" and "capabilities". We already know the
// ID, so we execute the GetSessionCapabilities command on each session
// to be able to return the capabilities.
Json : : Value session_descriptor ;
session_descriptor [ " id " ] = it - > first ;
SessionHandle session = it - > second ;
std : : string serialized_session_response ;
session - > ExecuteCommand ( get_caps_command , & serialized_session_response ) ;
Response session_response ;
session_response . Deserialize ( serialized_session_response ) ;
session_descriptor [ " capabilities " ] = session_response . value ( ) ;
sessions . append ( session_descriptor ) ;
}
Response response ;
response . SetSuccessResponse ( sessions ) ;
return response . Serialize ( ) ;
}
bool Server : : LookupSession ( const std : : string & session_id ,
SessionHandle * session_handle ) {
LOG ( TRACE ) < < " Entering Server::LookupSession " ;
SessionMap : : iterator it = this - > sessions_ . find ( session_id ) ;
if ( it = = this - > sessions_ . end ( ) ) {
return false ;
}
* session_handle = it - > second ;
return true ;
}
int Server : : SendResponseToClient ( struct mg_connection * conn ,
const struct mg_request_info * request_info ,
const std : : string & serialized_response ) {
LOG ( TRACE ) < < " Entering Server::SendResponseToClient " ;
int return_code = 0 ;
if ( serialized_response . size ( ) > 0 ) {
Response response ;
response . Deserialize ( serialized_response ) ;
2017-02-22 14:13:25 -08:00
return_code = response . GetHttpResponseCode ( ) ;
2013-01-11 22:18:32 +01:00
if ( return_code = = 0 ) {
this - > SendHttpOk ( conn ,
request_info ,
serialized_response ,
2017-02-22 14:13:25 -08:00
HTML_CONTENT_TYPE ) ;
2013-01-11 22:18:32 +01:00
return_code = 200 ;
} else if ( return_code = = 200 ) {
this - > SendHttpOk ( conn ,
request_info ,
serialized_response ,
2017-02-22 14:13:25 -08:00
JSON_CONTENT_TYPE ) ;
2013-01-11 22:18:32 +01:00
} else if ( return_code = = 303 ) {
std : : string location = response . value ( ) . asString ( ) ;
response . SetSuccessResponse ( response . value ( ) ) ;
this - > SendHttpSeeOther ( conn , request_info , location ) ;
return_code = 303 ;
} else if ( return_code = = 400 ) {
this - > SendHttpBadRequest ( conn , request_info , serialized_response ) ;
return_code = 400 ;
} else if ( return_code = = 404 ) {
this - > SendHttpNotFound ( conn , request_info , serialized_response ) ;
return_code = 404 ;
} else if ( return_code = = 405 ) {
2019-01-10 17:20:12 -08:00
std : : string allowed_verbs = " " ;
Json : : Value additional_data = response . additional_data ( ) ;
if ( additional_data . isObject ( ) & & additional_data . isMember ( " verbs " ) ) {
allowed_verbs = additional_data [ " verbs " ] . asString ( ) ;
}
this - > SendHttpMethodNotAllowed ( conn ,
request_info ,
allowed_verbs ,
serialized_response ) ;
2013-01-11 22:18:32 +01:00
return_code = 405 ;
} else if ( return_code = = 501 ) {
this - > SendHttpNotImplemented ( conn ,
request_info ,
" " ) ;
return_code = 501 ;
} else {
this - > SendHttpInternalError ( conn , request_info , serialized_response ) ;
return_code = 500 ;
}
}
return return_code ;
}
// The standard HTTP Status codes are implemented below. Chrome uses
// OK, See Other, Not Found, Method Not Allowed, and Internal Error.
// Internal Error, HTTP 500, is used as a catch all for any issue
// not covered in the JSON protocol.
void Server : : SendHttpOk ( struct mg_connection * connection ,
const struct mg_request_info * request_info ,
const std : : string & body ,
const std : : string & content_type ) {
LOG ( TRACE ) < < " Entering Server::SendHttpOk " ;
2018-12-20 09:14:41 -08:00
std : : string body_to_send = body + " \r \n " ;
2013-01-11 22:18:32 +01:00
std : : ostringstream out ;
out < < " HTTP/1.1 200 OK \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Length: " < < strlen ( body_to_send . c_str ( ) ) < < " \r \n "
2019-03-04 12:38:58 -08:00
< < " Content-Type: " < < content_type < < " ; charset=utf-8 \r \n "
2019-01-10 17:20:12 -08:00
< < " Cache-Control: no-cache \r \n "
< < " Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept \r \n "
< < " Accept-Ranges: bytes \r \n \r \n " ;
2013-01-11 22:18:32 +01:00
if ( strcmp ( request_info - > request_method , " HEAD " ) ! = 0 ) {
2018-12-20 09:14:41 -08:00
out < < body_to_send ;
2013-01-11 22:18:32 +01:00
}
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
void Server : : SendHttpBadRequest ( struct mg_connection * const connection ,
const struct mg_request_info * request_info ,
const std : : string & body ) {
LOG ( TRACE ) < < " Entering Server::SendHttpBadRequest " ;
2018-12-20 09:14:41 -08:00
std : : string body_to_send = body + " \r \n " ;
2013-01-11 22:18:32 +01:00
std : : ostringstream out ;
out < < " HTTP/1.1 400 Bad Request \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Length: " < < strlen ( body_to_send . c_str ( ) ) < < " \r \n "
2019-03-04 12:38:58 -08:00
< < " Content-Type: application/json; charset=utf-8 \r \n "
2019-01-10 17:20:12 -08:00
< < " Cache-Control: no-cache \r \n "
< < " Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept \r \n "
< < " Accept-Ranges: bytes \r \n \r \n " ;
2013-01-11 22:18:32 +01:00
if ( strcmp ( request_info - > request_method , " HEAD " ) ! = 0 ) {
2018-12-20 09:14:41 -08:00
out < < body_to_send ;
2013-01-11 22:18:32 +01:00
}
mg_printf ( connection , " %s " , out . str ( ) . c_str ( ) ) ;
}
void Server : : SendHttpInternalError ( struct mg_connection * connection ,
const struct mg_request_info * request_info ,
const std : : string & body ) {
LOG ( TRACE ) < < " Entering Server::SendHttpInternalError " ;
2018-12-20 09:14:41 -08:00
std : : string body_to_send = body + " \r \n " ;
2013-01-11 22:18:32 +01:00
std : : ostringstream out ;
out < < " HTTP/1.1 500 Internal Server Error \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Length: " < < strlen ( body_to_send . c_str ( ) ) < < " \r \n "
2019-03-04 12:38:58 -08:00
< < " Content-Type: application/json; charset=utf-8 \r \n "
2019-01-10 17:20:12 -08:00
< < " Cache-Control: no-cache \r \n "
< < " Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept \r \n "
< < " Accept-Ranges: bytes \r \n \r \n " ;
2013-01-11 22:18:32 +01:00
if ( strcmp ( request_info - > request_method , " HEAD " ) ! = 0 ) {
2018-12-20 09:14:41 -08:00
out < < body_to_send ;
2013-01-11 22:18:32 +01:00
}
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
void Server : : SendHttpNotFound ( struct mg_connection * const connection ,
const struct mg_request_info * request_info ,
const std : : string & body ) {
LOG ( TRACE ) < < " Entering Server::SendHttpNotFound " ;
2018-12-20 09:14:41 -08:00
std : : string body_to_send = body + " \r \n " ;
2013-01-11 22:18:32 +01:00
std : : ostringstream out ;
out < < " HTTP/1.1 404 Not Found \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Length: " < < strlen ( body_to_send . c_str ( ) ) < < " \r \n "
2019-03-04 12:38:58 -08:00
< < " Content-Type: application/json; charset=utf-8 \r \n "
2019-01-10 17:20:12 -08:00
< < " Cache-Control: no-cache \r \n "
< < " Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept \r \n "
< < " Accept-Ranges: bytes \r \n \r \n " ;
2013-01-11 22:18:32 +01:00
if ( strcmp ( request_info - > request_method , " HEAD " ) ! = 0 ) {
2018-12-20 09:14:41 -08:00
out < < body_to_send ;
2013-01-11 22:18:32 +01:00
}
mg_printf ( connection , " %s " , out . str ( ) . c_str ( ) ) ;
}
void Server : : SendHttpMethodNotAllowed (
struct mg_connection * connection ,
const struct mg_request_info * request_info ,
2019-01-10 17:20:12 -08:00
const std : : string & allowed_methods ,
const std : : string & body ) {
2013-01-11 22:18:32 +01:00
LOG ( TRACE ) < < " Entering Server::SendHttpMethodNotAllowed " ;
2019-01-10 17:20:12 -08:00
std : : string body_to_send = body + " \r \n " ;
2013-01-11 22:18:32 +01:00
std : : ostringstream out ;
out < < " HTTP/1.1 405 Method Not Allowed \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Type: text/html \r \n "
< < " Content-Length: " < < strlen ( body_to_send . c_str ( ) ) < < " \r \n "
< < " Allow: " < < allowed_methods < < " \r \n \r \n " ;
if ( strcmp ( request_info - > request_method , " HEAD " ) ! = 0 ) {
out < < body_to_send ;
}
2013-01-11 22:18:32 +01:00
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
2017-02-22 14:13:25 -08:00
void Server : : SendHttpTimeout ( struct mg_connection * connection ,
const struct mg_request_info * request_info ,
const std : : string & body ) {
LOG ( TRACE ) < < " Entering Server::SendHttpTimeout " ;
std : : ostringstream out ;
out < < " HTTP/1.1 408 Timeout \r \n \r \n "
2019-01-10 17:20:12 -08:00
< < " Content-Length: " < < strlen ( body . c_str ( ) ) < < " \r \n "
2019-03-04 12:38:58 -08:00
< < " Content-Type: application/json; charset=utf-8 \r \n "
2019-01-10 17:20:12 -08:00
< < " Cache-Control: no-cache \r \n "
< < " Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept \r \n "
< < " Accept-Ranges: bytes \r \n \r \n " ;
2017-02-22 14:13:25 -08:00
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
2013-01-11 22:18:32 +01:00
void Server : : SendHttpNotImplemented ( struct mg_connection * connection ,
const struct mg_request_info * request_info ,
const std : : string & body ) {
LOG ( TRACE ) < < " Entering Server::SendHttpNotImplemented " ;
std : : ostringstream out ;
out < < " HTTP/1.1 501 Not Implemented \r \n \r \n " ;
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
void Server : : SendHttpSeeOther ( struct mg_connection * connection ,
const struct mg_request_info * request_info ,
const std : : string & location ) {
LOG ( TRACE ) < < " Entering Server::SendHttpSeeOther " ;
std : : ostringstream out ;
out < < " HTTP/1.1 303 See Other \r \n "
2019-01-10 17:20:12 -08:00
< < " Location: " < < location < < " \r \n "
< < " Content-Type: text/html \r \n "
< < " Content-Length: 0 \r \n \r \n " ;
2013-01-11 22:18:32 +01:00
mg_write ( connection , out . str ( ) . c_str ( ) , out . str ( ) . size ( ) ) ;
}
std : : string Server : : LookupCommand ( const std : : string & uri ,
const std : : string & http_verb ,
std : : string * session_id ,
std : : string * locator ) {
LOG ( TRACE ) < < " Entering Server::LookupCommand " ;
std : : string value = webdriver : : CommandType : : NoCommand ;
2014-09-11 12:52:08 -04:00
std : : vector < std : : string > url_fragments ;
UriInfo : : ParseUri ( uri , & url_fragments , NULL ) ;
2013-01-11 22:18:32 +01:00
UrlMap : : const_iterator it = this - > commands_ . begin ( ) ;
for ( ; it ! = this - > commands_ . end ( ) ; + + it ) {
2013-02-04 17:51:42 -05:00
std : : vector < std : : string > locator_param_names ;
std : : vector < std : : string > locator_param_values ;
2014-09-11 12:52:08 -04:00
if ( it - > second - > IsUriMatch ( url_fragments ,
& locator_param_names ,
& locator_param_values ) ) {
if ( it - > second - > HasHttpVerb ( http_verb , & value ) ) {
std : : string param = this - > ConstructLocatorParameterJson (
locator_param_names , locator_param_values , session_id ) ;
2013-01-11 22:18:32 +01:00
locator - > append ( param ) ;
} else {
2014-09-11 12:52:08 -04:00
locator - > append ( it - > second - > GetSupportedVerbs ( ) ) ;
2013-01-11 22:18:32 +01:00
}
2014-09-11 12:52:08 -04:00
break ;
2013-01-11 22:18:32 +01:00
}
}
return value ;
}
2014-09-11 12:52:08 -04:00
std : : string Server : : ConstructLocatorParameterJson (
std : : vector < std : : string > locator_param_names ,
std : : vector < std : : string > locator_param_values ,
std : : string * session_id ) {
2013-02-04 17:51:42 -05:00
std : : string param = " { " ;
size_t param_count = locator_param_names . size ( ) ;
for ( unsigned int i = 0 ; i < param_count ; i + + ) {
if ( i ! = 0 ) {
param . append ( " , " ) ;
}
param . append ( " \" " ) ;
param . append ( locator_param_names [ i ] ) ;
param . append ( " \" : \" " ) ;
param . append ( locator_param_values [ i ] ) ;
param . append ( " \" " ) ;
if ( locator_param_names [ i ] = = " sessionid " ) {
session_id - > append ( locator_param_values [ i ] ) ;
}
}
param . append ( " } " ) ;
return param ;
}
2013-01-11 22:18:32 +01:00
void Server : : PopulateCommandRepository ( ) {
LOG ( TRACE ) < < " Entering Server::PopulateCommandRepository " ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session " , " POST " , webdriver : : CommandType : : NewSession ) ;
this - > AddCommand ( " /session/:sessionid " , " DELETE " , webdriver : : CommandType : : Quit ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /status " , " GET " , webdriver : : CommandType : : Status ) ;
this - > AddCommand ( " /session/:sessionid/timeouts " , " GET " , webdriver : : CommandType : : GetTimeouts ) ;
this - > AddCommand ( " /session/:sessionid/timeouts " , " POST " , webdriver : : CommandType : : SetTimeouts ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/url " , " GET " , webdriver : : CommandType : : GetCurrentUrl ) ;
this - > AddCommand ( " /session/:sessionid/url " , " POST " , webdriver : : CommandType : : Get ) ;
this - > AddCommand ( " /session/:sessionid/back " , " POST " , webdriver : : CommandType : : GoBack ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/forward " , " POST " , webdriver : : CommandType : : GoForward ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/refresh " , " POST " , webdriver : : CommandType : : Refresh ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/title " , " GET " , webdriver : : CommandType : : GetTitle ) ;
this - > AddCommand ( " /session/:sessionid/window " , " GET " , webdriver : : CommandType : : GetCurrentWindowHandle ) ;
this - > AddCommand ( " /session/:sessionid/window " , " POST " , webdriver : : CommandType : : SwitchToWindow ) ;
this - > AddCommand ( " /session/:sessionid/window " , " DELETE " , webdriver : : CommandType : : CloseWindow ) ;
this - > AddCommand ( " /session/:sessionid/window/handles " , " GET " , webdriver : : CommandType : : GetWindowHandles ) ;
2019-01-24 07:34:36 -08:00
this - > AddCommand ( " /session/:sessionid/window/new " , " POST " , webdriver : : CommandType : : NewWindow ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/frame " , " POST " , webdriver : : CommandType : : SwitchToFrame ) ;
2014-06-02 12:38:26 -04:00
this - > AddCommand ( " /session/:sessionid/frame/parent " , " POST " , webdriver : : CommandType : : SwitchToParentFrame ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/window/rect " , " GET " , webdriver : : CommandType : : GetWindowRect ) ;
this - > AddCommand ( " /session/:sessionid/window/rect " , " POST " , webdriver : : CommandType : : SetWindowRect ) ;
this - > AddCommand ( " /session/:sessionid/window/maximize " , " POST " , webdriver : : CommandType : : MaximizeWindow ) ;
this - > AddCommand ( " /session/:sessionid/window/minimize " , " POST " , webdriver : : CommandType : : MinimizeWindow ) ;
this - > AddCommand ( " /session/:sessionid/window/fullscreen " , " POST " , webdriver : : CommandType : : FullscreenWindow ) ;
this - > AddCommand ( " /session/:sessionid/element/active " , " GET " , webdriver : : CommandType : : GetActiveElement ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/element " , " POST " , webdriver : : CommandType : : FindElement ) ;
this - > AddCommand ( " /session/:sessionid/elements " , " POST " , webdriver : : CommandType : : FindElements ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/element " , " POST " , webdriver : : CommandType : : FindChildElement ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/elements " , " POST " , webdriver : : CommandType : : FindChildElements ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/element/:id/selected " , " GET " , webdriver : : CommandType : : IsElementSelected ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/attribute/:name " , " GET " , webdriver : : CommandType : : GetElementAttribute ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/property/:name " , " GET " , webdriver : : CommandType : : GetElementProperty ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/css/:propertyName " , " GET " , webdriver : : CommandType : : GetElementValueOfCssProperty ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/element/:id/text " , " GET " , webdriver : : CommandType : : GetElementText ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/name " , " GET " , webdriver : : CommandType : : GetElementTagName ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/element/:id/rect " , " GET " , webdriver : : CommandType : : GetElementRect ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/element/:id/enabled " , " GET " , webdriver : : CommandType : : IsElementEnabled ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /session/:sessionid/element/:id/click " , " POST " , webdriver : : CommandType : : ClickElement ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/clear " , " POST " , webdriver : : CommandType : : ClearElement ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/value " , " POST " , webdriver : : CommandType : : SendKeysToElement ) ;
this - > AddCommand ( " /session/:sessionid/source " , " GET " , webdriver : : CommandType : : GetPageSource ) ;
this - > AddCommand ( " /session/:sessionid/execute/sync " , " POST " , webdriver : : CommandType : : ExecuteScript ) ;
this - > AddCommand ( " /session/:sessionid/execute/async " , " POST " , webdriver : : CommandType : : ExecuteAsyncScript ) ;
this - > AddCommand ( " /session/:sessionid/cookie " , " GET " , webdriver : : CommandType : : GetAllCookies ) ;
this - > AddCommand ( " /session/:sessionid/cookie/:name " , " GET " , webdriver : : CommandType : : GetNamedCookie ) ;
this - > AddCommand ( " /session/:sessionid/cookie " , " POST " , webdriver : : CommandType : : AddCookie ) ;
this - > AddCommand ( " /session/:sessionid/cookie " , " DELETE " , webdriver : : CommandType : : DeleteAllCookies ) ;
this - > AddCommand ( " /session/:sessionid/cookie/:name " , " DELETE " , webdriver : : CommandType : : DeleteNamedCookie ) ;
this - > AddCommand ( " /session/:sessionid/actions " , " POST " , webdriver : : CommandType : : Actions ) ;
this - > AddCommand ( " /session/:sessionid/actions " , " DELETE " , webdriver : : CommandType : : ReleaseActions ) ;
this - > AddCommand ( " /session/:sessionid/alert/dismiss " , " POST " , webdriver : : CommandType : : DismissAlert ) ;
this - > AddCommand ( " /session/:sessionid/alert/accept " , " POST " , webdriver : : CommandType : : AcceptAlert ) ;
this - > AddCommand ( " /session/:sessionid/alert/text " , " GET " , webdriver : : CommandType : : GetAlertText ) ;
this - > AddCommand ( " /session/:sessionid/alert/text " , " POST " , webdriver : : CommandType : : SendKeysToAlert ) ;
this - > AddCommand ( " /session/:sessionid/screenshot " , " GET " , webdriver : : CommandType : : Screenshot ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/screenshot " , " GET " , webdriver : : CommandType : : ElementScreenshot ) ;
// Additional commands required to be supported, but not defined
// in the specification.
this - > AddCommand ( " /session/:sessionid/alert/credentials " , " POST " , webdriver : : CommandType : : SetAlertCredentials ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/element/:id/displayed " , " GET " , webdriver : : CommandType : : IsElementDisplayed ) ;
this - > AddCommand ( " /session/:sessionid/element/:id/equals/:other " , " GET " , webdriver : : CommandType : : ElementEquals ) ;
2017-02-22 14:13:25 -08:00
this - > AddCommand ( " /sessions " , " GET " , webdriver : : CommandType : : GetSessionList ) ;
this - > AddCommand ( " /session/:sessionid " , " GET " , webdriver : : CommandType : : GetSessionCapabilities ) ;
2013-02-04 17:51:42 -05:00
this - > AddCommand ( " /session/:sessionid/ime/available_engines " , " GET " , webdriver : : CommandType : : ListAvailableImeEngines ) ;
this - > AddCommand ( " /session/:sessionid/ime/active_engines " , " GET " , webdriver : : CommandType : : GetActiveImeEngine ) ;
this - > AddCommand ( " /session/:sessionid/ime/activated " , " GET " , webdriver : : CommandType : : IsImeActivated ) ;
this - > AddCommand ( " /session/:sessionid/ime/activate " , " POST " , webdriver : : CommandType : : ActivateImeEngine ) ;
this - > AddCommand ( " /session/:sessionid/ime/deactivate " , " POST " , webdriver : : CommandType : : DeactivateImeEngine ) ;
2013-01-11 22:18:32 +01:00
}
} // namespace webdriver