You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2013/03/05 18:29:28 UTC
svn commit: r1452911 - in /httpd/httpd/trunk/modules/proxy: config.m4
mod_proxy_websocket.c
Author: jim
Date: Tue Mar 5 17:29:28 2013
New Revision: 1452911
URL: http://svn.apache.org/r1452911
Log:
Rough start for simple, tunneling websocket proxy support.
Compiles at this stage and that's all I know :)
Added:
httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c (with props)
Modified:
httpd/httpd/trunk/modules/proxy/config.m4
Modified: httpd/httpd/trunk/modules/proxy/config.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/config.m4?rev=1452911&r1=1452910&r2=1452911&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/proxy/config.m4 (original)
+++ httpd/httpd/trunk/modules/proxy/config.m4 Tue Mar 5 17:29:28 2013
@@ -20,6 +20,7 @@ proxy_fcgi_objs="mod_proxy_fcgi.lo"
proxy_scgi_objs="mod_proxy_scgi.lo"
proxy_fdpass_objs="mod_proxy_fdpass.lo"
proxy_ajp_objs="mod_proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo ajp_utils.lo"
+proxy_websocket_objs="mod_proxy_websocket.lo"
proxy_balancer_objs="mod_proxy_balancer.lo"
case "$host" in
@@ -33,6 +34,7 @@ case "$host" in
proxy_scgi_objs="$proxy_scgi_objs mod_proxy.la"
proxy_fdpass_objs="$proxy_fdpass_objs mod_proxy.la"
proxy_ajp_objs="$proxy_ajp_objs mod_proxy.la"
+ proxy_websocket_objs="$proxy_websocket_objs mod_proxy.la"
proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la"
;;
esac
@@ -52,6 +54,7 @@ APACHE_MODULE(proxy_fdpass, Apache proxy
enable_proxy_fdpass=no
fi
],proxy)
+APACHE_MODULE(proxy_websocket, Apache proxy websocket module. Requires and is enabled by --enable-proxy., $proxy_websocket_objs, , $proxy_mods_enable,, proxy)
APACHE_MODULE(proxy_ajp, Apache proxy AJP module. Requires and is enabled by --enable-proxy., $proxy_ajp_objs, , $proxy_mods_enable,, proxy)
APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module. Requires and is enabled by --enable-proxy., $proxy_balancer_objs, , $proxy_mods_enable,, proxy)
Added: httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c?rev=1452911&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c (added)
+++ httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c Tue Mar 5 17:29:28 2013
@@ -0,0 +1,346 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mod_proxy.h"
+
+module AP_MODULE_DECLARE_DATA proxy_websocket_module;
+
+/*
+ * 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_websocket_canon(request_rec *r, char *url)
+{
+ char *host, *path, sport[7];
+ char *search = NULL;
+ const char *err;
+ char *scheme;
+ apr_port_t port, def_port;
+
+ /* ap_port_of_scheme() */
+ if (strncasecmp(url, "ws:", 3) == 0) {
+ url += 3;
+ scheme = "ws";
+ def_port = apr_uri_port_of_scheme("http");
+ }
+ else if (strncasecmp(url, "wss:", 4) == 0) {
+ url += 4;
+ scheme = "wss";
+ def_port = apr_uri_port_of_scheme("https");
+ }
+ else {
+ return DECLINED;
+ }
+
+ port = def_port;
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
+
+ /*
+ * do syntactic 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() "error parsing URL %s: %s",
+ url, err);
+ return HTTP_BAD_REQUEST;
+ }
+
+ /*
+ * now parse path/search args, according to rfc1738:
+ * process the path. With proxy-nocanon set (by
+ * mod_proxy) we use the raw, unparsed uri
+ */
+ if (apr_table_get(r->notes, "proxy-nocanon")) {
+ path = url; /* this is the raw path */
+ }
+ else {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
+ r->proxyreq);
+ search = r->args;
+ }
+ if (path == NULL)
+ return HTTP_BAD_REQUEST;
+
+ apr_snprintf(sport, sizeof(sport), ":%d", port);
+
+ 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);
+ return OK;
+}
+
+
+static int proxy_websocket_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
+ apr_bucket_brigade *bb, char *name)
+{
+ int rv;
+#ifdef DEBUGGING
+ apr_off_t len;
+#endif
+
+ do {
+ apr_brigade_cleanup(bb);
+ rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
+ APR_NONBLOCK_READ, AP_IOBUFSIZE);
+ if (rv == APR_SUCCESS) {
+ if (c_o->aborted)
+ return APR_EPIPE;
+ if (APR_BRIGADE_EMPTY(bb))
+ break;
+#ifdef DEBUGGING
+ len = -1;
+ apr_brigade_length(bb, 0, &len);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "read %" APR_OFF_T_FMT
+ " bytes from %s", len, name);
+#endif
+ rv = ap_pass_brigade(c_o->output_filters, bb);
+ if (rv == APR_SUCCESS) {
+ ap_fflush(c_o->output_filters, bb);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
+ "error on %s - ap_pass_brigade",
+ name);
+ }
+ } else if (!APR_STATUS_IS_EAGAIN(rv)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO()
+ "error on %s - ap_get_brigade",
+ name);
+ }
+ } while (rv == APR_SUCCESS);
+
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ rv = APR_SUCCESS;
+ }
+ return rv;
+}
+
+
+/*
+ * process the request and write the response.
+ */
+static int ap_proxy_websocket_request(apr_pool_t *p, request_rec *r,
+ proxy_conn_rec *conn,
+ conn_rec *origin,
+ proxy_dir_conf *conf,
+ apr_uri_t *uri,
+ char *url, char *server_portstr)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_pollset_t *pollset;
+ apr_pollfd_t pollfd;
+ const apr_pollfd_t *signalled;
+ apr_int32_t pollcnt, pi;
+ apr_int16_t pollevent;
+ conn_rec *c = r->connection;
+ apr_socket_t *sock = conn->sock;
+ conn_rec *backconn = conn->connection;
+ int client_error = 0;
+ apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
+ apr_socket_t *client_socket = ap_get_conn_socket(c);
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()");
+
+ if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
+ "error apr_pollset_create()");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ pollfd.p = p;
+ pollfd.desc_type = APR_POLL_SOCKET;
+ pollfd.reqevents = APR_POLLIN;
+ pollfd.desc.s = sock;
+ pollfd.client_data = NULL;
+ apr_pollset_add(pollset, &pollfd);
+
+ pollfd.desc.s = client_socket;
+ apr_pollset_add(pollset, &pollfd);
+
+
+ r->output_filters = c->output_filters;
+ r->proto_output_filters = c->output_filters;
+ r->input_filters = c->input_filters;
+ r->proto_input_filters = c->input_filters;
+
+ while (1) { /* Infinite loop until error (one side closes the connection) */
+ if ((rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled))
+ != APR_SUCCESS) {
+ if (APR_STATUS_IS_EINTR(rv)) {
+ continue;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO() "error apr_poll()");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+#ifdef DEBUGGING
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "woke from poll(), i=%d", pollcnt);
+#endif
+
+ for (pi = 0; pi < pollcnt; pi++) {
+ const apr_pollfd_t *cur = &signalled[pi];
+
+ if (cur->desc.s == sock) {
+ pollevent = cur->rtnevents;
+ if (pollevent & APR_POLLIN) {
+#ifdef DEBUGGING
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "sock was readable");
+#endif
+ rv = proxy_websocket_transfer(r, backconn, c, bb, "sock");
+ }
+ else if ((pollevent & APR_POLLERR)
+ || (pollevent & APR_POLLHUP)) {
+ rv = APR_EPIPE;
+ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO()
+ "err/hup on backconn");
+ }
+ if (rv != APR_SUCCESS)
+ client_error = 1;
+ }
+ else if (cur->desc.s == client_socket) {
+ pollevent = cur->rtnevents;
+ if (pollevent & APR_POLLIN) {
+#ifdef DEBUGGING
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "client was readable");
+#endif
+ rv = proxy_websocket_transfer(r, c, backconn, bb, "client");
+ }
+ }
+ else {
+ rv = APR_EBADF;
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+ "unknown socket in pollset");
+ }
+
+ }
+ if (rv != APR_SUCCESS) {
+ break;
+ }
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+ "finished with poll() - cleaning up");
+
+ if (client_error) {
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ return OK;
+}
+
+/*
+ */
+static int proxy_websocket_handler(request_rec *r, proxy_worker *worker,
+ proxy_server_conf *conf,
+ char *url, const char *proxyname,
+ apr_port_t proxyport)
+{
+ int status;
+ char server_portstr[32];
+ conn_rec *origin = NULL;
+ proxy_conn_rec *backend = NULL;
+ char *scheme;
+ int retry;
+ proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
+ &proxy_module);
+ apr_pool_t *p = r->pool;
+ apr_uri_t *uri;
+
+ if (strncasecmp(url, "wss:", 4) == 0) {
+ scheme = "WSS";
+ }
+ else if (strncasecmp(url, "ws:", 3) == 0) {
+ scheme = "WS";
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() "declining URL %s", url);
+ return DECLINED;
+ }
+
+ uri = apr_palloc(p, sizeof(*uri));
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() "serving URL %s", url);
+
+ /* create space for state information */
+ status = ap_proxy_acquire_connection(scheme, &backend, worker,
+ r->server);
+ if (status != OK) {
+ if (backend) {
+ backend->close = 1;
+ ap_proxy_release_connection(scheme, backend, r->server);
+ }
+ return status;
+ }
+
+ backend->is_ssl = 0;
+ backend->close = 0;
+
+ retry = 0;
+ while (retry < 2) {
+ char *locurl = url;
+ /* Step One: Determine Who To Connect To */
+ status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+ uri, &locurl, proxyname, proxyport,
+ server_portstr,
+ sizeof(server_portstr));
+
+ if (status != OK)
+ break;
+
+ /* Step Two: Make the Connection */
+ if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
+ "failed to make connection to backend: %s",
+ backend->hostname);
+ status = HTTP_SERVICE_UNAVAILABLE;
+ break;
+ }
+
+ /* Step Three: Process the Request */
+ status = ap_proxy_websocket_request(p, r, backend, origin, dconf, uri, locurl,
+ server_portstr);
+ break;
+ }
+
+ /* Do not close the socket */
+ ap_proxy_release_connection(scheme, backend, r->server);
+ return status;
+}
+
+static void ap_proxy_http_register_hook(apr_pool_t *p)
+{
+ proxy_hook_scheme_handler(proxy_websocket_handler, NULL, NULL, APR_HOOK_FIRST);
+ proxy_hook_canon_handler(proxy_websocket_canon, NULL, NULL, APR_HOOK_FIRST);
+}
+
+AP_DECLARE_MODULE(proxy_websocket) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command apr_table_t */
+ ap_proxy_http_register_hook /* register hooks */
+};
+
Propchange: httpd/httpd/trunk/modules/proxy/mod_proxy_websocket.c
------------------------------------------------------------------------------
svn:eol-style = native