ext/libuv/src/unix/tcp.c in libuv-0.10.0 vs ext/libuv/src/unix/tcp.c in libuv-0.10.2

- old
+ new

@@ -1,329 +1,329 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "internal.h" - -#include <stdlib.h> -#include <unistd.h> -#include <assert.h> -#include <errno.h> - - -int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { - uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); - return 0; -} - - -static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { - int sockfd; - int err; - - if (uv__stream_fd(handle) != -1) - return 0; - - err = uv__socket(domain, SOCK_STREAM, 0); - if (err < 0) - return err; - sockfd = err; - - err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); - if (err) { - close(sockfd); - return err; - } - - return 0; -} - - -static int uv__bind(uv_tcp_t* tcp, - int domain, - struct sockaddr* addr, - int addrsize) { - int err; - int on; - - err = maybe_new_socket(tcp, domain, UV_STREAM_READABLE | UV_STREAM_WRITABLE); - if (err) - return err; - - on = 1; - if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) - return -errno; - - errno = 0; - if (bind(tcp->io_watcher.fd, addr, addrsize) && errno != EADDRINUSE) - return -errno; - - tcp->delayed_error = -errno; - return 0; -} - - -static int uv__connect(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr* addr, - socklen_t addrlen, - uv_connect_cb cb) { - int err; - int r; - - assert(handle->type == UV_TCP); - - if (handle->connect_req != NULL) - return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */ - - err = maybe_new_socket(handle, - addr->sa_family, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); - if (err) - return err; - - handle->delayed_error = 0; - - do - r = connect(uv__stream_fd(handle), addr, addrlen); - while (r == -1 && errno == EINTR); - - if (r == -1) { - if (errno == EINPROGRESS) - ; /* not an error */ - else if (errno == ECONNREFUSED) - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. - */ - handle->delayed_error = -errno; - else - return -errno; - } - - uv__req_init(handle->loop, req, UV_CONNECT); - req->cb = cb; - req->handle = (uv_stream_t*) handle; - QUEUE_INIT(&req->queue); - handle->connect_req = req; - - uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); - - if (handle->delayed_error) - uv__io_feed(handle->loop, &handle->io_watcher); - - return 0; -} - - -int uv__tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { - return uv__bind(handle, - AF_INET, - (struct sockaddr*)&addr, - sizeof(struct sockaddr_in)); -} - - -int uv__tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { - return uv__bind(handle, - AF_INET6, - (struct sockaddr*)&addr, - sizeof(struct sockaddr_in6)); -} - - -int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { - return uv__stream_open((uv_stream_t*)handle, - sock, - UV_STREAM_READABLE | UV_STREAM_WRITABLE); -} - - -int uv_tcp_getsockname(uv_tcp_t* handle, - struct sockaddr* name, - int* namelen) { - socklen_t socklen; - - if (handle->delayed_error) - return handle->delayed_error; - - if (uv__stream_fd(handle) < 0) - return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getsockname(uv__stream_fd(handle), name, &socklen)) - return -errno; - - *namelen = (int) socklen; - return 0; -} - - -int uv_tcp_getpeername(uv_tcp_t* handle, - struct sockaddr* name, - int* namelen) { - socklen_t socklen; - - if (handle->delayed_error) - return handle->delayed_error; - - if (uv__stream_fd(handle) < 0) - return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getpeername(uv__stream_fd(handle), name, &socklen)) - return -errno; - - *namelen = (int) socklen; - return 0; -} - - -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - static int single_accept = -1; - int err; - - if (tcp->delayed_error) - return tcp->delayed_error; - - if (single_accept == -1) { - const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); - single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ - } - - if (single_accept) - tcp->flags |= UV_TCP_SINGLE_ACCEPT; - - err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE); - if (err) - return err; - - if (listen(tcp->io_watcher.fd, backlog)) - return -errno; - - tcp->connection_cb = cb; - - /* Start listening for connections. */ - tcp->io_watcher.cb = uv__server_io; - uv__io_start(tcp->loop, &tcp->io_watcher, UV__POLLIN); - - return 0; -} - - -int uv__tcp_connect(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in addr, - uv_connect_cb cb) { - return uv__connect(req, handle, (struct sockaddr*) &addr, sizeof addr, cb); -} - - -int uv__tcp_connect6(uv_connect_t* req, - uv_tcp_t* handle, - struct sockaddr_in6 addr, - uv_connect_cb cb) { - return uv__connect(req, handle, (struct sockaddr*) &addr, sizeof addr, cb); -} - - -int uv__tcp_nodelay(int fd, int on) { - return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); -} - - -int uv__tcp_keepalive(int fd, int on, unsigned int delay) { - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) - return -errno; - -#ifdef TCP_KEEPIDLE - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) - return -errno; -#endif - - /* Solaris/SmartOS, if you don't support keep-alive, - * then don't advertise it in your system headers... - */ - /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ -#if defined(TCP_KEEPALIVE) && !defined(__sun) - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) - return -errno; -#endif - - return 0; -} - - -int uv_tcp_nodelay(uv_tcp_t* handle, int on) { - int err; - - if (uv__stream_fd(handle) != -1) { - err = uv__tcp_nodelay(uv__stream_fd(handle), on); - if (err) - return err; - } - - if (on) - handle->flags |= UV_TCP_NODELAY; - else - handle->flags &= ~UV_TCP_NODELAY; - - return 0; -} - - -int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { - int err; - - if (uv__stream_fd(handle) != -1) { - err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay); - if (err) - return err; - } - - if (on) - handle->flags |= UV_TCP_KEEPALIVE; - else - handle->flags &= ~UV_TCP_KEEPALIVE; - - /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge - * uv_tcp_t with an int that's almost never used... - */ - - return 0; -} - - -int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { - if (enable) - handle->flags &= ~UV_TCP_SINGLE_ACCEPT; - else - handle->flags |= UV_TCP_SINGLE_ACCEPT; - return 0; -} - - -void uv__tcp_close(uv_tcp_t* handle) { - uv__stream_close((uv_stream_t*)handle); -} +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { + uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); + return 0; +} + + +static int maybe_new_socket(uv_tcp_t* handle, int domain, int flags) { + int sockfd; + int err; + + if (uv__stream_fd(handle) != -1) + return 0; + + err = uv__socket(domain, SOCK_STREAM, 0); + if (err < 0) + return err; + sockfd = err; + + err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); + if (err) { + close(sockfd); + return err; + } + + return 0; +} + + +static int uv__bind(uv_tcp_t* tcp, + int domain, + struct sockaddr* addr, + int addrsize) { + int err; + int on; + + err = maybe_new_socket(tcp, domain, UV_STREAM_READABLE | UV_STREAM_WRITABLE); + if (err) + return err; + + on = 1; + if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + return -errno; + + errno = 0; + if (bind(tcp->io_watcher.fd, addr, addrsize) && errno != EADDRINUSE) + return -errno; + + tcp->delayed_error = -errno; + return 0; +} + + +static int uv__connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr* addr, + socklen_t addrlen, + uv_connect_cb cb) { + int err; + int r; + + assert(handle->type == UV_TCP); + + if (handle->connect_req != NULL) + return -EALREADY; /* FIXME(bnoordhuis) -EINVAL or maybe -EBUSY. */ + + err = maybe_new_socket(handle, + addr->sa_family, + UV_STREAM_READABLE | UV_STREAM_WRITABLE); + if (err) + return err; + + handle->delayed_error = 0; + + do + r = connect(uv__stream_fd(handle), addr, addrlen); + while (r == -1 && errno == EINTR); + + if (r == -1) { + if (errno == EINPROGRESS) + ; /* not an error */ + else if (errno == ECONNREFUSED) + /* If we get a ECONNREFUSED wait until the next tick to report the + * error. Solaris wants to report immediately--other unixes want to + * wait. + */ + handle->delayed_error = -errno; + else + return -errno; + } + + uv__req_init(handle->loop, req, UV_CONNECT); + req->cb = cb; + req->handle = (uv_stream_t*) handle; + QUEUE_INIT(&req->queue); + handle->connect_req = req; + + uv__io_start(handle->loop, &handle->io_watcher, UV__POLLOUT); + + if (handle->delayed_error) + uv__io_feed(handle->loop, &handle->io_watcher); + + return 0; +} + + +int uv__tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr) { + return uv__bind(handle, + AF_INET, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); +} + + +int uv__tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) { + return uv__bind(handle, + AF_INET6, + (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); +} + + +int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { + return uv__stream_open((uv_stream_t*)handle, + sock, + UV_STREAM_READABLE | UV_STREAM_WRITABLE); +} + + +int uv_tcp_getsockname(uv_tcp_t* handle, + struct sockaddr* name, + int* namelen) { + socklen_t socklen; + + if (handle->delayed_error) + return handle->delayed_error; + + if (uv__stream_fd(handle) < 0) + return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t) *namelen; + + if (getsockname(uv__stream_fd(handle), name, &socklen)) + return -errno; + + *namelen = (int) socklen; + return 0; +} + + +int uv_tcp_getpeername(uv_tcp_t* handle, + struct sockaddr* name, + int* namelen) { + socklen_t socklen; + + if (handle->delayed_error) + return handle->delayed_error; + + if (uv__stream_fd(handle) < 0) + return -EINVAL; /* FIXME(bnoordhuis) -EBADF */ + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t) *namelen; + + if (getpeername(uv__stream_fd(handle), name, &socklen)) + return -errno; + + *namelen = (int) socklen; + return 0; +} + + +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { + static int single_accept = -1; + int err; + + if (tcp->delayed_error) + return tcp->delayed_error; + + if (single_accept == -1) { + const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); + single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ + } + + if (single_accept) + tcp->flags |= UV_TCP_SINGLE_ACCEPT; + + err = maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE); + if (err) + return err; + + if (listen(tcp->io_watcher.fd, backlog)) + return -errno; + + tcp->connection_cb = cb; + + /* Start listening for connections. */ + tcp->io_watcher.cb = uv__server_io; + uv__io_start(tcp->loop, &tcp->io_watcher, UV__POLLIN); + + return 0; +} + + +int uv__tcp_connect(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in addr, + uv_connect_cb cb) { + return uv__connect(req, handle, (struct sockaddr*) &addr, sizeof addr, cb); +} + + +int uv__tcp_connect6(uv_connect_t* req, + uv_tcp_t* handle, + struct sockaddr_in6 addr, + uv_connect_cb cb) { + return uv__connect(req, handle, (struct sockaddr*) &addr, sizeof addr, cb); +} + + +int uv__tcp_nodelay(int fd, int on) { + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); +} + + +int uv__tcp_keepalive(int fd, int on, unsigned int delay) { + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) + return -errno; + +#ifdef TCP_KEEPIDLE + if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) + return -errno; +#endif + + /* Solaris/SmartOS, if you don't support keep-alive, + * then don't advertise it in your system headers... + */ + /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ +#if defined(TCP_KEEPALIVE) && !defined(__sun) + if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) + return -errno; +#endif + + return 0; +} + + +int uv_tcp_nodelay(uv_tcp_t* handle, int on) { + int err; + + if (uv__stream_fd(handle) != -1) { + err = uv__tcp_nodelay(uv__stream_fd(handle), on); + if (err) + return err; + } + + if (on) + handle->flags |= UV_TCP_NODELAY; + else + handle->flags &= ~UV_TCP_NODELAY; + + return 0; +} + + +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { + int err; + + if (uv__stream_fd(handle) != -1) { + err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay); + if (err) + return err; + } + + if (on) + handle->flags |= UV_TCP_KEEPALIVE; + else + handle->flags &= ~UV_TCP_KEEPALIVE; + + /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge + * uv_tcp_t with an int that's almost never used... + */ + + return 0; +} + + +int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { + if (enable) + handle->flags &= ~UV_TCP_SINGLE_ACCEPT; + else + handle->flags |= UV_TCP_SINGLE_ACCEPT; + return 0; +} + + +void uv__tcp_close(uv_tcp_t* handle) { + uv__stream_close((uv_stream_t*)handle); +}