mod_proxy_wstunnel.c revision ac95effcd4bcdf02e41becbec4e9f2d3c577e7fd
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny/* Licensed to the Apache Software Foundation (ASF) under one or more
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * contributor license agreements. See the NOTICE file distributed with
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * this work for additional information regarding copyright ownership.
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * The ASF licenses this file to You under the Apache License, Version 2.0
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * (the "License"); you may not use this file except in compliance with
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * the License. You may obtain a copy of the License at
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * Unless required by applicable law or agreed to in writing, software
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * distributed under the License is distributed on an "AS IS" BASIS,
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * See the License for the specific language governing permissions and
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * limitations under the License.
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zelenymodule AP_MODULE_DECLARE_DATA proxy_wstunnel_module;
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zelenytypedef struct ws_baton_t {
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zelenystatic int proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozekstatic int proxy_wstunnel_pump(ws_baton_t *baton, apr_time_t timeout) {
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny if ((rv = apr_pollset_poll(pollset, timeout, &pollcnt, &signalled))
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02542) "Attempting to go asynch");
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444) "error apr_poll()");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02445)
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02446)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "sock was readable");
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek rv = proxy_wstunnel_transfer(r, backconn, c, bb, "sock");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "error on backconn");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02448)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "client was readable");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny rv = proxy_wstunnel_transfer(r, c, backconn, bb, "client");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "error on client conn");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "unknown socket in pollset");
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek "finished with poll() - cleaning up");
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozekstatic void proxy_wstunnel_callback(void *b) {
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek status = proxy_wstunnel_pump(baton, apr_time_from_sec(5));
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_mpm_register_socket_callback(sockets, baton->subpool, 1, proxy_wstunnel_callback, baton);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_mpm_unregister_socket_callback(sockets, baton->subpool);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * Canonicalise http-like URLs.
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * scheme is the scheme for the URL
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * url is the URL starting with the first '/'
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * def_port is the default port for this scheme.
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozekstatic int proxy_wstunnel_canon(request_rec *r, char *url)
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek const char *err;
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek /* ap_port_of_scheme() */
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * do syntactic check.
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * We break the URL into host, port, path, search
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02439) "error parsing URL %s: %s",
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * now parse path/search args, according to rfc1738:
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * process the path. With proxy-nocanon set (by
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek * mod_proxy) we use the raw, unparsed uri
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek if (apr_table_get(r->notes, "proxy-nocanon")) {
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek apr_snprintf(sport, sizeof(sport), ":%d", port);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek /* if literal IPv6 address */
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek host = apr_pstrcat(r->pool, "[", host, "]", NULL);
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport,
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozekstatic int proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02440)
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02441)
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek "error on %s - ap_pass_brigade",
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek } else if (!APR_STATUS_IS_EAGAIN(rv) && !APR_STATUS_IS_EOF(rv)) {
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(02442)
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek "error on %s - ap_get_brigade",
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozek/* Search thru the input filters and remove the reqtimeout one */
8ba8222afca3026fd67af08e224b1d9e848aceaaJakub Hrozekstatic void remove_reqtimeout(ap_filter_t *next)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny filter = ap_get_input_filter_handle("reqtimeout");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * process the request and write the response.
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zelenystatic int ap_proxy_wstunnel_request(apr_pool_t *p, request_rec *r,
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
aac3ca699a09090072ae4d68bdda8dec990ae393Sumit Bose apr_socket_t *client_socket = ap_get_conn_socket(c);
aac3ca699a09090072ae4d68bdda8dec990ae393Sumit Bose ws_baton_t *baton = apr_pcalloc(r->pool, sizeof(ws_baton_t));
aac3ca699a09090072ae4d68bdda8dec990ae393Sumit Bose header_brigade = apr_brigade_create(p, backconn->bucket_alloc);
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "sending request");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny rv = ap_proxy_create_hdrbrgd(p, header_brigade, r, conn,
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce buf = apr_pstrcat(p, "Upgrade: WebSocket", CRLF, "Connection: Upgrade", CRLF, CRLF, NULL);
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny if ((rv = ap_proxy_pass_brigade(c->bucket_alloc, r, conn, backconn,
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, "setting up poll()");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny if ((rv = apr_pollset_create(&pollset, 2, p, 0)) != APR_SUCCESS) {
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02443)
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny "error apr_pollset_create()");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny apr_socket_opt_set(client_socket, APR_SO_NONBLOCK, 1);
99bac83188601c2b07e0b141aac7dc7d882b464aSumit Bose apr_socket_opt_set(client_socket, APR_SO_KEEPALIVE, 1);
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny /* This handler should take care of the entire connection; make it so that
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny * nothing else is attempted on the connection after returning. */
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny status = proxy_wstunnel_pump(baton, apr_time_from_sec(5));
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny status = ap_mpm_register_socket_callback(sockets, baton->subpool, 1, proxy_wstunnel_callback, baton);
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02544) "No asynch support");
c0f9698cd951b7223f251ff2511c4b22a6e4ba60Jan Zeleny APLOGNO(02543) "error creating websockets tunnel");
return status;
int status;
char *scheme;
int retry;
return DECLINED;
r->server);
if (backend) {
return status;
retry = 0;
sizeof(server_portstr));
return status;