After taking my own advice on Sunday, I reworked everything I'd done for
UDP support and focused on allowing the core I/O filters to work with
non-connected sockets. I'm happy to report that I've come up with a
working prototype for prefork that doesn't require substantial amounts
of mucking with the MPM code. It requires patches to APR (to add a
apr_socket_sendtov function, needed by the core output filter) and
APR-Util to support apr_socket_readfrom in socket buckets that aren't
connected). Both of these patches would be useful in APR/APR-Util
regardles (although the APR patch still requires work for win32, beos
and netware - I suppose I'll tackle at least win32 [which I'm familiar
with] later today and we'll see about the others). As such, I've added
these both to bugzilla to
https://issues.apache.org/bugzilla/show_bug.cgi?id=43309 and
https://issues.apache.org/bugzilla/show_bug.cgi?id=43302, respectively.
To go through what I've done in the main patch (for those who want an
idea of what they're looking at):
* Export ap_alloc_listner and take socket type/protocol as arguments
* In core_create_conn, if the remote_addr of the socket can't be
detected from the socket, try to run recvfrom in PEEK mode to get the
peer address
* Core input filters run as-is (patch to apr-util does everything)
* Core output filter tries (in init) to see if socket is connected. If
not, it turns of a new flag, connected, in ctx which is ultimately used
to decide whether to pass data to apr_socket_sendv or apr_socket_sendtov
* Additionally, disable SENDFILE for non-stream sockets
* Create ListenUDP config directive to create UDP listeners
* Alter all MPMs to only call lr->accept_func if it's defined
* In prefork, re-create the pollset in every iteration of child_main.
Hack the use of lr->active to add a flag (value 2) for in-use UDP sockets
Index: buckets/apr_buckets_socket.c
===================================================================
--- buckets/apr_buckets_socket.c (revision 572605)
+++ buckets/apr_buckets_socket.c (working copy)
@@ -23,6 +23,8 @@
char *buf;
apr_status_t rv;
apr_interval_time_t timeout;
+ apr_sockaddr_t *peerptr, peer;
+ int sd_type;
if (block == APR_NONBLOCK_READ) {
apr_socket_timeout_get(p, &timeout);
@@ -33,7 +35,18 @@
*len = APR_BUCKET_BUFF_SIZE;
buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */
- rv = apr_socket_recv(p, buf, len);
+ apr_socket_type_get(p, &sd_type);
+ if (sd_type == SOCK_STREAM) {
+ rv = apr_socket_recv(p, buf, len);
+ } else {
+ /* Is socket connected? */
+ if (apr_socket_addr_get(&peerptr, APR_REMOTE, p) != APR_SUCCESS) {
+ rv = apr_socket_recv(p, buf, len);
+ } else {
+ /* Caller is responsible for detecting peer on his own if needed */
+ rv = apr_socket_recvfrom(&peer, p, 0, buf, len);
+ }
+ }
if (block == APR_NONBLOCK_READ) {
apr_socket_timeout_set(p, timeout);
Index: include/ap_listen.h
===================================================================
--- include/ap_listen.h (revision 565699)
+++ include/ap_listen.h (working copy)
@@ -81,6 +81,26 @@
AP_DECLARE(void) ap_listen_pre_config(void);
/**
+ * Allocate a new listener to be created during ap_setup_listeners.
+ * @param process The process_rec of the parent process
+ * @param addr The IP address to bind the socket to (uses same format as
+ * the Listen directive in httpd.conf)
+ * @param port The port to bind the socket to
+ * @param type The socket type (SOCK_STREAM, etc)
+ * @param protocol The socket protocol (APR_PROTO_TCP, etc)
+ * @param proto The optional protocol argument is not required for most
+ * configurations. If not specified, https is the default for port 443
+ * and http the default for all other ports. The protocol is used to
+ * determine which module should handle a request, and to apply
+ * protocol specific optimizations with the AcceptFilter directive.
+ * @return NULL on success, or an error message on failure
+ */
+
+AP_DECLARE(const char *) ap_alloc_listener(process_rec *process, char *addr,
+ apr_port_t port, int type, int protocol,
+ const char* proto);
+
+/**
* Loop through the global ap_listen_rec list and create all of the required
* sockets. This executes the listen and bind on the sockets.
* @param s The global server_rec
@@ -103,6 +123,9 @@
AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd, void
*dummy, const char *arg);
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
int argc, char *const argv[]);
+AP_DECLARE_NONSTD(const char *) ap_set_udp_listener(cmd_parms *cmd,
+ void *dummy, int argc,
+ char *const argv[]);
AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd, void
*dummy,
const char *arg);
AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
@@ -114,6 +137,10 @@
"Maximum length of the queue of pending connections, as used by listen(2)"),
\
AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
"A port number or a numeric IP address and a port number, and an optional
protocol"), \
+AP_INIT_TAKE_ARGV("ListenTCP", ap_set_listener, NULL, RSRC_CONF, \
+ "A port number or a numeric IP address and a port number, and an optional
protocol"), \
+AP_INIT_TAKE_ARGV("ListenUDP", ap_set_udp_listener, NULL, RSRC_CONF, \
+ "A port number or a numeric IP address and a port number, and an optional
protocol"), \
AP_INIT_TAKE1("SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, \
"Send buffer size in bytes"), \
AP_INIT_TAKE1("ReceiveBufferSize", ap_set_receive_buffer_size, NULL, \
Index: include/httpd.h
===================================================================
--- include/httpd.h (revision 565699)
+++ include/httpd.h (working copy)
@@ -1224,6 +1224,7 @@
apr_bucket_brigade *buffered_bb;
apr_size_t bytes_in;
apr_size_t bytes_written;
+ int connected;
} core_output_filter_ctx_t;
typedef struct core_filter_ctx {
Index: server/core.c
===================================================================
--- server/core.c (revision 565699)
+++ server/core.c (working copy)
@@ -3778,6 +3778,7 @@
apr_bucket_alloc_t *alloc)
{
apr_status_t rv;
+ int sd_type;
conn_rec *c = (conn_rec *) apr_pcalloc(ptrans, sizeof(conn_rec));
c->sbh = sbh;
@@ -3797,16 +3798,38 @@
apr_socket_close(csd);
return NULL;
}
-
apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
- if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
- != APR_SUCCESS) {
+
+ apr_socket_type_get(csd, &sd_type);
+ if (sd_type == SOCK_STREAM &&
+ (rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+ != APR_SUCCESS)
+ {
ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
"apr_socket_addr_get(APR_REMOTE)");
apr_socket_close(csd);
return NULL;
}
-
+ if (sd_type != SOCK_STREAM) {
+ if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+ != APR_SUCCESS)
+ {
+ char tmpbuf = 0;
+ apr_size_t tmpbuflen = 1;
+ /** Allocate a apr_sockaddr_t, since we can't use the one in csd */
+ c->remote_addr = apr_pcalloc(c->pool, sizeof(apr_sockaddr_t));
+ c->remote_addr->pool = c->pool;
+ if ((rv = apr_socket_recvfrom(c->remote_addr, csd, MSG_PEEK,
+ &tmpbuf, &tmpbuflen))
+ != APR_SUCCESS)
+ {
+ ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+ "Error reading UDP peer");
+ /* Don't close non-connected UDP sockets */
+ return NULL;
+ }
+ }
+ }
apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
c->base_server = server;
@@ -3828,6 +3851,7 @@
static int core_pre_connection(conn_rec *c, void *csd)
{
core_net_rec *net = apr_palloc(c->pool, sizeof(*net));
+ int sd_type;
apr_status_t rv;
#ifdef AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
@@ -3838,13 +3862,17 @@
* performance penalties. (Failing to disable Nagle is not much of a
* problem with simple HTTP.)
*/
- rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1);
- if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
- /* expected cause is that the client disconnected already,
- * hence the debug level
- */
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
- "apr_socket_opt_set(APR_TCP_NODELAY)");
+
+ apr_socket_type_get(csd, &sd_type);
+ if (sd_type == SOCK_STREAM) {
+ rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1);
+ if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
+ /* expected cause is that the client disconnected already,
+ * hence the debug level
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
+ "apr_socket_opt_set(APR_TCP_NODELAY)");
+ }
}
#endif
Index: server/core_filters.c
===================================================================
--- server/core_filters.c (revision 565699)
+++ server/core_filters.c (working copy)
@@ -311,6 +311,7 @@
int make_a_copy, conn_rec *c);
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+ int connected,
apr_bucket_brigade *bb,
apr_size_t *bytes_written,
conn_rec *c);
@@ -318,11 +319,13 @@
static void remove_empty_buckets(apr_bucket_brigade *bb);
static apr_status_t send_brigade_blocking(apr_socket_t *s,
+ int connected,
apr_bucket_brigade *bb,
apr_size_t *bytes_written,
conn_rec *c);
static apr_status_t writev_nonblocking(apr_socket_t *s,
+ int connected,
struct iovec *vec, apr_size_t nvec,
apr_bucket_brigade *bb,
apr_size_t *cumulative_bytes_written,
@@ -360,12 +363,23 @@
if (ctx == NULL) {
apr_status_t rv;
+ int sd_type;
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
+ ctx->connected = 1;
net->out_ctx = (core_output_filter_ctx_t *)ctx;
rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1);
if (rv != APR_SUCCESS) {
return rv;
}
+ /* Check for non-connected socket */
+ apr_socket_type_get(net->client_socket, &sd_type);
+ if (sd_type != SOCK_STREAM) {
+ apr_sockaddr_t *peer;
+ if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE,
+ net->client_socket)) != APR_SUCCESS)
+ /* Disconnected socket */
+ ctx->connected = 0;
+ }
}
if (new_bb != NULL) {
@@ -419,7 +433,8 @@
*/
if (new_bb == NULL) {
- apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
+ apr_status_t rv = send_brigade_nonblocking(net->client_socket,
+ ctx->connected, bb,
&(ctx->bytes_written), c);
if (APR_STATUS_IS_EAGAIN(rv)) {
rv = APR_SUCCESS;
@@ -439,7 +454,8 @@
next = APR_BUCKET_NEXT(bucket);
if (APR_BUCKET_IS_FLUSH(bucket)) {
apr_bucket_brigade *remainder = apr_brigade_split(bb, next);
- apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
+ apr_status_t rv = send_brigade_blocking(net->client_socket,
+ ctx->connected, bb,
&(ctx->bytes_written), c);
if (rv != APR_SUCCESS) {
/* The client has aborted the connection */
@@ -475,7 +491,8 @@
/* ### Writing the entire brigade may be excessive; we really just
* ### need to send enough data to be under THRESHOLD_MAX_BUFFER.
*/
- apr_status_t rv = send_brigade_blocking(net->client_socket, bb,
+ apr_status_t rv = send_brigade_blocking(net->client_socket,
+ ctx->connected, bb,
&(ctx->bytes_written), c);
if (rv != APR_SUCCESS) {
/* The client has aborted the connection */
@@ -484,7 +501,8 @@
}
}
else if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
- apr_status_t rv = send_brigade_nonblocking(net->client_socket, bb,
+ apr_status_t rv = send_brigade_nonblocking(net->client_socket,
+ ctx->connected, bb,
&(ctx->bytes_written), c);
if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
/* The client has aborted the connection */
@@ -535,12 +553,14 @@
#endif
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+ int connected,
apr_bucket_brigade *bb,
apr_size_t *bytes_written,
conn_rec *c)
{
apr_bucket *bucket, *next;
apr_status_t rv;
+ int type;
struct iovec vec[MAX_IOVEC_TO_WRITE];
apr_size_t nvec = 0;
@@ -552,7 +572,12 @@
int did_sendfile = 0;
next = APR_BUCKET_NEXT(bucket);
#if APR_HAS_SENDFILE
- if (APR_BUCKET_IS_FILE(bucket)) {
+ /** Don't even bother with sendfile if we're not dealing with a
+ * SOCK_STREAM socket
+ */
+ if (APR_BUCKET_IS_FILE(bucket) &&
+ ((apr_socket_type_get(s, &type) == APR_SUCCESS) &&
+ (type == SOCK_STREAM))) {
apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
apr_file_t *fd = file_bucket->fd;
/* Use sendfile to send this file unless:
@@ -566,7 +591,8 @@
did_sendfile = 1;
if (nvec > 0) {
(void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written,
c);
+ rv = writev_nonblocking(s, connected, vec, nvec, bb,
+ bytes_written, c);
nvec = 0;
if (rv != APR_SUCCESS) {
(void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
@@ -597,7 +623,9 @@
vec[nvec].iov_len = length;
nvec++;
if (nvec == MAX_IOVEC_TO_WRITE) {
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+
+ rv = writev_nonblocking(s, connected, vec, nvec, bb,
+ bytes_written, c);
nvec = 0;
if (rv != APR_SUCCESS) {
return rv;
@@ -608,7 +636,7 @@
}
if (nvec > 0) {
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+ rv = writev_nonblocking(s, connected, vec, nvec, bb, bytes_written, c);
if (rv != APR_SUCCESS) {
return rv;
}
@@ -630,6 +658,7 @@
}
static apr_status_t send_brigade_blocking(apr_socket_t *s,
+ int connected,
apr_bucket_brigade *bb,
apr_size_t *bytes_written,
conn_rec *c)
@@ -638,7 +667,7 @@
rv = APR_SUCCESS;
while (!APR_BRIGADE_EMPTY(bb)) {
- rv = send_brigade_nonblocking(s, bb, bytes_written, c);
+ rv = send_brigade_nonblocking(s, connected, bb, bytes_written, c);
if (rv != APR_SUCCESS) {
if (APR_STATUS_IS_EAGAIN(rv)) {
/* Wait until we can send more data */
@@ -665,6 +694,7 @@
}
static apr_status_t writev_nonblocking(apr_socket_t *s,
+ int connected,
struct iovec *vec, apr_size_t nvec,
apr_bucket_brigade *bb,
apr_size_t *cumulative_bytes_written,
@@ -690,7 +720,12 @@
offset = 0;
while (bytes_written < bytes_to_write) {
apr_size_t n = 0;
- rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
+ if (connected) {
+ rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
+ } else {
+ rv = apr_socket_sendtov(s, c->remote_addr, 0, vec + offset,
+ nvec - offset, &n);
+ }
if (n > 0) {
bytes_written += n;
for (i = offset; i < nvec; ) {
Index: server/listen.c
===================================================================
--- server/listen.c (revision 565699)
+++ server/listen.c (working copy)
@@ -49,6 +49,7 @@
int v6only_setting = 1;
#endif
#endif
+ int type;
apr_status_t stat;
#ifndef WIN32
@@ -136,8 +137,17 @@
apr_socket_close(s);
return stat;
}
-
- if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
+
+ if ((stat = apr_socket_type_get(s, &type)) != APR_SUCCESS) {
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
+ "make_sock: could not determine socket type for "
+ "address %pI", server->bind_addr);
+ apr_socket_close(s);
+ return stat;
+ }
+
+ if (type == SOCK_STREAM &&
+ ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS)) {
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p,
"make_sock: unable to listen for connections "
"on address %pI",
@@ -204,7 +214,8 @@
const char *accf;
apr_status_t rv;
const char *proto;
-
+ int protocol;
+
proto = lis->protocol;
if (!proto) {
@@ -225,10 +236,13 @@
}
#else
#ifdef APR_TCP_DEFER_ACCEPT
- rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
- if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
- "Failed to enable APR_TCP_DEFER_ACCEPT");
+ rv = apr_socket_protocol_get(s, &protocol);
+ if ((rv == APR_SUCCESS) && (protocol == APR_PROTO_TCP)) {
+ rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+ "Failed to enable APR_TCP_DEFER_ACCEPT");
+ }
}
#endif
#endif
@@ -241,12 +255,14 @@
return APR_SUCCESS;
}
-static const char *alloc_listener(process_rec *process, char *addr,
- apr_port_t port, const char* proto)
+AP_DECLARE(const char *) ap_alloc_listener(process_rec *process, char *addr,
+ apr_port_t port, int type, int protocol,
+ const char* proto)
{
ap_listen_rec **walk, *last;
apr_status_t status;
apr_sockaddr_t *sa;
+ apr_socket_t *sd;
int found_listener = 0;
/* see if we've got an old listener for this address:port */
@@ -255,13 +271,20 @@
/* Some listeners are not real so they will not have a bind_addr. */
if (sa) {
ap_listen_rec *new;
+ sd = (*walk)->sd;
apr_port_t oldport;
+ int oldprotocol = -1;
+ int oldtype = -1;
oldport = sa->port;
- /* If both ports are equivalent, then if their names are
equivalent,
+ apr_socket_type_get(sd, &oldtype);
+ apr_socket_protocol_get(sd, &oldprotocol);
+ /* If both ports are equivalent and both socket type/protocols are
+ * equivalent, then if their names are equivalent,
* then we will re-use the existing record.
*/
- if (port == oldport &&
+ if ((port == oldport &&
+ (type == oldtype && protocol == oldprotocol)) &&
((!addr && !sa->hostname) ||
((addr && sa->hostname) && !strcmp(sa->hostname, addr)))) {
new = *walk;
@@ -284,7 +307,7 @@
process->pool))
!= APR_SUCCESS) {
ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
- "alloc_listener: failed to set up sockaddr for %s",
+ "ap_alloc_listener: failed to set up sockaddr for %s",
addr);
return "Listen setup failed";
}
@@ -309,7 +332,7 @@
sa = sa->next;
status = apr_socket_create(&new->sd, new->bind_addr->family,
- SOCK_STREAM, 0, process->pool);
+ type, protocol, process->pool);
#if APR_HAVE_IPV6
/* What could happen is that we got an IPv6 address, but this system
@@ -322,7 +345,7 @@
#endif
if (status != APR_SUCCESS) {
ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
- "alloc_listener: failed to get a socket for %s",
+ "ap_alloc_listener: failed to get a socket for %s",
addr);
return "Listen setup failed";
}
@@ -583,7 +606,6 @@
ap_listenbacklog = DEFAULT_LISTENBACKLOG;
}
-
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
int argc, char *const argv[])
{
@@ -626,9 +648,57 @@
ap_str_tolower(proto);
}
- return alloc_listener(cmd->server->process, host, port, proto);
+ return ap_alloc_listener(cmd->server->process, host, port,
+ SOCK_STREAM, 0, proto);
}
+AP_DECLARE_NONSTD(const char *) ap_set_udp_listener(cmd_parms *cmd,
+ void *dummy, int argc,
+ char *const argv[])
+{
+ char *host, *scope_id, *proto;
+ apr_port_t port;
+ apr_status_t rv;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ if (argc < 1 || argc > 2) {
+ return "Listen requires 1 or 2 arguments.";
+ }
+
+ rv = apr_parse_addr_port(&host, &scope_id, &port, argv[0], cmd->pool);
+ if (rv != APR_SUCCESS) {
+ return "Invalid address or port";
+ }
+
+ if (host && !strcmp(host, "*")) {
+ host = NULL;
+ }
+
+ if (scope_id) {
+ /* XXX scope id support is useful with link-local IPv6 addresses */
+ return "Scope id is not supported";
+ }
+
+ if (!port) {
+ return "Port must be specified";
+ }
+
+ if (argc != 2) {
+ proto = "http";
+ }
+ else {
+ proto = apr_pstrdup(cmd->pool, argv[1]);
+ ap_str_tolower(proto);
+ }
+
+ return ap_alloc_listener(cmd->server->process, host, port,
+ SOCK_DGRAM, APR_PROTO_UDP, proto);
+}
+
AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,
void *dummy,
const char *arg)
Index: server/mpm/experimental/event/event.c
===================================================================
--- server/mpm/experimental/event/event.c (revision 565699)
+++ server/mpm/experimental/event/event.c (working copy)
@@ -1019,7 +1019,9 @@
apr_pool_tag(ptrans, "transaction");
- rc = lr->accept_func(&csd, lr, ptrans);
+ rc = APR_SUCCESS;
+ if (lr->accept_func)
+ rc = lr->accept_func(&csd, lr, ptrans);
/* later we trash rv and rely on csd to indicate
* success/failure
Index: server/mpm/experimental/leader/leader.c
===================================================================
--- server/mpm/experimental/leader/leader.c (revision 565699)
+++ server/mpm/experimental/leader/leader.c (working copy)
@@ -786,7 +786,9 @@
}
got_fd:
if (!workers_may_exit) {
- rv = lr->accept_func(&csd, lr, ptrans);
+ rv = APR_SUCCESS;
+ if (lr->accept_func)
+ rv = lr->accept_func(&csd, lr, ptrans);
/* later we trash rv and rely on csd to indicate success/failure */
AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
@@ -1996,3 +1998,4 @@
leader_hooks /* register_hooks */
};
+
Index: server/mpm/experimental/perchild/perchild.c
===================================================================
--- server/mpm/experimental/perchild/perchild.c (revision 565699)
+++ server/mpm/experimental/perchild/perchild.c (working copy)
@@ -736,7 +736,9 @@
}
got_fd:
if (!workers_may_exit) {
- rv = lr->accept_func(&csd, lr, ptrans);
+ rv = APR_SUCCESS;
+ if (lr->accept_func)
+ rv = lr->accept_func(&csd, lr, ptrans);
if (rv == APR_EGENERAL) {
/* E[NM]FILE, ENOMEM, etc */
workers_may_exit = 1;
@@ -2049,3 +2051,4 @@
perchild_hooks /* register_hooks */
};
+
Index: server/mpm/experimental/threadpool/threadpool.c
===================================================================
--- server/mpm/experimental/threadpool/threadpool.c (revision 565699)
+++ server/mpm/experimental/threadpool/threadpool.c (working copy)
@@ -855,7 +855,9 @@
}
}
if (!listener_may_exit) {
- rv = lr->accept_func(&csd, lr, ptrans);
+ rv = APR_SUCCESS;
+ if (lr->accept_func)
+ rv = lr->accept_func(&csd, lr, ptrans);
/* later we trash rv and rely on csd to indicate success/failure */
AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
@@ -2250,3 +2252,4 @@
threadpool_hooks /* register_hooks */
};
+
Index: server/mpm/prefork/prefork.c
===================================================================
--- server/mpm/prefork/prefork.c (revision 565699)
+++ server/mpm/prefork/prefork.c (working copy)
@@ -450,6 +450,7 @@
ap_sb_handle_t *sbh;
apr_bucket_alloc_t *bucket_alloc;
int last_poll_idx = 0;
+ int sd_type;
mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
* child initializes
@@ -491,24 +492,6 @@
ap_create_sb_handle(&sbh, pchild, my_child_num, 0);
- (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
-
- /* Set up the pollfd array */
- /* ### check the status */
- (void) apr_pollset_create(&pollset, num_listensocks, pchild, 0);
-
- for (lr = ap_listeners, i = num_listensocks; i--; lr = lr->next) {
- apr_pollfd_t pfd = { 0 };
-
- pfd.desc_type = APR_POLL_SOCKET;
- pfd.desc.s = lr->sd;
- pfd.reqevents = APR_POLLIN;
- pfd.client_data = lr;
-
- /* ### check the status */
- (void) apr_pollset_add(pollset, &pfd);
- }
-
mpm_state = AP_MPMQ_RUNNING;
bucket_alloc = apr_bucket_alloc_create(pchild);
@@ -540,6 +523,34 @@
/* Lock around "accept", if necessary */
SAFE_ACCEPT(accept_mutex_on());
+ /* Set up the pollfd array - we waste cycles on doing it inside the
+ * loop, but we've got to do it this way to lock the non-accept()
+ * ports */
+ /* ### check the status */
+ (void) apr_pollset_create(&pollset, num_listensocks, pchild, 0);
+
+ for (lr = ap_listeners, i = num_listensocks; i--; lr = lr->next) {
+ apr_pollfd_t pfd = { 0 };
+ /** FIXME: We currently use this as a hack to see if we're
actively
+ * operating on a UDP socket, since there's no accept()
+ *
+ * There's a really nasty gotcha here: since we only unlock the
UDP
+ * port at the end of the operation, but by that point another
child
+ * will already be polling the sockets, we won't start polling the
+ * UDP port until after the next connection is received (and the
next
+ * child starts polling) */
+ if (lr->active != 1)
+ continue;
+
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.desc.s = lr->sd;
+ pfd.reqevents = APR_POLLIN;
+ pfd.client_data = lr;
+
+ /* ### check the status */
+ (void) apr_pollset_add(pollset, &pfd);
+ }
+
if (num_listensocks == 1) {
/* There is only one listener record, so refer to that one. */
lr = ap_listeners;
@@ -598,8 +609,24 @@
/* if we accept() something we don't want to die, so we have to
* defer the exit
*/
- status = lr->accept_func(&csd, lr, ptrans);
+ status = APR_SUCCESS;
+
+ /** Check for non-stream socket + lock */
+ apr_socket_type_get(lr->sd, &sd_type);
+
+ if (sd_type == SOCK_STREAM) {
+ status = lr->accept_func(&csd, lr, ptrans);
+ } else {
+ /** TODO: Tie this worker to this socket permanently - or come up
+ * with a better way to ensure that a subsequent request can be
+ * polled */
+ csd = lr->sd;
+ lr->active = 2;
+ }
+
+ /** Release the pollset */
+ apr_pollset_destroy(pollset);
SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */
if (status == APR_EGENERAL) {
@@ -618,7 +645,9 @@
current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd,
my_child_num, sbh, bucket_alloc);
if (current_conn) {
ap_process_connection(current_conn, csd);
- ap_lingering_close(current_conn);
+ if (sd_type == SOCK_STREAM) {
+ ap_lingering_close(current_conn);
+ }
}
/* Check the pod and the generation number after processing a
@@ -636,6 +665,9 @@
*/
die_now = 1;
}
+
+ /** Unlock socket, if needed. No need to lock this */
+ lr->active = 1;
}
clean_child_exit(0);
}
Index: server/mpm/worker/worker.c
===================================================================
--- server/mpm/worker/worker.c (revision 565699)
+++ server/mpm/worker/worker.c (working copy)
@@ -737,7 +737,9 @@
apr_allocator_owner_set(allocator, ptrans);
}
apr_pool_tag(ptrans, "transaction");
- rv = lr->accept_func(&csd, lr, ptrans);
+ rv = APR_SUCCESS;
+ if (lr->accept_func)
+ rv = lr->accept_func(&csd, lr, ptrans);
/* later we trash rv and rely on csd to indicate success/failure */
AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
@@ -2340,3 +2342,4 @@
worker_hooks /* register_hooks */
};
+
Index: server/mpm_common.c
===================================================================
--- server/mpm_common.c (revision 565699)
+++ server/mpm_common.c (working copy)
@@ -480,6 +480,13 @@
*
* In spite of these problems, failure here is not a shooting offense.
*/
+
+ int protocol;
+ if (!((apr_socket_protocol_get(s, &protocol) == APR_SUCCESS) &&
+ protocol == APR_PROTO_TCP))
+ /** Don't do anything unless we're a TCP socket */
+ return;
+
apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
if (status != APR_SUCCESS) {
@@ -643,6 +650,13 @@
apr_socket_t *sock;
apr_pool_t *p;
apr_size_t len;
+ int protocol;
+ int type;
+
+ if (apr_socket_type_get(ap_listeners->sd, &type) != APR_SUCCESS)
+ type = SOCK_STREAM;
+ if (apr_socket_protocol_get(ap_listeners->sd, &protocol) != APR_SUCCESS)
+ type = 0;
/* create a temporary pool for the socket. pconf stays around too long */
rv = apr_pool_create(&p, pod->p);
@@ -651,7 +665,7 @@
}
rv = apr_socket_create(&sock, ap_listeners->bind_addr->family,
- SOCK_STREAM, 0, p);
+ type, protocol, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
"get socket to connect to listener");
Index: include/apr_network_io.h
===================================================================
--- include/apr_network_io.h (revision 572605)
+++ include/apr_network_io.h (working copy)
@@ -508,6 +508,33 @@
apr_size_t *len);
/**
+ * Send multiple packets of data over a network.
+ * @param sock The socket to send the data over.
+ * @param where The apr_sockaddr_t describing where to send the data
+ * @param flags The flags to use
+ * @param vec The array of iovec structs containing the data to send
+ * @param nvec The number of iovec structs in the array
+ * @param len Receives the number of bytes actually written
+ * @remark
+ * <PRE>
+ * This functions acts like a blocking write by default. To change
+ * this behavior, use apr_socket_timeout_set() or the APR_SO_NONBLOCK
+ * socket option.
+ * The number of bytes actually sent is stored in argument 3.
+ *
+ * It is possible for both bytes to be sent and an error to be returned.
+ *
+ * APR_EINTR is never returned.
+ * </PRE>
+ */
+APR_DECLARE(apr_status_t) apr_socket_sendtov(apr_socket_t *sock,
+ apr_sockaddr_t *where,
+ apr_int32_t flags,
+ const struct iovec *vec,
+ apr_int32_t nvec,
+ apr_size_t *len);
+
+/**
* Read data from a socket. On success, the address of the peer from
* which the data was sent is copied into the @param from parameter,
* and the @param len parameter is updated to give the number of bytes
Index: network_io/unix/sendrecv.c
===================================================================
--- network_io/unix/sendrecv.c (revision 572605)
+++ network_io/unix/sendrecv.c (working copy)
@@ -184,7 +184,15 @@
return APR_SUCCESS;
}
-apr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec,
+apr_status_t apr_socket_sendtov(apr_socket_t *sock, apr_sockaddr_t *where,
+ apr_int32_t flags, const struct iovec *vec,
+ apr_int32_t nvec, apr_size_t *len)
+{
+ *len = vec[0].iov_len;
+ return apr_socket_sendto(sock, where, flags, vec[0].iov_base, len);
+}
+
+apr_status_t apr_socket_sendv(apr_socket_t *sock, const struct iovec *vec,
apr_int32_t nvec, apr_size_t *len)
{
#ifdef HAVE_WRITEV