Return-Path: Delivered-To: apmail-httpd-dev-archive@www.apache.org Received: (qmail 67760 invoked from network); 5 Sep 2007 10:19:55 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 5 Sep 2007 10:19:55 -0000 Received: (qmail 42190 invoked by uid 500); 5 Sep 2007 10:19:45 -0000 Delivered-To: apmail-httpd-dev-archive@httpd.apache.org Received: (qmail 42124 invoked by uid 500); 5 Sep 2007 10:19:45 -0000 Mailing-List: contact dev-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list dev@httpd.apache.org Received: (qmail 42101 invoked by uid 99); 5 Sep 2007 10:19:44 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Sep 2007 03:19:44 -0700 X-ASF-Spam-Status: No, hits=1.2 required=10.0 tests=SPF_NEUTRAL X-Spam-Check-By: apache.org Received-SPF: neutral (nike.apache.org: local policy) Received: from [199.203.54.245] (HELO vl654.host245.netvision.net.il) (199.203.54.245) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 05 Sep 2007 10:20:56 +0000 Received: from [10.0.0.11] (bzq-88-154-64-58.red.bezeqint.net [88.154.64.58]) (authenticated bits=0) by mail1.mirimar.net (8.13.4/8.13.4/Debian-3sarge3) with ESMTP id l85AJ9GB031753 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 5 Sep 2007 13:19:10 +0300 Message-ID: <46DE8285.2010707@beamartyr.net> Date: Wed, 05 Sep 2007 13:18:45 +0300 From: Issac Goldstand Organization: Mirimar Networks User-Agent: Thunderbird 2.0.0.6 (Windows/20070728) MIME-Version: 1.0 To: dev@httpd.apache.org CC: dev@apr.apache.org Subject: [patch] experimental UDP support in httpd-prefork/apr/apr-util trunk X-Enigmail-Version: 0.95.3 OpenPGP: url=http://www.beamartyr.net/pubkey.asc Content-Type: multipart/mixed; boundary="------------070902020606070501010508" X-Virus-Scanned: ClamAV 0.91.2/4160/Wed Sep 5 12:22:20 2007 on hector.mirimar.net X-Virus-Status: Clean X-Virus-Checked: Checked by ClamAV on apache.org This is a multi-part message in MIME format. --------------070902020606070501010508 Content-Type: text/plain; charset=ISO-8859-8-I Content-Transfer-Encoding: 7bit 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 --------------070902020606070501010508 Content-Type: text/plain; name="aprutil-socket_bucket_udp.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="aprutil-socket_bucket_udp.patch" 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); --------------070902020606070501010508 Content-Type: text/plain; name="httpd-udp-disconnected.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="httpd-udp-disconnected.patch" 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"); --------------070902020606070501010508 Content-Type: text/plain; name="apr-sock-sendtov.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="apr-sock-sendtov.patch" 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 + *
+ * 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.
+ * 
+ */ +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 --------------070902020606070501010508--