diff --git a/BUILD.gn b/BUILD.gn index 48eefe8ee..d6a6ae2ed 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -523,6 +523,8 @@ static_library("libcef_static") { "libcef/browser/request_context_impl.cc", "libcef/browser/request_context_impl.h", "libcef/browser/scheme_impl.cc", + "libcef/browser/server_impl.cc", + "libcef/browser/server_impl.h", "libcef/browser/speech_recognition_manager_delegate.cc", "libcef/browser/speech_recognition_manager_delegate.h", "libcef/browser/ssl_host_state_delegate.cc", diff --git a/cef_paths.gypi b/cef_paths.gypi index e8861ded1..8f72dd2d4 100644 --- a/cef_paths.gypi +++ b/cef_paths.gypi @@ -8,7 +8,7 @@ # by hand. See the translator.README.txt file in the tools directory for # more information. # -# $hash=2a6e7c37a8421d3c0d032cc907fb3058790ac71d$ +# $hash=a508906eb96c55e83eda7f17639a9ddcc0632f45$ # { @@ -67,6 +67,7 @@ 'include/cef_response.h', 'include/cef_response_filter.h', 'include/cef_scheme.h', + 'include/cef_server.h', 'include/cef_ssl_info.h', 'include/cef_ssl_status.h', 'include/cef_stream.h', @@ -159,6 +160,7 @@ 'include/capi/cef_response_capi.h', 'include/capi/cef_response_filter_capi.h', 'include/capi/cef_scheme_capi.h', + 'include/capi/cef_server_capi.h', 'include/capi/cef_ssl_info_capi.h', 'include/capi/cef_ssl_status_capi.h', 'include/capi/cef_stream_capi.h', @@ -390,6 +392,10 @@ 'libcef_dll/cpptoc/views/scroll_view_cpptoc.h', 'libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.cc', 'libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h', + 'libcef_dll/cpptoc/server_cpptoc.cc', + 'libcef_dll/cpptoc/server_cpptoc.h', + 'libcef_dll/ctocpp/server_handler_ctocpp.cc', + 'libcef_dll/ctocpp/server_handler_ctocpp.h', 'libcef_dll/ctocpp/set_cookie_callback_ctocpp.cc', 'libcef_dll/ctocpp/set_cookie_callback_ctocpp.h', 'libcef_dll/cpptoc/stream_reader_cpptoc.cc', @@ -672,6 +678,10 @@ 'libcef_dll/ctocpp/views/scroll_view_ctocpp.h', 'libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.cc', 'libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h', + 'libcef_dll/ctocpp/server_ctocpp.cc', + 'libcef_dll/ctocpp/server_ctocpp.h', + 'libcef_dll/cpptoc/server_handler_cpptoc.cc', + 'libcef_dll/cpptoc/server_handler_cpptoc.h', 'libcef_dll/cpptoc/set_cookie_callback_cpptoc.cc', 'libcef_dll/cpptoc/set_cookie_callback_cpptoc.h', 'libcef_dll/ctocpp/stream_reader_ctocpp.cc', diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 81824e1f5..2203acbd3 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -240,6 +240,8 @@ 'tests/cefclient/browser/root_window_manager.h', 'tests/cefclient/browser/scheme_test.cc', 'tests/cefclient/browser/scheme_test.h', + 'tests/cefclient/browser/server_test.cc', + 'tests/cefclient/browser/server_test.h', 'tests/cefclient/browser/temp_window.h', 'tests/cefclient/browser/test_runner.cc', 'tests/cefclient/browser/test_runner.h', @@ -278,8 +280,10 @@ 'tests/cefclient/resources/performance2.html', 'tests/cefclient/resources/preferences.html', 'tests/cefclient/resources/response_filter.html', + 'tests/cefclient/resources/server.html', 'tests/cefclient/resources/transparency.html', 'tests/cefclient/resources/urlrequest.html', + 'tests/cefclient/resources/websocket.html', 'tests/cefclient/resources/window.html', 'tests/cefclient/resources/xmlhttprequest.html', ], @@ -464,6 +468,7 @@ 'tests/ceftests/run_all_unittests.cc', 'tests/ceftests/scheme_handler_unittest.cc', 'tests/ceftests/scoped_temp_dir_unittest.cc', + 'tests/ceftests/server_unittest.cc', 'tests/ceftests/stream_unittest.cc', 'tests/ceftests/stream_resource_handler_unittest.cc', 'tests/ceftests/string_unittest.cc', diff --git a/include/base/cef_basictypes.h b/include/base/cef_basictypes.h index 0086a1dcb..e38f4f729 100644 --- a/include/base/cef_basictypes.h +++ b/include/base/cef_basictypes.h @@ -64,6 +64,16 @@ typedef int int32; typedef unsigned int uint32; #endif +#ifndef _INT16 +#define _INT16 +typedef short int16; +#endif + +#ifndef _UINT16 +#define _UINT16 +typedef unsigned short uint16; +#endif + // UTF-16 character type. // This should be kept synchronized with base/strings/string16.h #ifndef char16 diff --git a/include/capi/cef_request_context_capi.h b/include/capi/cef_request_context_capi.h index f6be925d6..d976b7c91 100644 --- a/include/capi/cef_request_context_capi.h +++ b/include/capi/cef_request_context_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=81e857497b1f5e1732af7fca2250edf78c0e5415$ +// $hash=35342e59ba48fc8b9783fae8e04ddc36448f72d2$ // #ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_ @@ -244,7 +244,7 @@ typedef struct _cef_request_context_t { /// // Clears all active and idle connections that Chromium currently has. This is // only recommended if you have released all other CEF objects but don't yet - // want to call cef_shutdown(). If |callback| is non-NULL it will be executed + // want to call Cefshutdown(). If |callback| is non-NULL it will be executed // on the UI thread after completion. /// void(CEF_CALLBACK* close_all_connections)( diff --git a/include/capi/cef_server_capi.h b/include/capi/cef_server_capi.h new file mode 100644 index 000000000..b1cdabdf2 --- /dev/null +++ b/include/capi/cef_server_capi.h @@ -0,0 +1,327 @@ +// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool and should not edited +// by hand. See the translator.README.txt file in the tools directory for +// more information. +// +// $hash=92c646f789ec75664bf0c9f6bc21a44ff75686cf$ +// + +#ifndef CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_ +#define CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_ +#pragma once + +#include "include/capi/cef_base_capi.h" +#include "include/capi/cef_callback_capi.h" +#include "include/capi/cef_request_capi.h" +#include "include/capi/cef_task_capi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _cef_server_handler_t; + +/// +// Structure representing a server that supports HTTP and WebSocket requests. +// Server capacity is limited and is intended to handle only a small number of +// simultaneous connections (e.g. for communicating between applications on +// localhost). The functions of this structure are safe to call from any thread +// in the brower process unless otherwise indicated. +/// +typedef struct _cef_server_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Returns the task runner for the dedicated server thread. + /// + struct _cef_task_runner_t*(CEF_CALLBACK* get_task_runner)( + struct _cef_server_t* self); + + /// + // Stop the server and shut down the dedicated server thread. See + // cef_server_handler_t::OnServerCreated documentation for a description of + // server lifespan. + /// + void(CEF_CALLBACK* shutdown)(struct _cef_server_t* self); + + /// + // Returns true (1) if the server is currently running and accepting incoming + // connections. See cef_server_handler_t::OnServerCreated documentation for a + // description of server lifespan. This function must be called on the + // dedicated server thread. + /// + int(CEF_CALLBACK* is_running)(struct _cef_server_t* self); + + /// + // Returns the server address including the port number. + /// + // The resulting string must be freed by calling cef_string_userfree_free(). + cef_string_userfree_t(CEF_CALLBACK* get_address)(struct _cef_server_t* self); + + /// + // Returns true (1) if the server currently has a connection. This function + // must be called on the dedicated server thread. + /// + int(CEF_CALLBACK* has_connection)(struct _cef_server_t* self); + + /// + // Returns true (1) if |connection_id| represents a valid connection. This + // function must be called on the dedicated server thread. + /// + int(CEF_CALLBACK* is_valid_connection)(struct _cef_server_t* self, + int connection_id); + + /// + // Send an HTTP 200 "OK" response to the connection identified by + // |connection_id|. |content_type| is the response content type (e.g. + // "text/html"), |data| is the response content, and |data_size| is the size + // of |data| in bytes. The contents of |data| will be copied. The connection + // will be closed automatically after the response is sent. + /// + void(CEF_CALLBACK* send_http200response)(struct _cef_server_t* self, + int connection_id, + const cef_string_t* content_type, + const void* data, + size_t data_size); + + /// + // Send an HTTP 404 "Not Found" response to the connection identified by + // |connection_id|. The connection will be closed automatically after the + // response is sent. + /// + void(CEF_CALLBACK* send_http404response)(struct _cef_server_t* self, + int connection_id); + + /// + // Send an HTTP 500 "Internal Server Error" response to the connection + // identified by |connection_id|. |error_message| is the associated error + // message. The connection will be closed automatically after the response is + // sent. + /// + void(CEF_CALLBACK* send_http500response)(struct _cef_server_t* self, + int connection_id, + const cef_string_t* error_message); + + /// + // Send a custom HTTP response to the connection identified by + // |connection_id|. |response_code| is the HTTP response code sent in the + // status line (e.g. 200), |content_type| is the response content type sent as + // the "Content-Type" header (e.g. "text/html"), |content_length| is the + // expected content length, and |extra_headers| is the map of extra response + // headers. If |content_length| is >= 0 then the "Content-Length" header will + // be sent. If |content_length| is 0 then no content is expected and the + // connection will be closed automatically after the response is sent. If + // |content_length| is < 0 then no "Content-Length" header will be sent and + // the client will continue reading until the connection is closed. Use the + // SendRawData function to send the content, if applicable, and call + // CloseConnection after all content has been sent. + /// + void(CEF_CALLBACK* send_http_response)(struct _cef_server_t* self, + int connection_id, + int response_code, + const cef_string_t* content_type, + int64 content_length, + cef_string_multimap_t extra_headers); + + /// + // Send raw data directly to the connection identified by |connection_id|. + // |data| is the raw data and |data_size| is the size of |data| in bytes. The + // contents of |data| will be copied. No validation of |data| is performed + // internally so the client should be careful to send the amount indicated by + // the "Content-Length" header, if specified. See SendHttpResponse + // documentation for intended usage. + /// + void(CEF_CALLBACK* send_raw_data)(struct _cef_server_t* self, + int connection_id, + const void* data, + size_t data_size); + + /// + // Close the connection identified by |connection_id|. See SendHttpResponse + // documentation for intended usage. + /// + void(CEF_CALLBACK* close_connection)(struct _cef_server_t* self, + int connection_id); + + /// + // Send a WebSocket message to the connection identified by |connection_id|. + // |data| is the response content and |data_size| is the size of |data| in + // bytes. The contents of |data| will be copied. See + // cef_server_handler_t::OnWebSocketRequest documentation for intended usage. + /// + void(CEF_CALLBACK* send_web_socket_message)(struct _cef_server_t* self, + int connection_id, + const void* data, + size_t data_size); +} cef_server_t; + +/// +// Create a new server that binds to |address| and |port|. |address| must be a +// valid IPv4 or IPv6 address (e.g. 127.0.0.1 or ::1) and |port| must be a port +// number outside of the reserved range (e.g. between 1025 and 65535 on most +// platforms). |backlog| is the maximum number of pending connections. A new +// thread will be created for each CreateServer call (the "dedicated server +// thread"). It is therefore recommended to use a different cef_server_handler_t +// instance for each CreateServer call to avoid thread safety issues in the +// cef_server_handler_t implementation. The +// cef_server_handler_t::OnServerCreated function will be called on the +// dedicated server thread to report success or failure. See +// cef_server_handler_t::OnServerCreated documentation for a description of +// server lifespan. +/// +CEF_EXPORT void cef_server_create(const cef_string_t* address, + uint16 port, + int backlog, + struct _cef_server_handler_t* handler); + +/// +// Implement this structure to handle HTTP server requests. A new thread will be +// created for each cef_server_t::CreateServer call (the "dedicated server +// thread"), and the functions of this structure will be called on that thread. +// It is therefore recommended to use a different cef_server_handler_t instance +// for each cef_server_t::CreateServer call to avoid thread safety issues in the +// cef_server_handler_t implementation. +/// +typedef struct _cef_server_handler_t { + /// + // Base structure. + /// + cef_base_ref_counted_t base; + + /// + // Called when |server| is created. If the server was started successfully + // then cef_server_t::IsRunning will return true (1). The server will continue + // running until cef_server_t::Shutdown is called, after which time + // OnServerDestroyed will be called. If the server failed to start then + // OnServerDestroyed will be called immediately after this function returns. + /// + void(CEF_CALLBACK* on_server_created)(struct _cef_server_handler_t* self, + struct _cef_server_t* server); + + /// + // Called when |server| is destroyed. The server thread will be stopped after + // this function returns. The client should release any references to |server| + // when this function is called. See OnServerCreated documentation for a + // description of server lifespan. + /// + void(CEF_CALLBACK* on_server_destroyed)(struct _cef_server_handler_t* self, + struct _cef_server_t* server); + + /// + // Called when a client connects to |server|. |connection_id| uniquely + // identifies the connection. Each call to this function will have a matching + // call to OnClientDisconnected. + /// + void(CEF_CALLBACK* on_client_connected)(struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id); + + /// + // Called when a client disconnects from |server|. |connection_id| uniquely + // identifies the connection. The client should release any data associated + // with |connection_id| when this function is called and |connection_id| + // should no longer be passed to cef_server_t functions. Disconnects can + // originate from either the client or the server. For example, the server + // will disconnect automatically after a cef_server_t::SendHttpXXXResponse + // function is called. + /// + void(CEF_CALLBACK* on_client_disconnected)(struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id); + + /// + // Called when |server| receives an HTTP request. |connection_id| uniquely + // identifies the connection, |client_address| is the requesting IPv4 or IPv6 + // client address including port number, and |request| contains the request + // contents (URL, function, headers and optional POST data). Call cef_server_t + // functions either synchronously or asynchronusly to send a response. + /// + void(CEF_CALLBACK* on_http_request)(struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id, + const cef_string_t* client_address, + struct _cef_request_t* request); + + /// + // Called when |server| receives a WebSocket request. |connection_id| uniquely + // identifies the connection, |client_address| is the requesting IPv4 or IPv6 + // client address including port number, and |request| contains the request + // contents (URL, function, headers and optional POST data). Execute + // |callback| either synchronously or asynchronously to accept or decline the + // WebSocket connection. If the request is accepted then OnWebSocketConnected + // will be called after the WebSocket has connected and incoming messages will + // be delivered to the OnWebSocketMessage callback. If the request is declined + // then the client will be disconnected and OnClientDisconnected will be + // called. Call the cef_server_t::SendWebSocketMessage function after + // receiving the OnWebSocketConnected callback to respond with WebSocket + // messages. + /// + void(CEF_CALLBACK* on_web_socket_request)(struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id, + const cef_string_t* client_address, + struct _cef_request_t* request, + struct _cef_callback_t* callback); + + /// + // Called after the client has accepted the WebSocket connection for |server| + // and |connection_id| via the OnWebSocketRequest callback. See + // OnWebSocketRequest documentation for intended usage. + /// + void(CEF_CALLBACK* on_web_socket_connected)( + struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id); + + /// + // Called when |server| receives an WebSocket message. |connection_id| + // uniquely identifies the connection, |data| is the message content and + // |data_size| is the size of |data| in bytes. Do not keep a reference to + // |data| outside of this function. See OnWebSocketRequest documentation for + // intended usage. + /// + void(CEF_CALLBACK* on_web_socket_message)(struct _cef_server_handler_t* self, + struct _cef_server_t* server, + int connection_id, + const void* data, + size_t data_size); +} cef_server_handler_t; + +#ifdef __cplusplus +} +#endif + +#endif // CEF_INCLUDE_CAPI_CEF_SERVER_CAPI_H_ diff --git a/include/cef_server.h b/include/cef_server.h new file mode 100644 index 000000000..e22b8a6bf --- /dev/null +++ b/include/cef_server.h @@ -0,0 +1,315 @@ +// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// The contents of this file must follow a specific format in order to +// support the CEF translator tool. See the translator.README.txt file in the +// tools directory for more information. +// + +#ifndef CEF_INCLUDE_CEF_SERVER_H_ +#define CEF_INCLUDE_CEF_SERVER_H_ +#pragma once + +#include +#include "include/cef_base.h" +#include "include/cef_callback.h" +#include "include/cef_request.h" +#include "include/cef_task.h" + +class CefServerHandler; + +/// +// Class representing a server that supports HTTP and WebSocket requests. Server +// capacity is limited and is intended to handle only a small number of +// simultaneous connections (e.g. for communicating between applications on +// localhost). The methods of this class are safe to call from any thread in the +// brower process unless otherwise indicated. +/// +/*--cef(source=library)--*/ +class CefServer : public CefBaseRefCounted { + public: + typedef std::multimap HeaderMap; + + /// + // Create a new server that binds to |address| and |port|. |address| must be a + // valid IPv4 or IPv6 address (e.g. 127.0.0.1 or ::1) and |port| must be a + // port number outside of the reserved range (e.g. between 1025 and 65535 on + // most platforms). |backlog| is the maximum number of pending connections. + // A new thread will be created for each CreateServer call (the "dedicated + // server thread"). It is therefore recommended to use a different + // CefServerHandler instance for each CreateServer call to avoid thread safety + // issues in the CefServerHandler implementation. The + // CefServerHandler::OnServerCreated method will be called on the dedicated + // server thread to report success or failure. See + // CefServerHandler::OnServerCreated documentation for a description of server + // lifespan. + /// + /*--cef()--*/ + static void CreateServer(const CefString& address, + uint16 port, + int backlog, + CefRefPtr handler); + + /// + // Returns the task runner for the dedicated server thread. + /// + /*--cef()--*/ + virtual CefRefPtr GetTaskRunner() = 0; + + /// + // Stop the server and shut down the dedicated server thread. See + // CefServerHandler::OnServerCreated documentation for a description of + // server lifespan. + /// + /*--cef()--*/ + virtual void Shutdown() = 0; + + /// + // Returns true if the server is currently running and accepting incoming + // connections. See CefServerHandler::OnServerCreated documentation for a + // description of server lifespan. This method must be called on the dedicated + // server thread. + /// + /*--cef()--*/ + virtual bool IsRunning() = 0; + + /// + // Returns the server address including the port number. + /// + /*--cef()--*/ + virtual CefString GetAddress() = 0; + + /// + // Returns true if the server currently has a connection. This method must be + // called on the dedicated server thread. + /// + /*--cef()--*/ + virtual bool HasConnection() = 0; + + /// + // Returns true if |connection_id| represents a valid connection. This method + // must be called on the dedicated server thread. + /// + /*--cef()--*/ + virtual bool IsValidConnection(int connection_id) = 0; + + /// + // Send an HTTP 200 "OK" response to the connection identified by + // |connection_id|. |content_type| is the response content type (e.g. + // "text/html"), |data| is the response content, and |data_size| is the size + // of |data| in bytes. The contents of |data| will be copied. The connection + // will be closed automatically after the response is sent. + /// + /*--cef()--*/ + virtual void SendHttp200Response(int connection_id, + const CefString& content_type, + const void* data, + size_t data_size) = 0; + + /// + // Send an HTTP 404 "Not Found" response to the connection identified by + // |connection_id|. The connection will be closed automatically after the + // response is sent. + /// + /*--cef()--*/ + virtual void SendHttp404Response(int connection_id) = 0; + + /// + // Send an HTTP 500 "Internal Server Error" response to the connection + // identified by |connection_id|. |error_message| is the associated error + // message. The connection will be closed automatically after the response is + // sent. + /// + /*--cef()--*/ + virtual void SendHttp500Response(int connection_id, + const CefString& error_message) = 0; + + /// + // Send a custom HTTP response to the connection identified by + // |connection_id|. |response_code| is the HTTP response code sent in the + // status line (e.g. 200), |content_type| is the response content type sent + // as the "Content-Type" header (e.g. "text/html"), |content_length| is the + // expected content length, and |extra_headers| is the map of extra response + // headers. If |content_length| is >= 0 then the "Content-Length" header will + // be sent. If |content_length| is 0 then no content is expected and the + // connection will be closed automatically after the response is sent. If + // |content_length| is < 0 then no "Content-Length" header will be sent and + // the client will continue reading until the connection is closed. Use the + // SendRawData method to send the content, if applicable, and call + // CloseConnection after all content has been sent. + /// + /*--cef(optional_param=extra_headers)--*/ + virtual void SendHttpResponse(int connection_id, + int response_code, + const CefString& content_type, + int64 content_length, + const HeaderMap& extra_headers) = 0; + + /// + // Send raw data directly to the connection identified by |connection_id|. + // |data| is the raw data and |data_size| is the size of |data| in bytes. + // The contents of |data| will be copied. No validation of |data| is + // performed internally so the client should be careful to send the amount + // indicated by the "Content-Length" header, if specified. See + // SendHttpResponse documentation for intended usage. + /// + /*--cef()--*/ + virtual void SendRawData(int connection_id, + const void* data, + size_t data_size) = 0; + + /// + // Close the connection identified by |connection_id|. See SendHttpResponse + // documentation for intended usage. + /// + /*--cef()--*/ + virtual void CloseConnection(int connection_id) = 0; + + /// + // Send a WebSocket message to the connection identified by |connection_id|. + // |data| is the response content and |data_size| is the size of |data| in + // bytes. The contents of |data| will be copied. See + // CefServerHandler::OnWebSocketRequest documentation for intended usage. + /// + /*--cef()--*/ + virtual void SendWebSocketMessage(int connection_id, + const void* data, + size_t data_size) = 0; +}; + +/// +// Implement this interface to handle HTTP server requests. A new thread will be +// created for each CefServer::CreateServer call (the "dedicated server +// thread"), and the methods of this class will be called on that thread. It is +// therefore recommended to use a different CefServerHandler instance for each +// CefServer::CreateServer call to avoid thread safety issues in the +// CefServerHandler implementation. +/// +/*--cef(source=client)--*/ +class CefServerHandler : public virtual CefBaseRefCounted { + public: + /// + // Called when |server| is created. If the server was started successfully + // then CefServer::IsRunning will return true. The server will continue + // running until CefServer::Shutdown is called, after which time + // OnServerDestroyed will be called. If the server failed to start then + // OnServerDestroyed will be called immediately after this method returns. + /// + /*--cef()--*/ + virtual void OnServerCreated(CefRefPtr server) = 0; + + /// + // Called when |server| is destroyed. The server thread will be stopped after + // this method returns. The client should release any references to |server| + // when this method is called. See OnServerCreated documentation for a + // description of server lifespan. + /// + /*--cef()--*/ + virtual void OnServerDestroyed(CefRefPtr server) = 0; + + /// + // Called when a client connects to |server|. |connection_id| uniquely + // identifies the connection. Each call to this method will have a matching + // call to OnClientDisconnected. + /// + /*--cef()--*/ + virtual void OnClientConnected(CefRefPtr server, + int connection_id) = 0; + + /// + // Called when a client disconnects from |server|. |connection_id| uniquely + // identifies the connection. The client should release any data associated + // with |connection_id| when this method is called and |connection_id| should + // no longer be passed to CefServer methods. Disconnects can originate from + // either the client or the server. For example, the server will disconnect + // automatically after a CefServer::SendHttpXXXResponse method is called. + /// + /*--cef()--*/ + virtual void OnClientDisconnected(CefRefPtr server, + int connection_id) = 0; + + /// + // Called when |server| receives an HTTP request. |connection_id| uniquely + // identifies the connection, |client_address| is the requesting IPv4 or IPv6 + // client address including port number, and |request| contains the request + // contents (URL, method, headers and optional POST data). Call CefServer + // methods either synchronously or asynchronusly to send a response. + /// + /*--cef()--*/ + virtual void OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) = 0; + + /// + // Called when |server| receives a WebSocket request. |connection_id| uniquely + // identifies the connection, |client_address| is the requesting IPv4 or + // IPv6 client address including port number, and |request| contains the + // request contents (URL, method, headers and optional POST data). Execute + // |callback| either synchronously or asynchronously to accept or decline the + // WebSocket connection. If the request is accepted then OnWebSocketConnected + // will be called after the WebSocket has connected and incoming messages will + // be delivered to the OnWebSocketMessage callback. If the request is declined + // then the client will be disconnected and OnClientDisconnected will be + // called. Call the CefServer::SendWebSocketMessage method after receiving the + // OnWebSocketConnected callback to respond with WebSocket messages. + /// + /*--cef()--*/ + virtual void OnWebSocketRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request, + CefRefPtr callback) = 0; + + /// + // Called after the client has accepted the WebSocket connection for |server| + // and |connection_id| via the OnWebSocketRequest callback. See + // OnWebSocketRequest documentation for intended usage. + /// + /*--cef()--*/ + virtual void OnWebSocketConnected(CefRefPtr server, + int connection_id) = 0; + + /// + // Called when |server| receives an WebSocket message. |connection_id| + // uniquely identifies the connection, |data| is the message content and + // |data_size| is the size of |data| in bytes. Do not keep a reference to + // |data| outside of this method. See OnWebSocketRequest documentation for + // intended usage. + /// + /*--cef()--*/ + virtual void OnWebSocketMessage(CefRefPtr server, + int connection_id, + const void* data, + size_t data_size) = 0; +}; + +#endif // CEF_INCLUDE_CEF_SERVER_H_ diff --git a/libcef/browser/server_impl.cc b/libcef/browser/server_impl.cc new file mode 100644 index 000000000..e92264e7f --- /dev/null +++ b/libcef/browser/server_impl.cc @@ -0,0 +1,643 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "libcef/browser/server_impl.h" + +#include "libcef/browser/thread_util.h" +#include "libcef/common/request_impl.h" +#include "libcef/common/task_runner_impl.h" + +#include "base/bind.h" +#include "base/format_macros.h" +#include "base/memory/ptr_util.h" +#include "base/strings/stringprintf.h" +#include "base/task_scheduler/post_task.h" +#include "base/threading/thread.h" +#include "net/base/net_errors.h" +#include "net/http/http_request_headers.h" +#include "net/server/http_server_request_info.h" +#include "net/server/http_server_response_info.h" +#include "net/socket/server_socket.h" +#include "net/socket/tcp_server_socket.h" + +#define CEF_CURRENTLY_ON_HT() CurrentlyOnHandlerThread() +#define CEF_REQUIRE_HT() DCHECK(CEF_CURRENTLY_ON_HT()) + +#define CEF_REQUIRE_HT_RETURN(var) \ + if (!CEF_CURRENTLY_ON_HT()) { \ + NOTREACHED() << "called on invalid thread"; \ + return var; \ + } + +#define CEF_REQUIRE_HT_RETURN_VOID() \ + if (!CEF_CURRENTLY_ON_HT()) { \ + NOTREACHED() << "called on invalid thread"; \ + return; \ + } + +#define CEF_POST_TASK_HT(task) task_runner_->PostTask(FROM_HERE, task); + +namespace { + +// Wrap a string in a unique_ptr to avoid extra copies. +std::unique_ptr CreateUniqueString(const void* data, + size_t data_size) { + std::unique_ptr ptr; + if (data && data_size > 0) { + ptr.reset(new std::string(static_cast(data), data_size)); + } else { + ptr.reset(new std::string()); + } + return ptr; +} + +CefRefPtr CreateRequest(const std::string& address, + const net::HttpServerRequestInfo& info, + bool is_websocket) { + DCHECK(!address.empty()); + DCHECK(!info.method.empty()); + DCHECK(!info.path.empty()); + + CefRefPtr post_data; + if (!info.data.empty()) { + post_data = CefPostData::Create(); + CefRefPtr post_element = CefPostDataElement::Create(); + post_element->SetToBytes(info.data.size(), info.data.data()); + post_data->AddElement(post_element); + } + + CefRequest::HeaderMap header_map; + if (!info.headers.empty()) { + net::HttpServerRequestInfo::HeadersMap::const_iterator it = + info.headers.begin(); + for (; it != info.headers.end(); ++it) { + header_map.insert(std::make_pair(it->first, it->second)); + } + } + + CefRefPtr request = new CefRequestImpl(); + request->Set((is_websocket ? "ws://" : "http://") + address + info.path, + info.method, post_data, header_map); + request->SetReadOnly(true); + return request; +} + +// Callback implementation for WebSocket acceptance. Always executes on the UI +// thread so we can avoid multiple execution by clearing the |impl_| reference. +class AcceptWebSocketCallback : public CefCallback { + public: + AcceptWebSocketCallback(CefRefPtr impl, + int connection_id, + net::HttpServerRequestInfo request_info) + : impl_(impl), + connection_id_(connection_id), + request_info_(request_info) {} + + ~AcceptWebSocketCallback() override { + if (impl_) + impl_->ContinueWebSocketRequest(connection_id_, request_info_, false); + } + + void Continue() override { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&AcceptWebSocketCallback::Continue, this)); + return; + } + if (!impl_) + return; + impl_->ContinueWebSocketRequest(connection_id_, request_info_, true); + impl_ = nullptr; + } + + void Cancel() override { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&AcceptWebSocketCallback::Cancel, this)); + return; + } + if (!impl_) + return; + impl_->ContinueWebSocketRequest(connection_id_, request_info_, false); + impl_ = nullptr; + } + + private: + CefRefPtr impl_; + int connection_id_; + net::HttpServerRequestInfo request_info_; + + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(AcceptWebSocketCallback); + DISALLOW_COPY_AND_ASSIGN(AcceptWebSocketCallback); +}; + +} // namespace + +// CefServer + +// static +void CefServer::CreateServer(const CefString& address, + uint16 port, + int backlog, + CefRefPtr handler) { + CefRefPtr server(new CefServerImpl(handler)); + server->Start(address, port, backlog); +} + +// CefServerImpl + +struct CefServerImpl::ConnectionInfo { + ConnectionInfo() : is_websocket(false), is_websocket_pending(false) {} + + // True if this connection is a WebSocket connection. + bool is_websocket; + bool is_websocket_pending; +}; + +CefServerImpl::CefServerImpl(CefRefPtr handler) + : handler_(handler) { + DCHECK(handler_); +} + +void CefServerImpl::Start(const std::string& address, + uint16 port, + int backlog) { + DCHECK(!address.empty()); + CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefServerImpl::StartOnUIThread, this, + address, port, backlog)); +} + +CefRefPtr CefServerImpl::GetTaskRunner() { + if (task_runner_) + return new CefTaskRunnerImpl(task_runner_); + return nullptr; +} + +void CefServerImpl::Shutdown() { + CEF_POST_TASK_HT( + base::BindOnce(&CefServerImpl::ShutdownOnHandlerThread, this)); +} + +bool CefServerImpl::IsRunning() { + CEF_REQUIRE_HT_RETURN(false); + return !!server_.get(); +} + +CefString CefServerImpl::GetAddress() { + return address_; +} + +bool CefServerImpl::HasConnection() { + CEF_REQUIRE_HT_RETURN(false); + return !connection_info_map_.empty(); +} + +bool CefServerImpl::IsValidConnection(int connection_id) { + CEF_REQUIRE_HT_RETURN(false); + return connection_info_map_.find(connection_id) != connection_info_map_.end(); +} + +void CefServerImpl::SendHttp200Response(int connection_id, + const CefString& content_type, + const void* data, + size_t data_size) { + SendHttp200ResponseInternal(connection_id, content_type, + CreateUniqueString(data, data_size)); +} + +void CefServerImpl::SendHttp404Response(int connection_id) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp404Response, this, + connection_id)); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + if (info->is_websocket) { + LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id " + << connection_id; + return; + } + + server_->Send404(connection_id); + server_->Close(connection_id); +} + +void CefServerImpl::SendHttp500Response(int connection_id, + const CefString& error_message) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp500Response, this, + connection_id, error_message)); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + if (info->is_websocket) { + LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id " + << connection_id; + return; + } + + server_->Send500(connection_id, error_message); + server_->Close(connection_id); +} + +void CefServerImpl::SendHttpResponse(int connection_id, + int response_code, + const CefString& content_type, + int64 content_length, + const HeaderMap& extra_headers) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttpResponse, this, + connection_id, response_code, content_type, + content_length, extra_headers)); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + if (info->is_websocket) { + LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id " + << connection_id; + return; + } + + net::HttpServerResponseInfo response( + static_cast(response_code)); + + HeaderMap::const_iterator it = extra_headers.begin(); + for (; it != extra_headers.end(); ++it) + response.AddHeader(it->first, it->second); + + response.AddHeader(net::HttpRequestHeaders::kContentType, content_type); + if (content_length >= 0) { + response.AddHeader( + net::HttpRequestHeaders::kContentLength, + base::StringPrintf("%" PRIuS, static_cast(content_length))); + } + + server_->SendResponse(connection_id, response); + if (content_length == 0) { + server_->Close(connection_id); + } +} + +void CefServerImpl::SendRawData(int connection_id, + const void* data, + size_t data_size) { + if (!data || data_size == 0) + return; + SendRawDataInternal(connection_id, CreateUniqueString(data, data_size)); +} + +void CefServerImpl::CloseConnection(int connection_id) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT( + base::BindOnce(&CefServerImpl::CloseConnection, this, connection_id)); + return; + } + + if (ValidateServer() && GetConnectionInfo(connection_id)) { + server_->Close(connection_id); + } +} + +void CefServerImpl::SendWebSocketMessage(int connection_id, + const void* data, + size_t data_size) { + if (!data || data_size == 0) + return; + SendWebSocketMessageInternal(connection_id, + CreateUniqueString(data, data_size)); +} + +void CefServerImpl::ContinueWebSocketRequest( + int connection_id, + const net::HttpServerRequestInfo& request_info, + bool allow) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::ContinueWebSocketRequest, + this, connection_id, request_info, allow)); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + DCHECK(info); + if (!info) + return; + + DCHECK(info->is_websocket); + DCHECK(info->is_websocket_pending); + if (!info->is_websocket || !info->is_websocket_pending) + return; + + info->is_websocket_pending = false; + + if (allow) { + server_->AcceptWebSocket(connection_id, request_info); + handler_->OnWebSocketConnected(this, connection_id); + } else { + server_->Close(connection_id); + } +} + +void CefServerImpl::SendHttp200ResponseInternal( + int connection_id, + const CefString& content_type, + std::unique_ptr data) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp200ResponseInternal, + this, connection_id, content_type, + base::Passed(std::move(data)))); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + if (info->is_websocket) { + LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id " + << connection_id; + return; + } + + server_->Send200(connection_id, *data, content_type); + server_->Close(connection_id); +} + +void CefServerImpl::SendRawDataInternal(int connection_id, + std::unique_ptr data) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendRawDataInternal, this, + connection_id, + base::Passed(std::move(data)))); + return; + } + + if (!ValidateServer()) + return; + + if (!GetConnectionInfo(connection_id)) + return; + + server_->SendRaw(connection_id, *data); +} + +void CefServerImpl::SendWebSocketMessageInternal( + int connection_id, + std::unique_ptr data) { + if (!CEF_CURRENTLY_ON_HT()) { + CEF_POST_TASK_HT( + base::BindOnce(&CefServerImpl::SendWebSocketMessageInternal, this, + connection_id, base::Passed(std::move(data)))); + return; + } + + if (!ValidateServer()) + return; + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + if (!info->is_websocket || info->is_websocket_pending) { + LOG(ERROR) << "Invalid attempt to send WebSocket message for connection_id " + << connection_id; + return; + } + + server_->SendOverWebSocket(connection_id, *data); +} + +void CefServerImpl::OnConnect(int connection_id) { + CEF_REQUIRE_HT(); + + CreateConnectionInfo(connection_id); + handler_->OnClientConnected(this, connection_id); +} + +void CefServerImpl::OnHttpRequest( + int connection_id, + const net::HttpServerRequestInfo& request_info) { + CEF_REQUIRE_HT(); + + ConnectionInfo* info = GetConnectionInfo(connection_id); + DCHECK(info); + if (!info) + return; + + DCHECK(!info->is_websocket); + + handler_->OnHttpRequest(this, connection_id, request_info.peer.ToString(), + CreateRequest(address_, request_info, false)); +} + +void CefServerImpl::OnWebSocketRequest( + int connection_id, + const net::HttpServerRequestInfo& request_info) { + CEF_REQUIRE_HT(); + + ConnectionInfo* info = GetConnectionInfo(connection_id); + DCHECK(info); + if (!info) + return; + + DCHECK(!info->is_websocket); + info->is_websocket = true; + info->is_websocket_pending = true; + + // Will eventually result in a call to ContinueWebSocketRequest. + CefRefPtr callback = + new AcceptWebSocketCallback(this, connection_id, request_info); + handler_->OnWebSocketRequest( + this, connection_id, request_info.peer.ToString(), + CreateRequest(address_, request_info, true), callback); +} + +void CefServerImpl::OnWebSocketMessage(int connection_id, + const std::string& data) { + CEF_REQUIRE_HT(); + + ConnectionInfo* info = GetConnectionInfo(connection_id); + if (!info) + return; + + DCHECK(info->is_websocket); + DCHECK(!info->is_websocket_pending); + + handler_->OnWebSocketMessage(this, connection_id, data.data(), data.size()); +} + +void CefServerImpl::OnClose(int connection_id) { + CEF_REQUIRE_HT(); + + RemoveConnectionInfo(connection_id); + handler_->OnClientDisconnected(this, connection_id); +} + +void CefServerImpl::StartOnUIThread(const std::string& address, + uint16 port, + int backlog) { + CEF_REQUIRE_UIT(); + DCHECK(!thread_); + + std::unique_ptr thread( + new base::Thread(base::StringPrintf("%s:%d", address.c_str(), port))); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + if (thread->StartWithOptions(options)) { + // Add a reference that will be released in ShutdownOnUIThread(). + AddRef(); + + thread_ = std::move(thread); + task_runner_ = thread_->task_runner(); + + CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::StartOnHandlerThread, this, + address, port, backlog)); + } +} + +void CefServerImpl::StartOnHandlerThread(const std::string& address, + uint16 port, + int backlog) { + CEF_REQUIRE_HT(); + + std::unique_ptr socket( + new net::TCPServerSocket(nullptr, net::NetLogSource())); + if (socket->ListenWithAddressAndPort(address, port, backlog) == net::OK) { + server_.reset(new net::HttpServer(std::move(socket), this)); + + net::IPEndPoint address; + if (server_->GetLocalAddress(&address) == net::OK) + address_ = address.ToString(); + } + + handler_->OnServerCreated(this); + + if (!server_) { + // Server failed to start. + handler_->OnServerDestroyed(this); + + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this)); + } +} + +void CefServerImpl::ShutdownOnHandlerThread() { + CEF_REQUIRE_HT(); + + if (server_) { + // Stop the server. + server_.reset(); + + if (!connection_info_map_.empty()) { + // Clear |connection_info_map_| first so any calls from + // OnClientDisconnected will fail as expected. + ConnectionInfoMap temp_map; + temp_map.swap(connection_info_map_); + + // OnClose won't be called for clients that are connected when the server + // shuts down, so send the disconnected notification here. + ConnectionInfoMap::const_iterator it = temp_map.begin(); + for (; it != temp_map.end(); ++it) { + handler_->OnClientDisconnected(this, it->first); + } + } + + handler_->OnServerDestroyed(this); + } + + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this)); +} + +void CefServerImpl::ShutdownOnUIThread() { + CEF_REQUIRE_UIT(); + + handler_ = nullptr; + + if (thread_) { + // Stop the handler thread as a background task so the UI thread isn't + // blocked. + base::PostTaskWithTraits( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + BindOnce([](std::unique_ptr) {}, std::move(thread_))); + + // Release the reference that was added in StartupOnUIThread(). + Release(); + } +} + +bool CefServerImpl::ValidateServer() const { + CEF_REQUIRE_HT(); + if (!server_) { + LOG(ERROR) << "Server is not running"; + return false; + } + return true; +} + +CefServerImpl::ConnectionInfo* CefServerImpl::CreateConnectionInfo( + int connection_id) { + CEF_REQUIRE_HT(); + +#if DCHECK_IS_ON() + ConnectionInfoMap::const_iterator it = + connection_info_map_.find(connection_id); + DCHECK(it == connection_info_map_.end()); +#endif + + ConnectionInfo* info = new ConnectionInfo(); + connection_info_map_.insert( + std::make_pair(connection_id, base::WrapUnique(info))); + return info; +} + +CefServerImpl::ConnectionInfo* CefServerImpl::GetConnectionInfo( + int connection_id) const { + CEF_REQUIRE_HT(); + ConnectionInfoMap::const_iterator it = + connection_info_map_.find(connection_id); + if (it != connection_info_map_.end()) + return it->second.get(); + + LOG(ERROR) << "Invalid connection_id " << connection_id; + return nullptr; +} + +void CefServerImpl::RemoveConnectionInfo(int connection_id) { + CEF_REQUIRE_HT(); + ConnectionInfoMap::iterator it = connection_info_map_.find(connection_id); + DCHECK(it != connection_info_map_.end()); + if (it != connection_info_map_.end()) + connection_info_map_.erase(it); +} + +bool CefServerImpl::CurrentlyOnHandlerThread() const { + return task_runner_ && task_runner_->BelongsToCurrentThread(); +} diff --git a/libcef/browser/server_impl.h b/libcef/browser/server_impl.h new file mode 100644 index 000000000..77157e3fd --- /dev/null +++ b/libcef/browser/server_impl.h @@ -0,0 +1,113 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_SERVER_IMPL_H_ +#define CEF_LIBCEF_BROWSER_SERVER_IMPL_H_ +#pragma once + +#include +#include + +#include "include/cef_server.h" + +#include "base/single_thread_task_runner.h" +#include "net/server/http_server.h" + +namespace base { +class Thread; +}; + +class CefServerImpl : public CefServer, net::HttpServer::Delegate { + public: + explicit CefServerImpl(CefRefPtr handler); + + void Start(const std::string& address, uint16 port, int backlog); + + // CefServer methods: + CefRefPtr GetTaskRunner() override; + void Shutdown() override; + bool IsRunning() override; + CefString GetAddress() override; + bool HasConnection() override; + bool IsValidConnection(int connection_id) override; + void SendHttp200Response(int connection_id, + const CefString& content_type, + const void* data, + size_t data_size) override; + void SendHttp404Response(int connection_id) override; + void SendHttp500Response(int connection_id, + const CefString& error_message) override; + void SendHttpResponse(int connection_id, + int response_code, + const CefString& content_type, + int64 content_length, + const HeaderMap& extra_headers) override; + void SendRawData(int connection_id, + const void* data, + size_t data_size) override; + void CloseConnection(int connection_id) override; + void SendWebSocketMessage(int connection_id, + const void* data, + size_t data_size) override; + + void ContinueWebSocketRequest(int connection_id, + const net::HttpServerRequestInfo& request_info, + bool allow); + + private: + void SendHttp200ResponseInternal(int connection_id, + const CefString& content_type, + std::unique_ptr data); + void SendRawDataInternal(int connection_id, + std::unique_ptr data); + void SendWebSocketMessageInternal(int connection_id, + std::unique_ptr data); + + // HttpServer::Delegate methods: + void OnConnect(int connection_id) override; + void OnHttpRequest(int connection_id, + const net::HttpServerRequestInfo& request_info) override; + void OnWebSocketRequest( + int connection_id, + const net::HttpServerRequestInfo& request_info) override; + void OnWebSocketMessage(int connection_id, const std::string& data) override; + void OnClose(int connection_id) override; + + void StartOnUIThread(const std::string& address, uint16 port, int backlog); + void StartOnHandlerThread(const std::string& address, + uint16 port, + int backlog); + + void ShutdownOnHandlerThread(); + void ShutdownOnUIThread(); + + bool ValidateServer() const; + + struct ConnectionInfo; + ConnectionInfo* CreateConnectionInfo(int connection_id); + ConnectionInfo* GetConnectionInfo(int connection_id) const; + void RemoveConnectionInfo(int connection_id); + + bool CurrentlyOnHandlerThread() const; + + // Safe to access from any thread. + scoped_refptr task_runner_; + std::string address_; + + // Only accessed on the UI thread. + std::unique_ptr thread_; + + // Only accessed on the server thread. + CefRefPtr handler_; + std::unique_ptr server_; + + // Map of connection_id to ConnectionInfo. + using ConnectionInfoMap = std::map>; + ConnectionInfoMap connection_info_map_; + + IMPLEMENT_REFCOUNTING(CefServerImpl); + DISALLOW_COPY_AND_ASSIGN(CefServerImpl); +}; + +#endif // CEF_LIBCEF_BROWSER_SERVER_IMPL_H_ diff --git a/libcef_dll/cpptoc/server_cpptoc.cc b/libcef_dll/cpptoc/server_cpptoc.cc new file mode 100644 index 000000000..5c075e02d --- /dev/null +++ b/libcef_dll/cpptoc/server_cpptoc.cc @@ -0,0 +1,301 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=aa194ad76a31d7a0497333d78595e8b349a0551e$ +// + +#include "libcef_dll/cpptoc/server_cpptoc.h" +#include "libcef_dll/cpptoc/task_runner_cpptoc.h" +#include "libcef_dll/ctocpp/server_handler_ctocpp.h" +#include "libcef_dll/transfer_util.h" + +// GLOBAL FUNCTIONS - Body may be edited by hand. + +CEF_EXPORT void cef_server_create(const cef_string_t* address, + uint16 port, + int backlog, + struct _cef_server_handler_t* handler) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: address; type: string_byref_const + DCHECK(address); + if (!address) + return; + // Verify param: handler; type: refptr_diff + DCHECK(handler); + if (!handler) + return; + + // Execute + CefServer::CreateServer(CefString(address), port, backlog, + CefServerHandlerCToCpp::Wrap(handler)); +} + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +struct _cef_task_runner_t* CEF_CALLBACK +server_get_task_runner(struct _cef_server_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return NULL; + + // Execute + CefRefPtr _retval = + CefServerCppToC::Get(self)->GetTaskRunner(); + + // Return type: refptr_same + return CefTaskRunnerCppToC::Wrap(_retval); +} + +void CEF_CALLBACK server_shutdown(struct _cef_server_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefServerCppToC::Get(self)->Shutdown(); +} + +int CEF_CALLBACK server_is_running(struct _cef_server_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefServerCppToC::Get(self)->IsRunning(); + + // Return type: bool + return _retval; +} + +cef_string_userfree_t CEF_CALLBACK +server_get_address(struct _cef_server_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return NULL; + + // Execute + CefString _retval = CefServerCppToC::Get(self)->GetAddress(); + + // Return type: string + return _retval.DetachToUserFree(); +} + +int CEF_CALLBACK server_has_connection(struct _cef_server_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefServerCppToC::Get(self)->HasConnection(); + + // Return type: bool + return _retval; +} + +int CEF_CALLBACK server_is_valid_connection(struct _cef_server_t* self, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + + // Execute + bool _retval = CefServerCppToC::Get(self)->IsValidConnection(connection_id); + + // Return type: bool + return _retval; +} + +void CEF_CALLBACK server_send_http200response(struct _cef_server_t* self, + int connection_id, + const cef_string_t* content_type, + const void* data, + size_t data_size) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: content_type; type: string_byref_const + DCHECK(content_type); + if (!content_type) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + CefServerCppToC::Get(self)->SendHttp200Response( + connection_id, CefString(content_type), data, data_size); +} + +void CEF_CALLBACK server_send_http404response(struct _cef_server_t* self, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefServerCppToC::Get(self)->SendHttp404Response(connection_id); +} + +void CEF_CALLBACK +server_send_http500response(struct _cef_server_t* self, + int connection_id, + const cef_string_t* error_message) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: error_message; type: string_byref_const + DCHECK(error_message); + if (!error_message) + return; + + // Execute + CefServerCppToC::Get(self)->SendHttp500Response(connection_id, + CefString(error_message)); +} + +void CEF_CALLBACK +server_send_http_response(struct _cef_server_t* self, + int connection_id, + int response_code, + const cef_string_t* content_type, + int64 content_length, + cef_string_multimap_t extra_headers) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: content_type; type: string_byref_const + DCHECK(content_type); + if (!content_type) + return; + // Unverified params: extra_headers + + // Translate param: extra_headers; type: string_map_multi_byref_const + std::multimap extra_headersMultimap; + transfer_string_multimap_contents(extra_headers, extra_headersMultimap); + + // Execute + CefServerCppToC::Get(self)->SendHttpResponse( + connection_id, response_code, CefString(content_type), content_length, + extra_headersMultimap); +} + +void CEF_CALLBACK server_send_raw_data(struct _cef_server_t* self, + int connection_id, + const void* data, + size_t data_size) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + CefServerCppToC::Get(self)->SendRawData(connection_id, data, data_size); +} + +void CEF_CALLBACK server_close_connection(struct _cef_server_t* self, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefServerCppToC::Get(self)->CloseConnection(connection_id); +} + +void CEF_CALLBACK server_send_web_socket_message(struct _cef_server_t* self, + int connection_id, + const void* data, + size_t data_size) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + CefServerCppToC::Get(self)->SendWebSocketMessage(connection_id, data, + data_size); +} + +} // namespace + +// CONSTRUCTOR - Do not edit by hand. + +CefServerCppToC::CefServerCppToC() { + GetStruct()->get_task_runner = server_get_task_runner; + GetStruct()->shutdown = server_shutdown; + GetStruct()->is_running = server_is_running; + GetStruct()->get_address = server_get_address; + GetStruct()->has_connection = server_has_connection; + GetStruct()->is_valid_connection = server_is_valid_connection; + GetStruct()->send_http200response = server_send_http200response; + GetStruct()->send_http404response = server_send_http404response; + GetStruct()->send_http500response = server_send_http500response; + GetStruct()->send_http_response = server_send_http_response; + GetStruct()->send_raw_data = server_send_raw_data; + GetStruct()->close_connection = server_close_connection; + GetStruct()->send_web_socket_message = server_send_web_socket_message; +} + +template <> +CefRefPtr +CefCppToCRefCounted::UnwrapDerived( + CefWrapperType type, + cef_server_t* s) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template <> +base::AtomicRefCount + CefCppToCRefCounted::DebugObjCt + ATOMIC_DECLARATION; +#endif + +template <> +CefWrapperType CefCppToCRefCounted:: + kWrapperType = WT_SERVER; diff --git a/libcef_dll/cpptoc/server_cpptoc.h b/libcef_dll/cpptoc/server_cpptoc.h new file mode 100644 index 000000000..e9fef355b --- /dev/null +++ b/libcef_dll/cpptoc/server_cpptoc.h @@ -0,0 +1,35 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=0f4a3453e43f2aa85bfdfaed15772373c43eccb8$ +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +#include "include/capi/cef_server_capi.h" +#include "include/cef_server.h" +#include "libcef_dll/cpptoc/cpptoc_ref_counted.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed DLL-side only. +class CefServerCppToC + : public CefCppToCRefCounted { + public: + CefServerCppToC(); +}; + +#endif // CEF_LIBCEF_DLL_CPPTOC_SERVER_CPPTOC_H_ diff --git a/libcef_dll/cpptoc/server_handler_cpptoc.cc b/libcef_dll/cpptoc/server_handler_cpptoc.cc new file mode 100644 index 000000000..3949f882a --- /dev/null +++ b/libcef_dll/cpptoc/server_handler_cpptoc.cc @@ -0,0 +1,244 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=36e97984dadd60442c0eba6189c08b6512d05f78$ +// + +#include "libcef_dll/cpptoc/server_handler_cpptoc.h" +#include "libcef_dll/ctocpp/callback_ctocpp.h" +#include "libcef_dll/ctocpp/request_ctocpp.h" +#include "libcef_dll/ctocpp/server_ctocpp.h" + +namespace { + +// MEMBER FUNCTIONS - Body may be edited by hand. + +void CEF_CALLBACK +server_handler_on_server_created(struct _cef_server_handler_t* self, + cef_server_t* server) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnServerCreated( + CefServerCToCpp::Wrap(server)); +} + +void CEF_CALLBACK +server_handler_on_server_destroyed(struct _cef_server_handler_t* self, + cef_server_t* server) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnServerDestroyed( + CefServerCToCpp::Wrap(server)); +} + +void CEF_CALLBACK +server_handler_on_client_connected(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnClientConnected( + CefServerCToCpp::Wrap(server), connection_id); +} + +void CEF_CALLBACK +server_handler_on_client_disconnected(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnClientDisconnected( + CefServerCToCpp::Wrap(server), connection_id); +} + +void CEF_CALLBACK +server_handler_on_http_request(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id, + const cef_string_t* client_address, + cef_request_t* request) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + // Verify param: client_address; type: string_byref_const + DCHECK(client_address); + if (!client_address) + return; + // Verify param: request; type: refptr_diff + DCHECK(request); + if (!request) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnHttpRequest( + CefServerCToCpp::Wrap(server), connection_id, CefString(client_address), + CefRequestCToCpp::Wrap(request)); +} + +void CEF_CALLBACK +server_handler_on_web_socket_request(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id, + const cef_string_t* client_address, + cef_request_t* request, + cef_callback_t* callback) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + // Verify param: client_address; type: string_byref_const + DCHECK(client_address); + if (!client_address) + return; + // Verify param: request; type: refptr_diff + DCHECK(request); + if (!request) + return; + // Verify param: callback; type: refptr_diff + DCHECK(callback); + if (!callback) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnWebSocketRequest( + CefServerCToCpp::Wrap(server), connection_id, CefString(client_address), + CefRequestCToCpp::Wrap(request), CefCallbackCToCpp::Wrap(callback)); +} + +void CEF_CALLBACK +server_handler_on_web_socket_connected(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnWebSocketConnected( + CefServerCToCpp::Wrap(server), connection_id); +} + +void CEF_CALLBACK +server_handler_on_web_socket_message(struct _cef_server_handler_t* self, + cef_server_t* server, + int connection_id, + const void* data, + size_t data_size) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Verify param: server; type: refptr_diff + DCHECK(server); + if (!server) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + CefServerHandlerCppToC::Get(self)->OnWebSocketMessage( + CefServerCToCpp::Wrap(server), connection_id, data, data_size); +} + +} // namespace + +// CONSTRUCTOR - Do not edit by hand. + +CefServerHandlerCppToC::CefServerHandlerCppToC() { + GetStruct()->on_server_created = server_handler_on_server_created; + GetStruct()->on_server_destroyed = server_handler_on_server_destroyed; + GetStruct()->on_client_connected = server_handler_on_client_connected; + GetStruct()->on_client_disconnected = server_handler_on_client_disconnected; + GetStruct()->on_http_request = server_handler_on_http_request; + GetStruct()->on_web_socket_request = server_handler_on_web_socket_request; + GetStruct()->on_web_socket_connected = server_handler_on_web_socket_connected; + GetStruct()->on_web_socket_message = server_handler_on_web_socket_message; +} + +template <> +CefRefPtr CefCppToCRefCounted< + CefServerHandlerCppToC, + CefServerHandler, + cef_server_handler_t>::UnwrapDerived(CefWrapperType type, + cef_server_handler_t* s) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template <> +base::AtomicRefCount CefCppToCRefCounted::DebugObjCt + ATOMIC_DECLARATION; +#endif + +template <> +CefWrapperType CefCppToCRefCounted::kWrapperType = + WT_SERVER_HANDLER; diff --git a/libcef_dll/cpptoc/server_handler_cpptoc.h b/libcef_dll/cpptoc/server_handler_cpptoc.h new file mode 100644 index 000000000..b08022904 --- /dev/null +++ b/libcef_dll/cpptoc/server_handler_cpptoc.h @@ -0,0 +1,37 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=01080491feb0e65e1385fcc5547ecb9c88de70c3$ +// + +#ifndef CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_ +#define CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_ +#pragma once + +#if !defined(WRAPPING_CEF_SHARED) +#error This file can be included wrapper-side only +#endif + +#include "include/capi/cef_server_capi.h" +#include "include/cef_server.h" +#include "libcef_dll/cpptoc/cpptoc_ref_counted.h" + +// Wrap a C++ class with a C structure. +// This class may be instantiated and accessed wrapper-side only. +class CefServerHandlerCppToC + : public CefCppToCRefCounted { + public: + CefServerHandlerCppToC(); +}; + +#endif // CEF_LIBCEF_DLL_CPPTOC_SERVER_HANDLER_CPPTOC_H_ diff --git a/libcef_dll/ctocpp/server_ctocpp.cc b/libcef_dll/ctocpp/server_ctocpp.cc new file mode 100644 index 000000000..078543ae2 --- /dev/null +++ b/libcef_dll/ctocpp/server_ctocpp.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=ffa6d8f2bc0fd724a50856dfa867cf275345716c$ +// + +#include "libcef_dll/ctocpp/server_ctocpp.h" +#include "libcef_dll/cpptoc/server_handler_cpptoc.h" +#include "libcef_dll/ctocpp/task_runner_ctocpp.h" +#include "libcef_dll/transfer_util.h" + +// STATIC METHODS - Body may be edited by hand. + +void CefServer::CreateServer(const CefString& address, + uint16 port, + int backlog, + CefRefPtr handler) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: address; type: string_byref_const + DCHECK(!address.empty()); + if (address.empty()) + return; + // Verify param: handler; type: refptr_diff + DCHECK(handler.get()); + if (!handler.get()) + return; + + // Execute + cef_server_create(address.GetStruct(), port, backlog, + CefServerHandlerCppToC::Wrap(handler)); +} + +// VIRTUAL METHODS - Body may be edited by hand. + +CefRefPtr CefServerCToCpp::GetTaskRunner() { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, get_task_runner)) + return NULL; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_task_runner_t* _retval = _struct->get_task_runner(_struct); + + // Return type: refptr_same + return CefTaskRunnerCToCpp::Wrap(_retval); +} + +void CefServerCToCpp::Shutdown() { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, shutdown)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->shutdown(_struct); +} + +bool CefServerCToCpp::IsRunning() { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, is_running)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->is_running(_struct); + + // Return type: bool + return _retval ? true : false; +} + +CefString CefServerCToCpp::GetAddress() { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, get_address)) + return CefString(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + cef_string_userfree_t _retval = _struct->get_address(_struct); + + // Return type: string + CefString _retvalStr; + _retvalStr.AttachToUserFree(_retval); + return _retvalStr; +} + +bool CefServerCToCpp::HasConnection() { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, has_connection)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->has_connection(_struct); + + // Return type: bool + return _retval ? true : false; +} + +bool CefServerCToCpp::IsValidConnection(int connection_id) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, is_valid_connection)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->is_valid_connection(_struct, connection_id); + + // Return type: bool + return _retval ? true : false; +} + +void CefServerCToCpp::SendHttp200Response(int connection_id, + const CefString& content_type, + const void* data, + size_t data_size) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_http200response)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: content_type; type: string_byref_const + DCHECK(!content_type.empty()); + if (content_type.empty()) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + _struct->send_http200response(_struct, connection_id, + content_type.GetStruct(), data, data_size); +} + +void CefServerCToCpp::SendHttp404Response(int connection_id) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_http404response)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->send_http404response(_struct, connection_id); +} + +void CefServerCToCpp::SendHttp500Response(int connection_id, + const CefString& error_message) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_http500response)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: error_message; type: string_byref_const + DCHECK(!error_message.empty()); + if (error_message.empty()) + return; + + // Execute + _struct->send_http500response(_struct, connection_id, + error_message.GetStruct()); +} + +void CefServerCToCpp::SendHttpResponse(int connection_id, + int response_code, + const CefString& content_type, + int64 content_length, + const HeaderMap& extra_headers) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_http_response)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: content_type; type: string_byref_const + DCHECK(!content_type.empty()); + if (content_type.empty()) + return; + // Unverified params: extra_headers + + // Translate param: extra_headers; type: string_map_multi_byref_const + cef_string_multimap_t extra_headersMultimap = cef_string_multimap_alloc(); + DCHECK(extra_headersMultimap); + if (extra_headersMultimap) + transfer_string_multimap_contents(extra_headers, extra_headersMultimap); + + // Execute + _struct->send_http_response(_struct, connection_id, response_code, + content_type.GetStruct(), content_length, + extra_headersMultimap); + + // Restore param:extra_headers; type: string_map_multi_byref_const + if (extra_headersMultimap) + cef_string_multimap_free(extra_headersMultimap); +} + +void CefServerCToCpp::SendRawData(int connection_id, + const void* data, + size_t data_size) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_raw_data)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + _struct->send_raw_data(_struct, connection_id, data, data_size); +} + +void CefServerCToCpp::CloseConnection(int connection_id) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, close_connection)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->close_connection(_struct, connection_id); +} + +void CefServerCToCpp::SendWebSocketMessage(int connection_id, + const void* data, + size_t data_size) { + cef_server_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, send_web_socket_message)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + _struct->send_web_socket_message(_struct, connection_id, data, data_size); +} + +// CONSTRUCTOR - Do not edit by hand. + +CefServerCToCpp::CefServerCToCpp() {} + +template <> +cef_server_t* +CefCToCppRefCounted::UnwrapDerived( + CefWrapperType type, + CefServer* c) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template <> +base::AtomicRefCount + CefCToCppRefCounted::DebugObjCt + ATOMIC_DECLARATION; +#endif + +template <> +CefWrapperType CefCToCppRefCounted:: + kWrapperType = WT_SERVER; diff --git a/libcef_dll/ctocpp/server_ctocpp.h b/libcef_dll/ctocpp/server_ctocpp.h new file mode 100644 index 000000000..e8d7f7ee4 --- /dev/null +++ b/libcef_dll/ctocpp/server_ctocpp.h @@ -0,0 +1,62 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=7218e524c29b0837d4759117c81ffa1401759596$ +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_ +#pragma once + +#if !defined(WRAPPING_CEF_SHARED) +#error This file can be included wrapper-side only +#endif + +#include "include/capi/cef_server_capi.h" +#include "include/cef_server.h" +#include "libcef_dll/ctocpp/ctocpp_ref_counted.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed wrapper-side only. +class CefServerCToCpp + : public CefCToCppRefCounted { + public: + CefServerCToCpp(); + + // CefServer methods. + CefRefPtr GetTaskRunner() OVERRIDE; + void Shutdown() OVERRIDE; + bool IsRunning() OVERRIDE; + CefString GetAddress() OVERRIDE; + bool HasConnection() OVERRIDE; + bool IsValidConnection(int connection_id) OVERRIDE; + void SendHttp200Response(int connection_id, + const CefString& content_type, + const void* data, + size_t data_size) OVERRIDE; + void SendHttp404Response(int connection_id) OVERRIDE; + void SendHttp500Response(int connection_id, + const CefString& error_message) OVERRIDE; + void SendHttpResponse(int connection_id, + int response_code, + const CefString& content_type, + int64 content_length, + const HeaderMap& extra_headers) OVERRIDE; + void SendRawData(int connection_id, + const void* data, + size_t data_size) OVERRIDE; + void CloseConnection(int connection_id) OVERRIDE; + void SendWebSocketMessage(int connection_id, + const void* data, + size_t data_size) OVERRIDE; +}; + +#endif // CEF_LIBCEF_DLL_CTOCPP_SERVER_CTOCPP_H_ diff --git a/libcef_dll/ctocpp/server_handler_ctocpp.cc b/libcef_dll/ctocpp/server_handler_ctocpp.cc new file mode 100644 index 000000000..df0b23a22 --- /dev/null +++ b/libcef_dll/ctocpp/server_handler_ctocpp.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=262176ac6f772f186c59e0bf07e07d3514fa3440$ +// + +#include "libcef_dll/ctocpp/server_handler_ctocpp.h" +#include "libcef_dll/cpptoc/callback_cpptoc.h" +#include "libcef_dll/cpptoc/request_cpptoc.h" +#include "libcef_dll/cpptoc/server_cpptoc.h" + +// VIRTUAL METHODS - Body may be edited by hand. + +void CefServerHandlerCToCpp::OnServerCreated(CefRefPtr server) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_server_created)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + + // Execute + _struct->on_server_created(_struct, CefServerCppToC::Wrap(server)); +} + +void CefServerHandlerCToCpp::OnServerDestroyed(CefRefPtr server) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_server_destroyed)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + + // Execute + _struct->on_server_destroyed(_struct, CefServerCppToC::Wrap(server)); +} + +void CefServerHandlerCToCpp::OnClientConnected(CefRefPtr server, + int connection_id) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_client_connected)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + + // Execute + _struct->on_client_connected(_struct, CefServerCppToC::Wrap(server), + connection_id); +} + +void CefServerHandlerCToCpp::OnClientDisconnected(CefRefPtr server, + int connection_id) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_client_disconnected)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + + // Execute + _struct->on_client_disconnected(_struct, CefServerCppToC::Wrap(server), + connection_id); +} + +void CefServerHandlerCToCpp::OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_http_request)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + // Verify param: client_address; type: string_byref_const + DCHECK(!client_address.empty()); + if (client_address.empty()) + return; + // Verify param: request; type: refptr_diff + DCHECK(request.get()); + if (!request.get()) + return; + + // Execute + _struct->on_http_request(_struct, CefServerCppToC::Wrap(server), + connection_id, client_address.GetStruct(), + CefRequestCppToC::Wrap(request)); +} + +void CefServerHandlerCToCpp::OnWebSocketRequest( + CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request, + CefRefPtr callback) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_web_socket_request)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + // Verify param: client_address; type: string_byref_const + DCHECK(!client_address.empty()); + if (client_address.empty()) + return; + // Verify param: request; type: refptr_diff + DCHECK(request.get()); + if (!request.get()) + return; + // Verify param: callback; type: refptr_diff + DCHECK(callback.get()); + if (!callback.get()) + return; + + // Execute + _struct->on_web_socket_request(_struct, CefServerCppToC::Wrap(server), + connection_id, client_address.GetStruct(), + CefRequestCppToC::Wrap(request), + CefCallbackCppToC::Wrap(callback)); +} + +void CefServerHandlerCToCpp::OnWebSocketConnected(CefRefPtr server, + int connection_id) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_web_socket_connected)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + + // Execute + _struct->on_web_socket_connected(_struct, CefServerCppToC::Wrap(server), + connection_id); +} + +void CefServerHandlerCToCpp::OnWebSocketMessage(CefRefPtr server, + int connection_id, + const void* data, + size_t data_size) { + cef_server_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_web_socket_message)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: server; type: refptr_diff + DCHECK(server.get()); + if (!server.get()) + return; + // Verify param: data; type: simple_byaddr + DCHECK(data); + if (!data) + return; + + // Execute + _struct->on_web_socket_message(_struct, CefServerCppToC::Wrap(server), + connection_id, data, data_size); +} + +// CONSTRUCTOR - Do not edit by hand. + +CefServerHandlerCToCpp::CefServerHandlerCToCpp() {} + +template <> +cef_server_handler_t* +CefCToCppRefCounted::UnwrapDerived(CefWrapperType type, + CefServerHandler* c) { + NOTREACHED() << "Unexpected class type: " << type; + return NULL; +} + +#if DCHECK_IS_ON() +template <> +base::AtomicRefCount CefCToCppRefCounted::DebugObjCt + ATOMIC_DECLARATION; +#endif + +template <> +CefWrapperType CefCToCppRefCounted::kWrapperType = + WT_SERVER_HANDLER; diff --git a/libcef_dll/ctocpp/server_handler_ctocpp.h b/libcef_dll/ctocpp/server_handler_ctocpp.h new file mode 100644 index 000000000..6563a8cf6 --- /dev/null +++ b/libcef_dll/ctocpp/server_handler_ctocpp.h @@ -0,0 +1,60 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. +// +// --------------------------------------------------------------------------- +// +// This file was generated by the CEF translator tool. If making changes by +// hand only do so within the body of existing method and function +// implementations. See the translator.README.txt file in the tools directory +// for more information. +// +// $hash=a5012dffe69ac9161fce2e11bc2f65772bd21ff4$ +// + +#ifndef CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_ +#define CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_ +#pragma once + +#if !defined(BUILDING_CEF_SHARED) +#error This file can be included DLL-side only +#endif + +#include "include/capi/cef_server_capi.h" +#include "include/cef_server.h" +#include "libcef_dll/ctocpp/ctocpp_ref_counted.h" + +// Wrap a C structure with a C++ class. +// This class may be instantiated and accessed DLL-side only. +class CefServerHandlerCToCpp + : public CefCToCppRefCounted { + public: + CefServerHandlerCToCpp(); + + // CefServerHandler methods. + void OnServerCreated(CefRefPtr server) override; + void OnServerDestroyed(CefRefPtr server) override; + void OnClientConnected(CefRefPtr server, + int connection_id) override; + void OnClientDisconnected(CefRefPtr server, + int connection_id) override; + void OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) override; + void OnWebSocketRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request, + CefRefPtr callback) override; + void OnWebSocketConnected(CefRefPtr server, + int connection_id) override; + void OnWebSocketMessage(CefRefPtr server, + int connection_id, + const void* data, + size_t data_size) override; +}; + +#endif // CEF_LIBCEF_DLL_CTOCPP_SERVER_HANDLER_CTOCPP_H_ diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index b121ac598..230062a8d 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=2df1182745ef1d14ed20428c53ed7638742c13f9$ +// $hash=4ae6ce5d8ccf3b39d55d6d8a67f8daf547242271$ // #include "include/capi/cef_app_capi.h" @@ -74,6 +74,7 @@ #include "libcef_dll/cpptoc/run_context_menu_callback_cpptoc.h" #include "libcef_dll/cpptoc/scheme_registrar_cpptoc.h" #include "libcef_dll/cpptoc/select_client_certificate_callback_cpptoc.h" +#include "libcef_dll/cpptoc/server_cpptoc.h" #include "libcef_dll/cpptoc/sslinfo_cpptoc.h" #include "libcef_dll/cpptoc/sslstatus_cpptoc.h" #include "libcef_dll/cpptoc/stream_reader_cpptoc.h" @@ -152,6 +153,7 @@ #include "libcef_dll/ctocpp/response_filter_ctocpp.h" #include "libcef_dll/ctocpp/run_file_dialog_callback_ctocpp.h" #include "libcef_dll/ctocpp/scheme_handler_factory_ctocpp.h" +#include "libcef_dll/ctocpp/server_handler_ctocpp.h" #include "libcef_dll/ctocpp/set_cookie_callback_ctocpp.h" #include "libcef_dll/ctocpp/string_visitor_ctocpp.h" #include "libcef_dll/ctocpp/task_ctocpp.h" @@ -344,6 +346,8 @@ CEF_EXPORT void cef_shutdown() { DCHECK(base::AtomicRefCountIsZero(&CefScrollViewCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero( &CefSelectClientCertificateCallbackCppToC::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefServerCppToC::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefServerHandlerCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefSetCookieCallbackCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefStreamReaderCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefStreamWriterCppToC::DebugObjCt)); diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 3df98c6a7..e0be752cf 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=82e24ebf13dbb1b7430a4710fd3d296b98311d1d$ +// $hash=8f5702806c240c66d2b9235cf44e58ba52fe3b63$ // #include "include/capi/cef_app_capi.h" @@ -81,6 +81,7 @@ #include "libcef_dll/cpptoc/response_filter_cpptoc.h" #include "libcef_dll/cpptoc/run_file_dialog_callback_cpptoc.h" #include "libcef_dll/cpptoc/scheme_handler_factory_cpptoc.h" +#include "libcef_dll/cpptoc/server_handler_cpptoc.h" #include "libcef_dll/cpptoc/set_cookie_callback_cpptoc.h" #include "libcef_dll/cpptoc/string_visitor_cpptoc.h" #include "libcef_dll/cpptoc/task_cpptoc.h" @@ -134,6 +135,7 @@ #include "libcef_dll/ctocpp/run_context_menu_callback_ctocpp.h" #include "libcef_dll/ctocpp/scheme_registrar_ctocpp.h" #include "libcef_dll/ctocpp/select_client_certificate_callback_ctocpp.h" +#include "libcef_dll/ctocpp/server_ctocpp.h" #include "libcef_dll/ctocpp/sslinfo_ctocpp.h" #include "libcef_dll/ctocpp/sslstatus_ctocpp.h" #include "libcef_dll/ctocpp/stream_reader_ctocpp.h" @@ -335,6 +337,8 @@ CEF_GLOBAL void CefShutdown() { DCHECK(base::AtomicRefCountIsZero(&CefScrollViewCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero( &CefSelectClientCertificateCallbackCToCpp::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefServerCToCpp::DebugObjCt)); + DCHECK(base::AtomicRefCountIsZero(&CefServerHandlerCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefSetCookieCallbackCppToC::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefStreamReaderCToCpp::DebugObjCt)); DCHECK(base::AtomicRefCountIsZero(&CefStreamWriterCToCpp::DebugObjCt)); diff --git a/libcef_dll/wrapper_types.h b/libcef_dll/wrapper_types.h index a9eddad08..40f254098 100644 --- a/libcef_dll/wrapper_types.h +++ b/libcef_dll/wrapper_types.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=0e103b8e82d15405eb5fdb2efc48992dae1e9656$ +// $hash=9a3b6de92214d6b132bd66d84724272c886a3758$ // #ifndef CEF_LIBCEF_DLL_WRAPPER_TYPES_H_ @@ -115,6 +115,8 @@ enum CefWrapperType { WT_SCHEME_REGISTRAR, WT_SCROLL_VIEW, WT_SELECT_CLIENT_CERTIFICATE_CALLBACK, + WT_SERVER, + WT_SERVER_HANDLER, WT_SET_COOKIE_CALLBACK, WT_STREAM_READER, WT_STREAM_WRITER, diff --git a/tests/cefclient/browser/binding_test.cc b/tests/cefclient/browser/binding_test.cc index 563017742..297ebb394 100644 --- a/tests/cefclient/browser/binding_test.cc +++ b/tests/cefclient/browser/binding_test.cc @@ -7,12 +7,14 @@ #include #include +#include "tests/cefclient/browser/test_runner.h" + namespace client { namespace binding_test { namespace { -const char kTestUrl[] = "http://tests/binding"; +const char kTestUrlPath[] = "/binding"; const char kTestMessageName[] = "BindingTest"; // Handle messages in the browser process. @@ -29,7 +31,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { CefRefPtr callback) OVERRIDE { // Only handle messages from the test URL. const std::string& url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; const std::string& message_name = request; diff --git a/tests/cefclient/browser/dialog_test.cc b/tests/cefclient/browser/dialog_test.cc index eebdce181..5860d4421 100644 --- a/tests/cefclient/browser/dialog_test.cc +++ b/tests/cefclient/browser/dialog_test.cc @@ -8,13 +8,14 @@ #include "include/cef_browser.h" #include "include/wrapper/cef_helpers.h" +#include "tests/cefclient/browser/test_runner.h" namespace client { namespace dialog_test { namespace { -const char kTestUrl[] = "http://tests/dialogs"; +const char kTestUrlPath[] = "/dialogs"; const char kFileOpenMessageName[] = "DialogTest.FileOpen"; const char kFileOpenMultipleMessageName[] = "DialogTest.FileOpenMultiple"; const char kFileOpenFolderMessageName[] = "DialogTest.FileOpenFolder"; @@ -109,7 +110,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { // Only handle messages from the test URL. const std::string& url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; if (!dialog_state_.get()) diff --git a/tests/cefclient/browser/drm_test.cc b/tests/cefclient/browser/drm_test.cc index 25eda95a7..19a716644 100644 --- a/tests/cefclient/browser/drm_test.cc +++ b/tests/cefclient/browser/drm_test.cc @@ -9,6 +9,7 @@ #include "include/cef_parser.h" #include "include/cef_web_plugin.h" +#include "tests/cefclient/browser/test_runner.h" namespace client { namespace drm_test { @@ -19,7 +20,7 @@ namespace { const int kMessageFormatError = 1; const int kCdmLoadError = 2; -const char kTestUrl[] = "http://tests/drm"; +const char kTestUrlPath[] = "/drm"; const char kWidevineCdmPathKey[] = "widevine_cdm_path"; // Callback executed once CDM registration is complete. @@ -49,7 +50,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { public: Handler() {} - // Called due to cefQuery execution in binding.html. + // Called due to cefQuery execution in drm.html. virtual bool OnQuery(CefRefPtr browser, CefRefPtr frame, int64 query_id, @@ -58,7 +59,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { CefRefPtr callback) OVERRIDE { // Only handle messages from the test URL. const std::string& url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; // Parse |request| as a JSON dictionary. diff --git a/tests/cefclient/browser/preferences_test.cc b/tests/cefclient/browser/preferences_test.cc index c83e050a7..81e60dbe5 100644 --- a/tests/cefclient/browser/preferences_test.cc +++ b/tests/cefclient/browser/preferences_test.cc @@ -11,13 +11,14 @@ #include "include/base/cef_logging.h" #include "include/cef_command_line.h" #include "include/cef_parser.h" +#include "tests/cefclient/browser/test_runner.h" namespace client { namespace preferences_test { namespace { -const char kTestUrl[] = "http://tests/preferences"; +const char kTestUrlPath[] = "/preferences"; // Application-specific error codes. const int kMessageFormatError = 1; @@ -52,8 +53,8 @@ class Handler : public CefMessageRouterBrowserSide::Handler { CEF_REQUIRE_UI_THREAD(); // Only handle messages from the test URL. - std::string url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + const std::string& url = frame->GetURL(); + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; // Parse |request| as a JSON dictionary. diff --git a/tests/cefclient/browser/resource.h b/tests/cefclient/browser/resource.h index a342765c3..55e661e36 100644 --- a/tests/cefclient/browser/resource.h +++ b/tests/cefclient/browser/resource.h @@ -58,12 +58,14 @@ #define IDS_PERFORMANCE2_HTML 1013 #define IDS_PREFERENCES_HTML 1014 #define IDS_RESPONSE_FILTER_HTML 1015 -#define IDS_TRANSPARENCY_HTML 1016 -#define IDS_URLREQUEST_HTML 1017 -#define IDS_WINDOW_HTML 1018 -#define IDS_WINDOW_ICON_1X_PNG 1019 -#define IDS_WINDOW_ICON_2X_PNG 1020 -#define IDS_XMLHTTPREQUEST_HTML 1021 +#define IDS_SERVER_HTML 1016 +#define IDS_TRANSPARENCY_HTML 1017 +#define IDS_URLREQUEST_HTML 1018 +#define IDS_WEBSOCKET_HTML 1019 +#define IDS_WINDOW_HTML 1020 +#define IDS_WINDOW_ICON_1X_PNG 1021 +#define IDS_WINDOW_ICON_2X_PNG 1022 +#define IDS_XMLHTTPREQUEST_HTML 1023 #define IDS_EXTENSIONS_SET_PAGE_COLOR_ICON_PNG 1030 #define IDS_EXTENSIONS_SET_PAGE_COLOR_MANIFEST_JSON 1031 diff --git a/tests/cefclient/browser/resource_util_win_idmap.cc b/tests/cefclient/browser/resource_util_win_idmap.cc index 6f5a4f36a..94ece31ba 100644 --- a/tests/cefclient/browser/resource_util_win_idmap.cc +++ b/tests/cefclient/browser/resource_util_win_idmap.cc @@ -38,8 +38,10 @@ int GetResourceId(const char* resource_name) { {"performance2.html", IDS_PERFORMANCE2_HTML}, {"preferences.html", IDS_PREFERENCES_HTML}, {"response_filter.html", IDS_RESPONSE_FILTER_HTML}, + {"server.html", IDS_SERVER_HTML}, {"transparency.html", IDS_TRANSPARENCY_HTML}, {"urlrequest.html", IDS_URLREQUEST_HTML}, + {"websocket.html", IDS_WEBSOCKET_HTML}, {"window.html", IDS_WINDOW_HTML}, {"window_icon.1x.png", IDS_WINDOW_ICON_1X_PNG}, {"window_icon.2x.png", IDS_WINDOW_ICON_2X_PNG}, diff --git a/tests/cefclient/browser/response_filter_test.cc b/tests/cefclient/browser/response_filter_test.cc index 15f12429e..f437fbf85 100644 --- a/tests/cefclient/browser/response_filter_test.cc +++ b/tests/cefclient/browser/response_filter_test.cc @@ -10,6 +10,7 @@ #include "include/base/cef_logging.h" #include "include/cef_command_line.h" +#include "tests/cefclient/browser/test_runner.h" #include "tests/shared/common/client_switches.h" namespace client { @@ -17,7 +18,7 @@ namespace response_filter_test { namespace { -const char kTestUrl[] = "http://tests/response_filter"; +const char kTestUrlPath[] = "/response_filter"; const char kFindString[] = "REPLACE_THIS_STRING"; const char kReplaceString[] = "This is the replaced string!"; @@ -226,7 +227,8 @@ CefRefPtr GetResourceResponseFilter( CefRefPtr response) { // Use the find/replace filter on the test URL. const std::string& url = request->GetURL(); - if (url.find(kTestUrl) == 0) + + if (test_runner::IsTestURL(url, kTestUrlPath)) return new FindReplaceResponseFilter(); if (MatchesFilterURL(url)) diff --git a/tests/cefclient/browser/server_test.cc b/tests/cefclient/browser/server_test.cc new file mode 100644 index 000000000..2029c026c --- /dev/null +++ b/tests/cefclient/browser/server_test.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "tests/cefclient/browser/server_test.h" + +#include +#include + +#include "include/base/cef_bind.h" +#include "include/base/cef_weak_ptr.h" +#include "include/cef_parser.h" +#include "include/cef_server.h" +#include "include/wrapper/cef_closure_task.h" +#include "tests/shared/browser/resource_util.h" + +namespace client { +namespace server_test { + +namespace { + +// Application-specific error codes. +const int kMessageFormatError = 1; +const int kActionStateError = 1; + +// JSON dictionary keys. +const char kActionKey[] = "action"; +const char kResultKey[] = "result"; +const char kPortKey[] = "port"; +const char kStatusKey[] = "status"; +const char kMessageKey[] = "message"; + +// Required URL for cefQuery execution. +const char kTestUrl[] = "http://tests/server"; + +// Server default values. +const char kServerAddress[] = "127.0.0.1"; +const int kServerPortDefault = 8099; +const int kServerBacklog = 10; +const char kDefaultPath[] = "websocket.html"; + +// Handles the HTTP/WebSocket server. +class ServerHandler : public CefServerHandler { + public: + typedef base::Callback CompleteCallback; + + ServerHandler() {} + + // |complete_callback| will be executed on the UI thread after completion. + void StartServer(int port, const CompleteCallback& complete_callback) { + CEF_REQUIRE_UI_THREAD(); + DCHECK(!server_); + DCHECK(port >= 1025 && port <= 65535); + port_ = port; + complete_callback_ = complete_callback; + CefServer::CreateServer(kServerAddress, port, kServerBacklog, this); + } + + // |complete_callback| will be executed on the UI thread after completion. + void StopServer(const CompleteCallback& complete_callback) { + CEF_REQUIRE_UI_THREAD(); + DCHECK(server_); + complete_callback_ = complete_callback; + server_->Shutdown(); + } + + // CefServerHandler methods are called on the server thread. + + void OnServerCreated(CefRefPtr server) override { + DCHECK(!server_); + server_ = server; + RunCompleteCallback(server->IsRunning()); + } + + void OnServerDestroyed(CefRefPtr server) override { + DCHECK(server_); + server_ = nullptr; + RunCompleteCallback(true); + } + + void OnClientConnected(CefRefPtr server, + int connection_id) override {} + + void OnClientDisconnected(CefRefPtr server, + int connection_id) override {} + + void OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) override { + // Parse the request URL and retrieve the path without leading slash. + CefURLParts url_parts; + CefParseURL(request->GetURL(), url_parts); + std::string path = CefString(&url_parts.path); + if (!path.empty() && path[0] == '/') + path = path.substr(1); + + if (path.empty()) + path = kDefaultPath; + + std::string mime_type; + const size_t sep = path.find_last_of("."); + if (sep != std::string::npos) { + // Determine the mime type based on the extension. + mime_type = CefGetMimeType(path.substr(sep + 1)); + } else { + // No extension. Assume html. + path += ".html"; + } + if (mime_type.empty()) + mime_type = "text/html"; + + CefRefPtr stream; + CefResponse::HeaderMap extra_headers; + + if (path == "request.html") { + // Return the request contents. + stream = test_runner::GetDumpResponse(request, extra_headers); + } + + if (!stream) { + // Load any resource supported by cefclient. + stream = GetBinaryResourceReader(path.c_str()); + } + + if (stream) { + SendHttpResponseStream(server, connection_id, mime_type, stream, + extra_headers); + } else { + server->SendHttp404Response(connection_id); + } + } + + void OnWebSocketRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request, + CefRefPtr callback) override { + // Always accept WebSocket connections. + callback->Continue(); + } + + void OnWebSocketConnected(CefRefPtr server, + int connection_id) override {} + + void OnWebSocketMessage(CefRefPtr server, + int connection_id, + const void* data, + size_t data_size) override { + // Echo the reverse of the message. + std::string message(static_cast(data), data_size); + std::reverse(message.begin(), message.end()); + + server->SendWebSocketMessage(connection_id, message.data(), message.size()); + } + + int port() const { return port_; } + + private: + void RunCompleteCallback(bool success) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(&ServerHandler::RunCompleteCallback, this, + success)); + return; + } + + if (!complete_callback_.is_null()) { + complete_callback_.Run(success); + complete_callback_.Reset(); + } + } + + static void SendHttpResponseStream(CefRefPtr server, + int connection_id, + const std::string& mime_type, + CefRefPtr stream, + CefResponse::HeaderMap extra_headers) { + // Determine the stream size. + stream->Seek(0, SEEK_END); + int64 content_length = stream->Tell(); + stream->Seek(0, SEEK_SET); + + // Send response headers. + server->SendHttpResponse(connection_id, 200, mime_type, content_length, + extra_headers); + + // Send stream contents. + char buffer[8192]; + size_t read; + do { + read = stream->Read(buffer, 1, sizeof(buffer)); + if (read > 0) + server->SendRawData(connection_id, buffer, read); + } while (!stream->Eof() && read != 0); + + // Close the connection. + server->CloseConnection(connection_id); + } + + CefRefPtr server_; + + // The below members are only accessed on the UI thread. + int port_; + CompleteCallback complete_callback_; + + IMPLEMENT_REFCOUNTING(ServerHandler); + DISALLOW_COPY_AND_ASSIGN(ServerHandler); +}; + +// Handle messages in the browser process. +class Handler : public CefMessageRouterBrowserSide::Handler { + public: + Handler() : weak_ptr_factory_(this) {} + + virtual ~Handler() { + if (handler_) { + handler_->StopServer(ServerHandler::CompleteCallback()); + handler_ = nullptr; + } + } + + // Called due to cefQuery execution in server.html. + virtual bool OnQuery(CefRefPtr browser, + CefRefPtr frame, + int64 query_id, + const CefString& request, + bool persistent, + CefRefPtr callback) OVERRIDE { + CEF_REQUIRE_UI_THREAD(); + + // Only handle messages from the test URL. + const std::string& url = frame->GetURL(); + if (url.find(kTestUrl) != 0) + return false; + + // Parse |request| as a JSON dictionary. + CefRefPtr request_dict = ParseJSON(request); + if (!request_dict) { + callback->Failure(kMessageFormatError, "Incorrect message format"); + return true; + } + + if (!VerifyKey(request_dict, kActionKey, VTYPE_STRING, callback)) + return true; + + const std::string& action = request_dict->GetString(kActionKey); + if (action == "query") { + HandleQueryAction(request_dict, callback); + } else if (action == "start") { + HandleStartAction(request_dict, callback); + } else if (action == "stop") { + HandleStopAction(request_dict, callback); + } else { + callback->Failure(kMessageFormatError, "Unrecognized action: " + action); + } + + return true; + } + + private: + // Return current server status. + void HandleQueryAction(CefRefPtr request_dict, + CefRefPtr callback) { + CefRefPtr result_dict = CefDictionaryValue::Create(); + if (handler_) { + result_dict->SetInt(kPortKey, handler_->port()); + result_dict->SetString(kStatusKey, "running"); + } else { + result_dict->SetInt(kPortKey, kServerPortDefault); + result_dict->SetString(kStatusKey, "stopped"); + } + SendResponse(callback, true, result_dict); + } + + // Start the server. + void HandleStartAction(CefRefPtr request_dict, + CefRefPtr callback) { + if (handler_) { + callback->Failure(kActionStateError, "Server is currently running"); + return; + } + + if (!VerifyKey(request_dict, kPortKey, VTYPE_INT, callback)) + return; + + const int port = request_dict->GetInt(kPortKey); + if (port < 8000 || port > 65535) { + callback->Failure(kMessageFormatError, "Invalid port number specified"); + return; + } + + handler_ = new ServerHandler(); + + // Start the server. OnComplete will be executed upon completion. + handler_->StartServer(port, + base::Bind(&Handler::OnStartComplete, + weak_ptr_factory_.GetWeakPtr(), callback)); + } + + // Stop the server. + void HandleStopAction(CefRefPtr request_dict, + CefRefPtr callback) { + if (!handler_) { + callback->Failure(kActionStateError, "Server is not currently running"); + return; + } + + // Stop the server. OnComplete will be executed upon completion. + handler_->StopServer(base::Bind(&Handler::OnStopComplete, + weak_ptr_factory_.GetWeakPtr(), callback)); + + handler_ = nullptr; + } + + // Server start completed. + void OnStartComplete(CefRefPtr callback, bool success) { + CEF_REQUIRE_UI_THREAD(); + CefRefPtr result_dict = CefDictionaryValue::Create(); + if (!success) { + handler_ = nullptr; + result_dict->SetString(kMessageKey, "Server failed to start."); + } + SendResponse(callback, success, result_dict); + } + + // Server stop completed. + void OnStopComplete(CefRefPtr callback, bool success) { + CEF_REQUIRE_UI_THREAD(); + CefRefPtr result_dict = CefDictionaryValue::Create(); + if (!success) { + result_dict->SetString(kMessageKey, "Server failed to stop."); + } + SendResponse(callback, success, result_dict); + } + + // Send a response in the format expected by server.html. + static void SendResponse(CefRefPtr callback, + bool success, + CefRefPtr result_dict) { + if (!result_dict) { + result_dict = CefDictionaryValue::Create(); + } + result_dict->SetString(kResultKey, success ? "success" : "failure"); + CefRefPtr value = CefValue::Create(); + value->SetDictionary(result_dict); + const std::string& response = CefWriteJSON(value, JSON_WRITER_DEFAULT); + callback->Success(response); + } + + // Convert a JSON string to a dictionary value. + static CefRefPtr ParseJSON(const CefString& string) { + CefRefPtr value = CefParseJSON(string, JSON_PARSER_RFC); + if (value.get() && value->GetType() == VTYPE_DICTIONARY) + return value->GetDictionary(); + return NULL; + } + + // Verify that |key| exists in |dictionary| and has type |value_type|. Fails + // |callback| and returns false on failure. + static bool VerifyKey(CefRefPtr dictionary, + const char* key, + cef_value_type_t value_type, + CefRefPtr callback) { + if (!dictionary->HasKey(key) || dictionary->GetType(key) != value_type) { + callback->Failure( + kMessageFormatError, + "Missing or incorrectly formatted message key: " + std::string(key)); + return false; + } + return true; + } + + // Non-nullptr while the server is running. + CefRefPtr handler_; + + // Must be the last member. + base::WeakPtrFactory weak_ptr_factory_; +}; + +} // namespace + +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers) { + handlers.insert(new Handler()); +} + +} // namespace server_test +} // namespace client diff --git a/tests/cefclient/browser/server_test.h b/tests/cefclient/browser/server_test.h new file mode 100644 index 000000000..ffe3172ac --- /dev/null +++ b/tests/cefclient/browser/server_test.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_ +#pragma once + +#include "tests/cefclient/browser/test_runner.h" + +namespace client { +namespace server_test { + +// Create message handlers. Called from test_runner.cc. +void CreateMessageHandlers(test_runner::MessageHandlerSet& handlers); + +} // namespace server_test +} // namespace client + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_SERVER_TEST_H_ diff --git a/tests/cefclient/browser/test_runner.cc b/tests/cefclient/browser/test_runner.cc index f3398af63..935a5885c 100644 --- a/tests/cefclient/browser/test_runner.cc +++ b/tests/cefclient/browser/test_runner.cc @@ -22,6 +22,7 @@ #include "tests/cefclient/browser/response_filter_test.h" #include "tests/cefclient/browser/root_window_manager.h" #include "tests/cefclient/browser/scheme_test.h" +#include "tests/cefclient/browser/server_test.h" #include "tests/cefclient/browser/urlrequest_test.h" #include "tests/cefclient/browser/window_test.h" #include "tests/shared/browser/resource_util.h" @@ -31,6 +32,8 @@ namespace test_runner { namespace { +const char kTestHost[] = "tests"; +const char kLocalHost[] = "localhost"; const char kTestOrigin[] = "http://tests/"; // Replace all instances of |from| with |to| in |str|. @@ -432,13 +435,12 @@ class RequestDumpResourceProvider : public CefResourceManager::Provider { return false; } - const std::string& dump = DumpRequestContents(request->request()); - std::string str = - "
" + dump + "
"; - CefRefPtr stream = CefStreamReader::CreateForData( - static_cast(const_cast(str.c_str())), str.size()); - DCHECK(stream.get()); - request->Continue(new CefStreamResourceHandler("text/html", stream)); + CefResponse::HeaderMap response_headers; + CefRefPtr response = + GetDumpResponse(request->request(), response_headers); + + request->Continue(new CefStreamResourceHandler(200, "OK", "text/html", + response_headers, response)); return true; } @@ -592,6 +594,49 @@ std::string DumpRequestContents(CefRefPtr request) { return ss.str(); } +CefRefPtr GetDumpResponse( + CefRefPtr request, + CefResponse::HeaderMap& response_headers) { + std::string origin; + + // Extract the origin request header, if any. It will be specified for + // cross-origin requests. + { + CefRequest::HeaderMap requestMap; + request->GetHeaderMap(requestMap); + + CefRequest::HeaderMap::const_iterator it = requestMap.begin(); + for (; it != requestMap.end(); ++it) { + std::string key = it->first; + std::transform(key.begin(), key.end(), key.begin(), ::tolower); + if (key == "origin") { + origin = it->second; + break; + } + } + } + + if (!origin.empty() && + (origin.find("http://" + std::string(kTestHost)) == 0 || + origin.find("http://" + std::string(kLocalHost)) == 0)) { + // Allow cross-origin XMLHttpRequests from test origins. + response_headers.insert( + std::make_pair("Access-Control-Allow-Origin", origin)); + + // Allow the custom header from the xmlhttprequest.html example. + response_headers.insert( + std::make_pair("Access-Control-Allow-Headers", "My-Custom-Header")); + } + + const std::string& dump = DumpRequestContents(request); + std::string str = + "
" + dump + "
"; + CefRefPtr stream = CefStreamReader::CreateForData( + static_cast(const_cast(str.c_str())), str.size()); + DCHECK(stream); + return stream; +} + std::string GetDataURI(const std::string& data, const std::string& mime_type) { return "data:" + mime_type + ";base64," + CefURIEncode(CefBase64Encode(data.data(), data.size()), false) @@ -710,6 +755,18 @@ void Alert(CefRefPtr browser, const std::string& message) { frame->ExecuteJavaScript("alert('" + msg + "');", frame->GetURL(), 0); } +bool IsTestURL(const std::string& url, const std::string& path) { + CefURLParts parts; + CefParseURL(url, parts); + + const std::string& url_host = CefString(&parts.host); + if (url_host != kTestHost && url_host != kLocalHost) + return false; + + const std::string& url_path = CefString(&parts.path); + return url_path.find(path) == 0; +} + void CreateMessageHandlers(MessageHandlerSet& handlers) { handlers.insert(new PromptHandler); @@ -725,6 +782,9 @@ void CreateMessageHandlers(MessageHandlerSet& handlers) { // Create the preferences test handlers. preferences_test::CreateMessageHandlers(handlers); + // Create the server test handlers. + server_test::CreateMessageHandlers(handlers); + // Create the urlrequest test handlers. urlrequest_test::CreateMessageHandlers(handlers); diff --git a/tests/cefclient/browser/test_runner.h b/tests/cefclient/browser/test_runner.h index b2a499017..da366cfed 100644 --- a/tests/cefclient/browser/test_runner.h +++ b/tests/cefclient/browser/test_runner.h @@ -23,6 +23,12 @@ void RunTest(CefRefPtr browser, int id); // Returns the contents of the CefRequest as a string. std::string DumpRequestContents(CefRefPtr request); +// Returns the dump response as a stream. |request| is the request. +// |response_headers| will be populated with extra response headers, if any. +CefRefPtr GetDumpResponse( + CefRefPtr request, + CefResponse::HeaderMap& response_headers); + // Returns a data: URI with the specified contents. std::string GetDataURI(const std::string& data, const std::string& mime_type); @@ -35,6 +41,10 @@ void SetupResourceManager(CefRefPtr resource_manager); // Show a JS alert message. void Alert(CefRefPtr browser, const std::string& message); +// Returns true if |url| is a test URL with the specified |path|. This matches +// both http://tests/ and http://localhost:xxxx/. +bool IsTestURL(const std::string& url, const std::string& path); + // Create all CefMessageRouterBrowserSide::Handler objects. They will be // deleted when the ClientHandler is destroyed. typedef std::set MessageHandlerSet; diff --git a/tests/cefclient/browser/urlrequest_test.cc b/tests/cefclient/browser/urlrequest_test.cc index 4e6219c5f..bb59edd63 100644 --- a/tests/cefclient/browser/urlrequest_test.cc +++ b/tests/cefclient/browser/urlrequest_test.cc @@ -11,13 +11,14 @@ #include "include/base/cef_logging.h" #include "include/cef_urlrequest.h" #include "include/wrapper/cef_helpers.h" +#include "tests/cefclient/browser/test_runner.h" namespace client { namespace urlrequest_test { namespace { -const char kTestUrl[] = "http://tests/urlrequest"; +const char kTestUrlPath[] = "/urlrequest"; const char kTestMessageName[] = "URLRequestTest"; // Implementation of CefURLRequestClient that stores response information. Only @@ -97,13 +98,14 @@ class Handler : public CefMessageRouterBrowserSide::Handler { CEF_REQUIRE_UI_THREAD(); // Only handle messages from the test URL. - std::string url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + const std::string& url = frame->GetURL(); + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; const std::string& message_name = request; if (message_name.find(kTestMessageName) == 0) { - url = message_name.substr(sizeof(kTestMessageName)); + const std::string& load_url = + message_name.substr(sizeof(kTestMessageName)); CancelPendingRequest(); @@ -114,7 +116,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { // Create a CefRequest for the specified URL. CefRefPtr cef_request = CefRequest::Create(); - cef_request->SetURL(url); + cef_request->SetURL(load_url); cef_request->SetMethod("GET"); // Callback to be executed on request completion. diff --git a/tests/cefclient/browser/window_test.cc b/tests/cefclient/browser/window_test.cc index e2032e62e..4cb0461d9 100644 --- a/tests/cefclient/browser/window_test.cc +++ b/tests/cefclient/browser/window_test.cc @@ -12,6 +12,7 @@ #include "include/base/cef_bind.h" #include "include/wrapper/cef_stream_resource_handler.h" #include "tests/cefclient/browser/main_context.h" +#include "tests/cefclient/browser/test_runner.h" #include "tests/cefclient/browser/window_test_runner.h" #if defined(OS_WIN) || defined(OS_LINUX) @@ -31,7 +32,7 @@ namespace window_test { namespace { -const char kTestUrl[] = "http://tests/window"; +const char kTestUrlPath[] = "/window"; const char kMessagePositionName[] = "WindowTest.Position"; const char kMessageMinimizeName[] = "WindowTest.Minimize"; const char kMessageMaximizeName[] = "WindowTest.Maximize"; @@ -69,7 +70,7 @@ class Handler : public CefMessageRouterBrowserSide::Handler { CefRefPtr callback) OVERRIDE { // Only handle messages from the test URL. const std::string& url = frame->GetURL(); - if (url.find(kTestUrl) != 0) + if (!test_runner::IsTestURL(url, kTestUrlPath)) return false; const std::string& message_name = request; diff --git a/tests/cefclient/resources/binding.html b/tests/cefclient/resources/binding.html index 9ee0775ec..855e644b1 100644 --- a/tests/cefclient/resources/binding.html +++ b/tests/cefclient/resources/binding.html @@ -3,9 +3,22 @@ Binding Test - -
+ + Message:

You should see the reverse of your message below: diff --git a/tests/cefclient/resources/dialogs.html b/tests/cefclient/resources/dialogs.html index 9d1671ac8..6caf5fd58 100644 --- a/tests/cefclient/resources/dialogs.html +++ b/tests/cefclient/resources/dialogs.html @@ -29,6 +29,17 @@ function update_time() { function setup() { update_time(); setInterval(update_time, 1000); + + if (location.hostname != 'tests' && location.hostname != 'localhost') { + alert('Parts of this page can only be run from tests or localhost.'); + return; + } + + // Enable all elements. + var elements = document.getElementById("form").elements; + for (var i = 0, element; element = elements[i++]; ) { + element.disabled = false; + } } function show_file_dialog(element, test) { @@ -49,16 +60,16 @@ window.addEventListener('load', setup, false); - + Click a button to show the associated dialog type.



input type="file": -
-
-
-
+
+
+
+

diff --git a/tests/cefclient/resources/drm.html b/tests/cefclient/resources/drm.html index 097727040..cfb1cc0db 100644 --- a/tests/cefclient/resources/drm.html +++ b/tests/cefclient/resources/drm.html @@ -3,6 +3,21 @@ DRM Test - + Important notes:
  • Clearkey support is built in and should always be enabled.
  • @@ -117,7 +132,7 @@ Important notes:
  • Test DRM video playback here. Select an "asset" that includes Clearkey or Widevine in the name.
-
+ Widevine CDM Path:
diff --git a/tests/cefclient/resources/other_tests.html b/tests/cefclient/resources/other_tests.html index edd963a00..4571a9dd7 100644 --- a/tests/cefclient/resources/other_tests.html +++ b/tests/cefclient/resources/other_tests.html @@ -8,32 +8,34 @@
  • Accelerated 2D Canvas
  • Accelerated Layers
  • Cursors
  • -
  • Dialogs
  • +
  • Dialogs
  • Drag & Drop
  • -
  • Draggable Regions
  • -
  • DRM (Clearkey, Widevine)
  • +
  • Draggable Regions
  • +
  • DRM (Clearkey, Widevine)
  • Flash Plugin - requires "enable-system-flash" flag on Win/Mac and "ppapi-flash-path", "ppapi-flash-version" flags on Linux
  • Geolocation
  • HTML5 Feature Test
  • HTML5 Filesystem - requires "cache-path" flag
  • HTML5 Video
  • -
  • JavaScript Binding
  • -
  • JavaScript Performance Tests
  • -
  • JavaScript Performance (2) Tests
  • -
  • JavaScript Window Manipulation
  • -
  • Local Storage
  • -
  • PDF Viewer direct
  • -
  • PDF Viewer iframe
  • -
  • Preferences
  • +
  • JavaScript Binding
  • +
  • JavaScript Performance Tests
  • +
  • JavaScript Performance (2) Tests
  • +
  • JavaScript Window Manipulation
  • +
  • Local Storage
  • +
  • PDF Viewer direct
  • +
  • PDF Viewer iframe
  • +
  • Preferences
  • requestAnimationFrame
  • -
  • Response Filtering
  • +
  • Response Filtering
  • Scheme Handler
  • +
  • HTTP/WebSocket Server
  • +
  • WebSocket Client
  • Speech Input - requires "enable-speech-input" flag
  • -
  • Transparency
  • +
  • Transparency
  • WebGL
  • WebRTC - requires "enable-media-stream" flag
  • -
  • CefURLRequest
  • -
  • XMLHttpRequest
  • +
  • CefURLRequest
  • +
  • XMLHttpRequest
  • Print this page with "javascript:window.print();"
  • diff --git a/tests/cefclient/resources/preferences.html b/tests/cefclient/resources/preferences.html index 168d9a654..1062f1eaf 100644 --- a/tests/cefclient/resources/preferences.html +++ b/tests/cefclient/resources/preferences.html @@ -10,8 +10,23 @@ script hosting from http://cdnjs.com/libraries/jsoneditor --> + + - + +
    +

    @@ -88,6 +105,8 @@

    +
    + + + + + +

    +This page starts an HTTP/WebSocket server on localhost with the specified port number. +After starting the server click the "Open Example" button to open the WebSocket Client test in a popup window. +

    +

    +With this example each browser window can create/manage a separate server instance. +The server will be stopped automatically when the managing browser window is closed. +

    +
    +Server port: +
    + + +
    + + diff --git a/tests/cefclient/resources/urlrequest.html b/tests/cefclient/resources/urlrequest.html index 98e6a0804..fc7484db6 100644 --- a/tests/cefclient/resources/urlrequest.html +++ b/tests/cefclient/resources/urlrequest.html @@ -1,7 +1,20 @@ - + -
    + + + URL:

    diff --git a/tests/cefclient/resources/websocket.html b/tests/cefclient/resources/websocket.html new file mode 100644 index 000000000..e13abc0cf --- /dev/null +++ b/tests/cefclient/resources/websocket.html @@ -0,0 +1,107 @@ + + +WebSocket Test + + + + + +

    +This page tests a WebSocket connection. +The example implementation in server_test.cc will then echo the message contents in reverse. +

    + +Server URL: +
    + +
    Message: + +
    Response: +

    +The example implementation in server_test.cc can also serve the HTTP-based Other Tests. +
    + + diff --git a/tests/cefclient/resources/win/cefclient.rc b/tests/cefclient/resources/win/cefclient.rc index b284f7618..06605d43f 100644 --- a/tests/cefclient/resources/win/cefclient.rc +++ b/tests/cefclient/resources/win/cefclient.rc @@ -45,8 +45,10 @@ IDS_PERFORMANCE_HTML BINARY "..\\performance.html" IDS_PERFORMANCE2_HTML BINARY "..\\performance2.html" IDS_PREFERENCES_HTML BINARY "..\\preferences.html" IDS_RESPONSE_FILTER_HTML BINARY "..\\response_filter.html" +IDS_SERVER_HTML BINARY "..\\server.html" IDS_TRANSPARENCY_HTML BINARY "..\\transparency.html" IDS_URLREQUEST_HTML BINARY "..\\urlrequest.html" +IDS_WEBSOCKET_HTML BINARY "..\\websocket.html" IDS_WINDOW_HTML BINARY "..\\window.html" IDS_WINDOW_ICON_1X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.1x.png" IDS_WINDOW_ICON_2X_PNG BINARY "..\\..\\..\\shared\\resources\\window_icon.2x.png" diff --git a/tests/cefclient/resources/window.html b/tests/cefclient/resources/window.html index 7d4fe6640..7703d0d7d 100644 --- a/tests/cefclient/resources/window.html +++ b/tests/cefclient/resources/window.html @@ -2,6 +2,19 @@ Window Test - -
    + + Click a button to perform the associated window action.


    (minimizes and then restores the window as topmost) -
    X: Y: Width: Height: +
    +X: +Y: +Width: +Height:
    diff --git a/tests/cefclient/resources/xmlhttprequest.html b/tests/cefclient/resources/xmlhttprequest.html index 20dbef919..8da20f3dd 100644 --- a/tests/cefclient/resources/xmlhttprequest.html +++ b/tests/cefclient/resources/xmlhttprequest.html @@ -3,8 +3,20 @@ WebSocket Test"; + // clang-format on + } + + void OnHandlerCreated(CefRefPtr handler) override { + handler->SetExpectedConnectionCount(connection_ct_); + handler->SetExpectedWsRequestCount(connection_ct_); + handler->SetExpectedWsConnectedCount(connection_ct_); + handler->SetExpectedWsMessageCount(connection_ct_ * message_ct_); + + EchoWebSocketRequestHandler* echo_handler = + new EchoWebSocketRequestHandler(connection_ct_ * message_ct_); + ws_url_ = echo_handler->GetWebSocketUrl(); + handler->AddWsRequestHandler( + make_scoped_ptr(echo_handler)); + } + + void OnDoneMessage(const std::string& result) override { + const int complete_message_ct = atoi(result.c_str()); + EXPECT_EQ(connection_ct_ * message_ct_, complete_message_ct); + ShutdownServer(); + } + + private: + int connection_ct_; + int message_ct_; + bool in_parallel_; + std::string ws_url_; + + IMPLEMENT_REFCOUNTING(EchoWebSocketTestHandler); + DISALLOW_COPY_AND_ASSIGN(EchoWebSocketTestHandler); +}; + +} // namespace + +// Test handling of a single connection with a single message. +TEST(ServerTest, WebSocketSingleConnectionSingleMessage) { + CefRefPtr handler = + new EchoWebSocketTestHandler(1, 1, true); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test handling of a single connection with multiple messages. +TEST(ServerTest, WebSocketSingleConnectionMultipleMessages) { + CefRefPtr handler = + new EchoWebSocketTestHandler(1, 5, true); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test handling of multiple connections and multiple messages in parallel. +TEST(ServerTest, WebSocketMultipleConnectionsMultipleMessagesInParallel) { + CefRefPtr handler = + new EchoWebSocketTestHandler(4, 6, true); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test handling of multiple connections and multiple messages in serial. +TEST(ServerTest, WebSocketMultipleConnectionsMultipleMessagesInSerial) { + CefRefPtr handler = + new EchoWebSocketTestHandler(4, 6, false); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} diff --git a/tools/cef_parser.py b/tools/cef_parser.py index 843d893a3..c356744cb 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -352,6 +352,8 @@ _simpletypes = { 'void': ['void', ''], 'void*': ['void*', 'NULL'], 'int': ['int', '0'], + 'int16': ['int16', '0'], + 'uint16': ['uint16', '0'], 'int32': ['int32', '0'], 'uint32': ['uint32', '0'], 'int64': ['int64', '0'],