diff --git a/NEWS.md b/NEWS.md index 76b1b68..cd3eafb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # websocket (development version) +* Add support for connecting to websocket through a proxy + # websocket 1.4.1 * Add UCRT toolchain support (@jeroen, #82) diff --git a/R/cpp11.R b/R/cpp11.R index 6076008..3032b58 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -12,6 +12,14 @@ wsAddProtocols <- function(wsc_xptr, protocols) { invisible(.Call(`_websocket_wsAddProtocols`, wsc_xptr, protocols)) } +wsAddProxy <- function(wsc_xptr, proxy_url) { + invisible(.Call(`_websocket_wsAddProxy`, wsc_xptr, proxy_url)) +} + +wsAddProxyBasicAuth <- function(wsc_xptr, username, password) { + invisible(.Call(`_websocket_wsAddProxyBasicAuth`, wsc_xptr, username, password)) +} + wsConnect <- function(wsc_xptr) { invisible(.Call(`_websocket_wsConnect`, wsc_xptr)) } diff --git a/R/websocket.R b/R/websocket.R index fdd95fd..4a1b9ee 100644 --- a/R/websocket.R +++ b/R/websocket.R @@ -116,6 +116,11 @@ null_func <- function(...) { } #' @param maxMessageSize The maximum size of a message in bytes. If a message #' larger than this is sent, the connection will fail with the \code{message_too_big} #' protocol error. +#' @param proxyUrl Optional parameter to connect to websocket through proxy, +#' will usually begin with \code{http://} or \code{https://}. +#' @param proxyUsername Optional parameter to specify proxy username +#' @param proxyPassword Optional parameter to specify proxy password, can not be +#' \code{NULL} if \code{proxyUsername} is given. #' #' #' @name WebSocket @@ -151,7 +156,10 @@ WebSocket <- R6Class("WebSocket", accessLogChannels = c("none"), errorLogChannels = NULL, maxMessageSize = 32 * 1024 * 1024, - loop = later::current_loop() + loop = later::current_loop(), + proxyUrl = NULL, + proxyUsername = NULL, + proxyPassword = NULL ) { private$callbacks <- new.env(parent = emptyenv()) private$callbacks$open <- Callbacks$new() @@ -175,6 +183,15 @@ WebSocket <- R6Class("WebSocket", }) wsAddProtocols(private$wsObj, protocols) + if (!is.null(proxyUrl)) { + wsAddProxy(private$wsObj, proxyUrl) + + if (!is.null(proxyUsername)) { + if (is.null(proxyPassword)) + wsAddProxyBasicAuth(private$wsObj, proxyUsername, proxyPassword); + } + } + private$pendingConnect <- TRUE if (autoConnect) { self$connect() diff --git a/man/WebSocket.Rd b/man/WebSocket.Rd index adcc0f9..3538974 100644 --- a/man/WebSocket.Rd +++ b/man/WebSocket.Rd @@ -53,6 +53,14 @@ must call `ws$connect()` to establish the WebSocket connection.} \item{maxMessageSize}{The maximum size of a message in bytes. If a message larger than this is sent, the connection will fail with the \code{message_too_big} protocol error.} + +\item{proxyUrl}{Optional parameter to connect to websocket through proxy, +will usually begin with \code{http://} or \code{https://}.} + +\item{proxyUsername}{Optional parameter to specify proxy username} + +\item{proxyPassword}{Optional parameter to specify proxy password, can not be +\code{NULL} if \code{proxyUsername} is given.} } \description{ \preformatted{ diff --git a/src/client.hpp b/src/client.hpp index 3792ed6..1ea9805 100644 --- a/src/client.hpp +++ b/src/client.hpp @@ -49,6 +49,8 @@ class Client { virtual void setup_connection(std::string location, ws_websocketpp::lib::error_code &ec) = 0; virtual void append_header(std::string key, std::string value) = 0; virtual void add_subprotocol(std::string const & request) = 0; + virtual void set_proxy(std::string const & proxy_url) = 0; + virtual void set_proxy_basic_auth(std::string const & username, std::string const & password) = 0; virtual void connect() = 0; virtual std::string get_subprotocol() const = 0; @@ -137,6 +139,12 @@ class ClientImpl : public Client { void add_subprotocol(std::string const & value) { con->add_subprotocol(value); }; + void set_proxy(std::string const & proxy_url) { + this->con->set_proxy(proxy_url); + } + void set_proxy_basic_auth(std::string const & username, std::string const & password) { + this->con->set_proxy_basic_auth(username, password); + } void connect() { client.connect(this->con); }; diff --git a/src/cpp11.cpp b/src/cpp11.cpp index 52f02bd..e982345 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -3,6 +3,7 @@ #include "cpp11/declarations.hpp" +#include // websocket.cpp SEXP wsCreate(std::string uri, int loop_id, cpp11::environment robjPublic, cpp11::environment robjPrivate, cpp11::strings accessLogChannels, cpp11::strings errorLogChannels, int maxMessageSize); @@ -28,6 +29,22 @@ extern "C" SEXP _websocket_wsAddProtocols(SEXP wsc_xptr, SEXP protocols) { END_CPP11 } // websocket.cpp +void wsAddProxy(SEXP wsc_xptr, std::string proxy_url); +extern "C" SEXP _websocket_wsAddProxy(SEXP wsc_xptr, SEXP proxy_url) { + BEGIN_CPP11 + wsAddProxy(cpp11::as_cpp>(wsc_xptr), cpp11::as_cpp>(proxy_url)); + return R_NilValue; + END_CPP11 +} +// websocket.cpp +void wsAddProxyBasicAuth(SEXP wsc_xptr, std::string username, std::string password); +extern "C" SEXP _websocket_wsAddProxyBasicAuth(SEXP wsc_xptr, SEXP username, SEXP password) { + BEGIN_CPP11 + wsAddProxyBasicAuth(cpp11::as_cpp>(wsc_xptr), cpp11::as_cpp>(username), cpp11::as_cpp>(password)); + return R_NilValue; + END_CPP11 +} +// websocket.cpp void wsConnect(SEXP wsc_xptr); extern "C" SEXP _websocket_wsConnect(SEXP wsc_xptr) { BEGIN_CPP11 @@ -75,19 +92,10 @@ extern "C" SEXP _websocket_wsUpdateLogChannels(SEXP wsc_xptr, SEXP accessOrError } extern "C" { -/* .Call calls */ -extern SEXP _websocket_wsAddProtocols(SEXP, SEXP); -extern SEXP _websocket_wsAppendHeader(SEXP, SEXP, SEXP); -extern SEXP _websocket_wsClose(SEXP, SEXP, SEXP); -extern SEXP _websocket_wsConnect(SEXP); -extern SEXP _websocket_wsCreate(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); -extern SEXP _websocket_wsProtocol(SEXP); -extern SEXP _websocket_wsSend(SEXP, SEXP); -extern SEXP _websocket_wsState(SEXP); -extern SEXP _websocket_wsUpdateLogChannels(SEXP, SEXP, SEXP, SEXP); - static const R_CallMethodDef CallEntries[] = { {"_websocket_wsAddProtocols", (DL_FUNC) &_websocket_wsAddProtocols, 2}, + {"_websocket_wsAddProxy", (DL_FUNC) &_websocket_wsAddProxy, 2}, + {"_websocket_wsAddProxyBasicAuth", (DL_FUNC) &_websocket_wsAddProxyBasicAuth, 3}, {"_websocket_wsAppendHeader", (DL_FUNC) &_websocket_wsAppendHeader, 3}, {"_websocket_wsClose", (DL_FUNC) &_websocket_wsClose, 3}, {"_websocket_wsConnect", (DL_FUNC) &_websocket_wsConnect, 1}, @@ -100,7 +108,7 @@ static const R_CallMethodDef CallEntries[] = { }; } -extern "C" void R_init_websocket(DllInfo* dll){ +extern "C" attribute_visible void R_init_websocket(DllInfo* dll){ R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); R_forceSymbols(dll, TRUE); diff --git a/src/websocket.cpp b/src/websocket.cpp index 010e3ad..96a6d70 100644 --- a/src/websocket.cpp +++ b/src/websocket.cpp @@ -84,6 +84,20 @@ void wsAddProtocols(SEXP wsc_xptr, cpp11::strings protocols) { } } +[[cpp11::register]] +void wsAddProxy(SEXP wsc_xptr, std::string proxy_url) { + ASSERT_MAIN_THREAD() + shared_ptr wsc = xptrGetWsConn(wsc_xptr); + wsc->client->set_proxy(proxy_url); +} + +[[cpp11::register]] +void wsAddProxyBasicAuth(SEXP wsc_xptr, std::string username, std::string password) { + ASSERT_MAIN_THREAD() + shared_ptr wsc = xptrGetWsConn(wsc_xptr); + wsc->client->set_proxy_basic_auth(username, password); +} + [[cpp11::register]] void wsConnect(SEXP wsc_xptr) { ASSERT_MAIN_THREAD()