static apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n,
request_rec *r, int flags, int *read);
+static const char *get_url_scheme(const char **url, int *is_ssl)
+{
+ const char *u = *url;
+
+ switch (u[0]) {
+ case 'h':
+ case 'H':
+ if (strncasecmp(u + 1, "ttp", 3) == 0) {
+ if (u[4] == ':') {
+ *is_ssl = 0;
+ *url = u + 5;
+ return "http";
+ }
+ if (apr_tolower(u[4]) == 's' && u[5] == ':') {
+ *is_ssl = 1;
+ *url = u + 6;
+ return "https";
+ }
+ }
+ break;
+
+ case 'w':
+ case 'W':
+ if (apr_tolower(u[1]) == 's') {
+ if (u[2] == ':') {
+ *is_ssl = 0;
+ *url = u + 3;
+ return "ws";
+ }
+ if (apr_tolower(u[2]) == 's' && u[3] == ':') {
+ *is_ssl = 0;
+ *url = u + 4;
+ return "wss";
+ }
+ }
+ break;
+ }
+
+ *is_ssl = 0;
+ return NULL;
+}
/*
* Canonicalise http-like URLs.
* scheme is the scheme for the URL
* url is the URL starting with the first '/'
- * def_port is the default port for this scheme.
*/
static int proxy_http_canon(request_rec *r, char *url)
{
+ const char *base_url = url;
char *host, *path, sport[7];
char *search = NULL;
const char *err;
const char *scheme;
apr_port_t port, def_port;
+ int is_ssl = 0;
- /* ap_port_of_scheme() */
- if (ap_cstr_casecmpn(url, "http:", 5) == 0) {
- url += 5;
- scheme = "http";
- }
- else if (ap_cstr_casecmpn(url, "https:", 6) == 0) {
- url += 6;
- scheme = "https";
- }
- else {
+ scheme = get_url_scheme((const char **)&url, &is_ssl);
+ if (!scheme) {
return DECLINED;
}
- port = def_port = ap_proxy_port_of_scheme(scheme);
+ port = def_port = (is_ssl) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
- "HTTP: canonicalising URL %s", url);
+ "HTTP: canonicalising URL %s", base_url);
/* do syntatic check.
* We break the URL into host, port, path, search
err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
if (err) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01083)
- "error parsing URL %s: %s", url, err);
+ "error parsing URL %s: %s", base_url, err);
return HTTP_BAD_REQUEST;
}
if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
host = apr_pstrcat(r->pool, "[", host, "]", NULL);
}
+
r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
- "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
+ "/", path, (search) ? "?" : "", search, NULL);
return OK;
}
* may be an HTTP_UPGRADE_REQUIRED response or some other status where
* Upgrade makes sense to negotiate the protocol by other means.
*/
- if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade)) {
+ if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade,
+ (*req->proto == 'w')
+ ? "WebSocket" : NULL)) {
apr_table_setn(r->headers_out, "Connection", "Upgrade");
apr_table_setn(r->headers_out, "Upgrade", apr_pstrdup(p, upgrade));
}
apr_port_t proxyport)
{
int status;
- char *scheme;
- const char *proxy_function;
- const char *u;
+ const char *scheme;
+ const char *u = url;
proxy_http_req_t *req = NULL;
proxy_conn_rec *backend = NULL;
apr_bucket_brigade *input_brigade = NULL;
apr_pool_t *p = r->pool;
apr_uri_t *uri;
- /* find the scheme */
- u = strchr(url, ':');
- if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
- return DECLINED;
- if ((u - url) > 14)
- return HTTP_BAD_REQUEST;
- scheme = apr_pstrmemdup(p, url, u - url);
- /* scheme is lowercase */
- ap_str_tolower(scheme);
- /* is it for us? */
- if (strcmp(scheme, "https") == 0) {
- if (!ap_proxy_ssl_enable(NULL)) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01112)
- "HTTPS: declining URL %s (mod_ssl not configured?)",
- url);
- return DECLINED;
- }
- is_ssl = 1;
- proxy_function = "HTTPS";
+ scheme = get_url_scheme(&u, &is_ssl);
+ if (!scheme && proxyname && strncasecmp(url, "ftp:", 4) == 0) {
+ u = url + 4;
+ scheme = "ftp";
+ is_ssl = 0;
}
- else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01113) "HTTP: declining URL %s",
- url);
- return DECLINED; /* only interested in HTTP, or FTP via proxy */
+ if (!scheme || u[0] != '/' || u[1] != '/' || u[2] == '\0') {
+ if (!scheme && (u = strchr(url, ':')) && (u - url) > 14) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10262)
+ "overlong proxy URL scheme in %s", url);
+ return HTTP_BAD_REQUEST;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01113)
+ "HTTP: declining URL %s", url);
+ return DECLINED; /* only interested in HTTP, WS or FTP via proxy */
}
- else {
- if (*scheme == 'h')
- proxy_function = "HTTP";
- else
- proxy_function = "FTP";
+ if (is_ssl && !ap_proxy_ssl_enable(NULL)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01112)
+ "HTTP: declining URL %s (mod_ssl not configured?)", url);
+ return DECLINED;
}
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "HTTP: serving URL %s", url);
/* create space for state information */
- if ((status = ap_proxy_acquire_connection(proxy_function, &backend,
+ if ((status = ap_proxy_acquire_connection(scheme, &backend,
worker, r->server)) != OK) {
return status;
}
req->dconf = dconf;
req->worker = worker;
req->backend = backend;
- req->proto = proxy_function;
+ req->proto = scheme;
req->bucket_alloc = c->bucket_alloc;
req->can_go_async = (mpm_can_poll &&
dconf->async_delay_set &&
if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
req->force10 = 1;
}
- else if (*worker->s->upgrade) {
- /* Forward Upgrade header if it matches the configured one(s). */
+ else if (*worker->s->upgrade || *req->proto == 'w') {
+ /* Forward Upgrade header if it matches the configured one(s),
+ * the default being "WebSocket" for ws[s] schemes.
+ */
const char *upgrade = apr_table_get(r->headers_in, "Upgrade");
- if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade)) {
+ if (upgrade && ap_proxy_worker_can_upgrade(p, worker, upgrade,
+ (*req->proto == 'w')
+ ? "WebSocket" : NULL)) {
req->upgrade = upgrade;
}
}
}
/* Step Two: Make the Connection */
- if (ap_proxy_check_connection(proxy_function, backend, r->server, 1,
+ if (ap_proxy_check_connection(scheme, backend, r->server, 1,
PROXY_CHECK_CONN_EMPTY)
- && ap_proxy_connect_backend(proxy_function, backend, worker,
+ && ap_proxy_connect_backend(scheme, backend, worker,
r->server)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01114)
"HTTP: failed to make connection to backend: %s",
}
/* Step Three: Create conn_rec */
- if ((status = ap_proxy_connection_create_ex(proxy_function,
- backend, r)) != OK)
+ if ((status = ap_proxy_connection_create_ex(scheme, backend, r)) != OK)
break;
req->origin = backend->connection;
if (req->backend) {
if (status != OK)
req->backend->close = 1;
- ap_proxy_release_connection(proxy_function, req->backend,
- r->server);
+ ap_proxy_release_connection(scheme, req->backend, r->server);
}
return status;
}
*/
#include "mod_proxy.h"
+#include "http_config.h"
#include "ap_mpm.h"
module AP_MODULE_DECLARE_DATA proxy_wstunnel_module;
const char *scheme;
} ws_baton_t;
+static int fallback_to_mod_proxy_http;
+
static void proxy_wstunnel_callback(void *b);
static int proxy_wstunnel_pump(ws_baton_t *baton, int async)
static int proxy_wstunnel_check_trans(request_rec *r, const char *url)
{
+ if (fallback_to_mod_proxy_http) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "check_trans fallback");
+ return DECLINED;
+ }
+
if (ap_cstr_casecmpn(url, "ws:", 3) != 0
&& ap_cstr_casecmpn(url, "wss:", 4) != 0) {
return DECLINED;
char *scheme;
apr_port_t port, def_port;
+ if (fallback_to_mod_proxy_http) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "canon fallback");
+ return DECLINED;
+ }
+
/* ap_port_of_scheme() */
if (ap_cstr_casecmpn(url, "ws:", 3) == 0) {
url += 3;
apr_uri_t *uri;
int is_ssl = 0;
+ if (fallback_to_mod_proxy_http) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, "handler fallback");
+ return DECLINED;
+ }
+
if (ap_cstr_casecmpn(url, "wss:", 4) == 0) {
scheme = "WSS";
is_ssl = 1;
scheme = "WS";
}
else {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02450) "declining URL %s", url);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02450)
+ "declining URL %s", url);
return DECLINED;
}
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "serving URL %s", url);
upgrade = apr_table_get(r->headers_in, "Upgrade");
- if (!upgrade || (*worker->s->upgrade &&
- !ap_proxy_worker_can_upgrade(p, worker, upgrade))
- || (!*worker->s->upgrade &&
- ap_cstr_casecmp(upgrade, "WebSocket") != 0)) {
+ if (!upgrade || !ap_proxy_worker_can_upgrade(p, worker, upgrade,
+ "WebSocket")) {
const char *worker_upgrade = *worker->s->upgrade ? worker->s->upgrade
: "WebSocket";
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02900)
return NULL;
}
+static int proxy_wstunnel_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ fallback_to_mod_proxy_http = 0;
+ if (ap_state_query(AP_SQ_MAIN_STATE) != AP_SQ_MS_CREATE_PRE_CONFIG) {
+ apr_size_t i = 0;
+ const module *mod;
+ while ((mod = ap_loaded_modules[i++])) {
+ if (strcmp(mod->name, "mod_proxy_http.c") == 0) {
+ fallback_to_mod_proxy_http = 1;
+ break;
+ }
+ }
+ }
+
+ return OK;
+}
+
static const command_rec ws_proxy_cmds[] =
{
AP_INIT_TAKE1("ProxyWebsocketIdleTimeout", proxyws_set_idle, NULL,
{NULL}
};
-static void ap_proxy_http_register_hook(apr_pool_t *p)
+static void ws_proxy_hooks(apr_pool_t *p)
{
static const char * const aszSucc[] = { "mod_proxy_http.c", NULL};
+ ap_hook_post_config(proxy_wstunnel_post_config, NULL, NULL, APR_HOOK_MIDDLE);
proxy_hook_scheme_handler(proxy_wstunnel_handler, NULL, aszSucc, APR_HOOK_FIRST);
proxy_hook_check_trans(proxy_wstunnel_check_trans, NULL, aszSucc, APR_HOOK_MIDDLE);
proxy_hook_canon_handler(proxy_wstunnel_canon, NULL, aszSucc, APR_HOOK_FIRST);
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
ws_proxy_cmds, /* command apr_table_t */
- ap_proxy_http_register_hook /* register hooks */
+ ws_proxy_hooks /* register hooks */
};