1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "mod_proxy.h"
19 #include <sys/types.h>
20 #include <sys/socket.h>
24 #error This module only works on unix platforms with the correct OS support
27 /* for apr_wait_for_io_or_timeout */
28 #include "apr_support.h"
30 #include "mod_proxy_fdpass.h"
32 module AP_MODULE_DECLARE_DATA proxy_fdpass_module
;
34 static int proxy_fdpass_canon(request_rec
*r
, char *url
)
38 if (strncasecmp(url
, "fd://", 5) == 0) {
45 path
= ap_server_root_relative(r
->pool
, url
);
47 r
->filename
= apr_pstrcat(r
->pool
, "proxy:fd://", path
, NULL
);
49 ap_log_rerror(APLOG_MARK
, APLOG_DEBUG
, 0, r
,
50 "proxy: FD: set r->filename to %s", r
->filename
);
54 /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
55 static apr_status_t
socket_connect_un(apr_socket_t
*sock
,
56 struct sockaddr_un
*sa
)
59 apr_os_sock_t rawsock
;
60 apr_interval_time_t t
;
62 rv
= apr_os_sock_get(&rawsock
, sock
);
67 rv
= apr_socket_timeout_get(sock
, &t
);
73 rv
= connect(rawsock
, (struct sockaddr
*)sa
, sizeof(*sa
) + strlen(sa
->sun_path
));
74 } while (rv
== -1 && errno
== EINTR
);
76 if ((rv
== -1) && (errno
== EINPROGRESS
|| errno
== EALREADY
)
78 rv
= apr_wait_for_io_or_timeout(NULL
, sock
, 0);
79 if (rv
!= APR_SUCCESS
) {
84 if (rv
== -1 && errno
!= EISCONN
) {
91 static apr_status_t
get_socket_from_path(apr_pool_t
*p
,
93 apr_socket_t
**out_sock
)
95 struct sockaddr_un sa
;
100 rv
= apr_socket_create(&s
, AF_UNIX
, SOCK_STREAM
, 0, p
);
106 sa
.sun_family
= AF_UNIX
;
107 apr_cpystrn(sa
.sun_path
, path
, sizeof(sa
.sun_path
));
109 rv
= socket_connect_un(s
, &sa
);
120 static apr_status_t
send_socket(apr_pool_t
*p
,
122 apr_socket_t
*outbound
)
125 apr_os_sock_t rawsock
;
126 apr_os_sock_t srawsock
;
128 struct cmsghdr
*cmsg
;
132 rv
= apr_os_sock_get(&rawsock
, outbound
);
137 rv
= apr_os_sock_get(&srawsock
, s
);
142 memset(&msg
, 0, sizeof(msg
));
150 cmsg
= apr_palloc(p
, sizeof(*cmsg
) + sizeof(rawsock
));
151 cmsg
->cmsg_len
= sizeof(*cmsg
) + sizeof(rawsock
);
152 cmsg
->cmsg_level
= SOL_SOCKET
;
153 cmsg
->cmsg_type
= SCM_RIGHTS
;
155 memcpy(CMSG_DATA(cmsg
), &rawsock
, sizeof(rawsock
));
157 msg
.msg_control
= cmsg
;
158 msg
.msg_controllen
= cmsg
->cmsg_len
;
160 rv
= sendmsg(srawsock
, &msg
, 0);
170 static int proxy_fdpass_handler(request_rec
*r
, proxy_worker
*worker
,
171 proxy_server_conf
*conf
,
172 char *url
, const char *proxyname
,
173 apr_port_t proxyport
)
177 apr_socket_t
*clientsock
;
179 if (strncasecmp(url
, "fd://", 5) == 0) {
186 rv
= get_socket_from_path(r
->pool
, url
, &sock
);
189 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
,
190 "proxy: FD: Failed to connect to '%s'",
192 return HTTP_INTERNAL_SERVER_ERROR
;
197 const char *flush_method
= worker
->flusher
? worker
->flusher
: "flush";
199 proxy_fdpass_flush
*flush
= ap_lookup_provider(PROXY_FDPASS_FLUSHER
, flush_method
, "0");
202 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
,
203 "proxy: FD: Unable to find configured flush "
204 "provider '%s'", flush_method
);
205 return HTTP_INTERNAL_SERVER_ERROR
;
208 status
= flush
->flusher(r
);
214 /* XXXXX: THIS IS AN EVIL HACK */
215 /* There should really be a (documented) public API for this ! */
216 clientsock
= ap_get_module_config(r
->connection
->conn_config
, &core_module
);
218 rv
= send_socket(r
->pool
, sock
, clientsock
);
220 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
,
221 "proxy: FD: send_socket failed:");
222 return HTTP_INTERNAL_SERVER_ERROR
;
227 /* Create a dummy unconnected socket, and set it as the one we were
228 * connected to, so that when the core closes it, it doesn't close
229 * the tcp connection to the client.
231 rv
= apr_socket_create(&dummy
, APR_INET
, SOCK_STREAM
, APR_PROTO_TCP
, r
->connection
->pool
);
233 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
,
234 "proxy: FD: failed to create dummy socket");
235 return HTTP_INTERNAL_SERVER_ERROR
;
237 ap_set_module_config(r
->connection
->conn_config
, &core_module
, dummy
);
244 static int standard_flush(request_rec
*r
)
247 apr_bucket_brigade
*bb
;
250 r
->connection
->keepalive
= AP_CONN_CLOSE
;
252 bb
= apr_brigade_create(r
->pool
, r
->connection
->bucket_alloc
);
253 e
= apr_bucket_flush_create(r
->connection
->bucket_alloc
);
255 APR_BRIGADE_INSERT_TAIL(bb
, e
);
257 status
= ap_pass_brigade(r
->output_filters
, bb
);
260 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, status
, r
,
261 "proxy: FD: ap_pass_brigade failed:");
269 static const proxy_fdpass_flush builtin_flush
=
276 static void register_hooks(apr_pool_t
*p
)
278 ap_register_provider(p
, PROXY_FDPASS_FLUSHER
, "flush", "0", &builtin_flush
);
279 proxy_hook_scheme_handler(proxy_fdpass_handler
, NULL
, NULL
, APR_HOOK_FIRST
);
280 proxy_hook_canon_handler(proxy_fdpass_canon
, NULL
, NULL
, APR_HOOK_FIRST
);
283 module AP_MODULE_DECLARE_DATA proxy_fdpass_module
= {
284 STANDARD20_MODULE_STUFF
,
285 NULL
, /* create per-directory config structure */
286 NULL
, /* merge per-directory config structures */
287 NULL
, /* create per-server config structure */
288 NULL
, /* merge per-server config structures */
289 NULL
, /* command apr_table_t */
290 register_hooks
/* register hooks */