mod_proxy_wstunnel.c revision 5d9953b57425b6ee84fdf2666f687c101e2ffd65
0922cbe8300e97215564748d449824f458196335Lennart Poettering/* Licensed to the Apache Software Foundation (ASF) under one or more
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * contributor license agreements. See the NOTICE file distributed with
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * this work for additional information regarding copyright ownership.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * The ASF licenses this file to You under the Apache License, Version 2.0
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * (the "License"); you may not use this file except in compliance with
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * the License. You may obtain a copy of the License at
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * http://www.apache.org/licenses/LICENSE-2.0
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * Unless required by applicable law or agreed to in writing, software
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * distributed under the License is distributed on an "AS IS" BASIS,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * See the License for the specific language governing permissions and
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * limitations under the License.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringmodule AP_MODULE_DECLARE_DATA proxy_wstunnel_module;
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringtypedef struct {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringtypedef struct ws_baton_t {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering apr_pool_t *subpool; /* cleared before each suspend, destroyed when request ends */
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering char *scheme; /* required to release the proxy connection */
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic apr_status_t proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic void proxy_wstunnel_callback(void *b);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic int proxy_wstunnel_pump(ws_baton_t *baton, apr_time_t timeout, int try_async) {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering proxy_conn_rec *conn = baton->proxy_connrec;
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering apr_socket_t *client_socket = baton->client_soc;
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering if ((rv = apr_pollset_poll(pollset, timeout, &pollcnt, &signalled))
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02542) "Attempting to go async");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(02444) "error apr_poll()");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02445)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering const apr_pollfd_t *cur = &signalled[pi];
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02446)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering "sock was readable");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering rv = proxy_wstunnel_transfer(r, backconn, c, bb, "sock");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02447)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "error on backconn");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02605)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "unknown event on backconn %d", pollevent);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering if (pollevent & (APR_POLLIN | APR_POLLHUP)) {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02448)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "client was readable");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering rv = proxy_wstunnel_transfer(r, c, backconn, bb, "client");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, APLOGNO(02607)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "error on client conn");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO(02606)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "unknown event on client conn %d", pollevent);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02449)
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering "unknown socket in pollset");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering "finished with poll() - cleaning up");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void proxy_wstunnel_finish(ws_baton_t *baton) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_wstunnel_finish");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering baton->proxy_connrec->close = 1; /* new handshake expected on each back-conn */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering baton->r->connection->keepalive = AP_CONN_CLOSE;
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_proxy_release_connection(baton->scheme, baton->proxy_connrec, baton->r->server);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_lingering_close(baton->r->connection);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_mpm_resume_suspended(baton->r->connection);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_process_request_after_handler(baton->r); /* don't touch baton or r after here */
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering/* If neither socket becomes readable in the specified timeout,
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering * this callback will kill the request. We do not have to worry about
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * having a cancel and a IO both queued.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void proxy_wstunnel_cancel_callback(void *b)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_wstunnel_cancel_callback, IO timed out");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering/* Invoked by the event loop when data is ready on either end.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * Pump both ends until they'd block and then start over again
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * We don't need the invoke_mtx, since we never put multiple callback events
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * in the queue.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic void proxy_wstunnel_callback(void *b) {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering apr_socket_t *sockets[3] = {NULL, NULL, NULL};
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering proxyws_dir_conf *dconf = ap_get_module_config(baton->r->per_dir_config, &proxy_wstunnel_module);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering status = proxy_wstunnel_pump(baton, dconf->async_delay, dconf->is_async);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_mpm_register_socket_callback_timeout(sockets, baton->subpool, 1,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, baton->r, "proxy_wstunnel_callback suspend");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * Canonicalise http-like URLs.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering * scheme is the scheme for the URL
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * url is the URL starting with the first '/'
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * def_port is the default port for this scheme.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic int proxy_wstunnel_canon(request_rec *r, char *url)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* ap_port_of_scheme() */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering def_port = apr_uri_port_of_scheme("http");
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering else if (strncasecmp(url, "wss:", 4) == 0) {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering def_port = apr_uri_port_of_scheme("https");
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "canonicalising URL %s", url);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * do syntactic check.
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * We break the URL into host, port, path, search
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02439) "error parsing URL %s: %s",
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * now parse path/search args, according to rfc1738:
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * process the path. With proxy-nocanon set (by
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering * mod_proxy) we use the raw, unparsed uri
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering if (apr_table_get(r->notes, "proxy-nocanon")) {
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering apr_snprintf(sport, sizeof(sport), ":%d", port);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering /* if literal IPv6 address */
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering host = apr_pstrcat(r->pool, "[", host, "]", NULL);
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "//", host, sport,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poetteringstatic apr_status_t proxy_wstunnel_transfer(request_rec *r, conn_rec *c_i, conn_rec *c_o,
5d6a86d7a034a1fb3d6e3f1b58e2c13739270894Lennart Poettering rv = ap_get_brigade(c_i->input_filters, bb, AP_MODE_READBYTES,
return APR_EPIPE;
#ifdef DEBUGGING
name);
name);
return rv;
char *buf;
apr_bucket *e;
int status;
return rv;
return rv;
return HTTP_INTERNAL_SERVER_ERROR;
pollfd.p = p;
baton->r = r;
return SUSPENDED;
return HTTP_INTERNAL_SERVER_ERROR;
return status;
int status;
char *scheme;
int retry;
int is_ssl = 0;
return DECLINED;
r->server);
if (backend) {
return status;
retry = 0;
sizeof(server_portstr));
return status;
return (void *) new;
return NULL;
return NULL;
AP_INIT_FLAG("ProxyWebsocketAsync", ap_set_flag_slot_char, (void*)APR_OFFSETOF(proxyws_dir_conf, is_async),
{NULL}