Socket::Socket(struct ev_loop* loop, int fd, int af) : loop_(loop), watcher_(loop), timer_(loop), startedAt_(ev_now(loop)), lastActivityAt_(ev_now(loop)), fd_(fd), addressFamily_(af), secure_(false), state_(Operational), mode_(None), tcpCork_(false), remoteIP_(), remotePort_(0), localIP_(), localPort_(), callback_(nullptr), callbackData_(0) { #ifndef NDEBUG setLogging(false); static std::atomic<unsigned long long> id(0); setLoggingPrefix("Socket(%d, %s:%d)", ++id, remoteIP().c_str(), remotePort()); #endif TRACE("created. fd:%d, local(%s:%d)", fd_, localIP().c_str(), localPort()); watcher_.set<Socket, &Socket::io>(this); timer_.set<Socket, &Socket::timeout>(this); }
/** start first async operation for this HttpConnection. * * This is done by simply registering the underlying socket to the the I/O service * to watch for available input. * * \note This method must be invoked right after the object construction. * * \see stop() */ void HttpConnection::start(ServerSocket* listener, Socket* client, const HttpWorker::ConnectionHandle& handle) { handle_ = handle; listener_ = listener; socket_ = client; socket_->setReadyCallback<HttpConnection, &HttpConnection::io>(this); sink_.setSocket(socket_); #if defined(TCP_NODELAY) if (worker_->server().tcpNoDelay()) socket_->setTcpNoDelay(true); #endif #if !defined(NDEBUG) setLoggingPrefix("HttpConnection[%d,%llu|%s:%d]", worker_->id(), id_, remoteIP().c_str(), remotePort()); #endif TRACE("starting (fd=%d)", socket_->handle()); ref(); // <-- this reference is being decremented in close() worker_->server_.onConnectionOpen(this); if (isAborted()) { // The connection got directly closed (aborted) upon connection instance creation (e.g. within the onConnectionOpen-callback), // so delete the object right away. close(); return; } request_ = new HttpRequest(*this); ref(); if (socket_->state() == Socket::Handshake) { TRACE("start: handshake."); socket_->handshake<HttpConnection, &HttpConnection::handshakeComplete>(this); } else { #if defined(TCP_DEFER_ACCEPT) && defined(WITH_TCP_DEFER_ACCEPT) TRACE("start: processing input"); // it is ensured, that we have data pending, so directly start reading if (readSome()) process(); else close(); TRACE("start: processing input done"); #else TRACE("start: watchInput."); // client connected, but we do not yet know if we have data pending watchInput(worker_->server_.maxReadIdle()); #endif } unref(); }
bool Socket::openUnix(const std::string& unixPath, int flags) { #ifndef NDEBUG setLoggingPrefix("Socket(unix:%s)", unixPath.c_str()); #endif TRACE("connect(unix=%s)", unixPath.c_str()); flags |= O_NONBLOCK | O_CLOEXEC; int typeMask = 0; #if defined(SOCK_NONBLOCK) if (flags & O_NONBLOCK) { flags &= ~O_NONBLOCK; typeMask |= SOCK_NONBLOCK; } #endif #if defined(SOCK_CLOEXEC) if (flags & O_CLOEXEC) { flags &= ~O_CLOEXEC; typeMask |= SOCK_CLOEXEC; } #endif fd_ = ::socket(PF_UNIX, SOCK_STREAM | typeMask, 0); if (fd_ < 0) { TRACE("socket creation error: %s", strerror(errno)); return false; } if (flags) { if (fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) | flags) < 0) { // error } } struct sockaddr_un addr; addr.sun_family = AF_UNIX; size_t addrlen = sizeof(addr.sun_family) + strlen(strncpy(addr.sun_path, unixPath.c_str(), sizeof(addr.sun_path))); int rv = ::connect(fd_, (struct sockaddr*) &addr, addrlen); if (rv < 0) { ::close(fd_); fd_ = -1; TRACE("could not connect to %s: %s", unixPath.c_str(), strerror(errno)); return false; } state_ = Operational; return true; }
// {{{ HttpProxy impl HttpProxy::HttpProxy(HttpDirector* director, const std::string& name, size_t capacity, const std::string& hostname, int port) : HttpBackend(director, name, capacity), hostname_(hostname), port_(port), connections_() { #ifndef NDEBUG setLoggingPrefix("HttpProxy/%s", name.c_str()); #endif }
bool Socket::openTcp(const IPAddress& host, int port, int flags) { #ifndef NDEBUG setLoggingPrefix("Socket(tcp:%s:%d)", host.str().c_str(), port); #endif flags |= O_NONBLOCK | O_CLOEXEC; int typeMask = 0; #if defined(SOCK_NONBLOCK) if (flags & O_NONBLOCK) { flags &= ~O_NONBLOCK; typeMask |= SOCK_NONBLOCK; } #endif #if defined(SOCK_CLOEXEC) if (flags & O_CLOEXEC) { flags &= ~O_CLOEXEC; typeMask |= SOCK_CLOEXEC; } #endif fd_ = ::socket(host.family(), SOCK_STREAM | typeMask, IPPROTO_TCP); if (fd_ < 0) { TRACE("socket creation error: %s", strerror(errno)); return false; } if (flags) { if (fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL) | flags) < 0) { // error } } int rv = ::connect(fd_, (struct sockaddr*) host.data(), host.size()); if (rv == 0) { TRACE("connect: instant success (fd:%d)", fd_); state_ = Operational; return true; } else if (/*rv < 0 &&*/ errno == EINPROGRESS) { TRACE("connect: backgrounding (fd:%d)", fd_); state_ = Connecting; setMode(Write); return true; } else { TRACE("could not connect to %s:%d: %s", host.str().c_str(), port, strerror(errno)); ::close(fd_); fd_ = -1; return false; } }
// {{{ HttpProxy::ProxyConnection impl HttpProxy::ProxyConnection::ProxyConnection(HttpProxy* proxy) : HttpMessageProcessor(HttpMessageProcessor::RESPONSE), proxy_(proxy), refCount_(1), request_(nullptr), backend_(nullptr), cloak_(true), connectTimeout_(0), readTimeout_(0), writeTimeout_(0), writeBuffer_(), writeOffset_(0), writeProgress_(0), readBuffer_(), processingDone_(false) { #ifndef NDEBUG setLoggingPrefix("ProxyConnection/%p", this); #endif TRACE("ProxyConnection()"); }
HttpRequest::HttpRequest(HttpConnection& conn) : outputState_(Unhandled), connection(conn), method(), uri(), path(), fileinfo(), pathinfo(), query(), httpVersionMajor(0), httpVersionMinor(0), requestHeaders(), bytesTransmitted_(0), username(), documentRoot(), expectingContinue(false), //customData(), status(), responseHeaders(), outputFilters(), hostid_(), bodyCallback_(nullptr), bodyCallbackData_(nullptr), errorHandler_(nullptr) { #ifndef NDEBUG static std::atomic<unsigned long long> rid(0); setLoggingPrefix("HttpRequest(%lld,%s:%d)", ++rid, connection.remoteIP().c_str(), connection.remotePort()); #endif responseHeaders.push_back("Date", connection.worker().now().http_str().str()); if (connection.worker().server().advertise() && !connection.worker().server().tag().empty()) if (!responseHeaders.contains("Server")) responseHeaders.push_back("Server", connection.worker().server().tag()); }
/*! starts listening on a UNIX domain server socket. * * \param path the path on the local file system, that this socket is to listen to. * \param flags some flags, such as O_CLOEXEC and O_NONBLOCK (the most prominent) to set on server socket and each created client socket * * \retval true successfully initialized * \retval false some failure occured during setting up the server socket. */ bool ServerSocket::open(const std::string& path, int flags) { #ifndef NDEBUG setLoggingPrefix("ServerSocket(%s)", path.c_str()); #endif TRACE("opening"); int fd = -1; size_t addrlen; int sd_fd_count = sd_listen_fds(false); typeMask_ = 0; flags_ = flags; if (flags & O_CLOEXEC) { flags_ &= ~O_CLOEXEC; typeMask_ |= SOCK_CLOEXEC; } if (flags & O_NONBLOCK) { flags_ &= ~O_NONBLOCK; typeMask_ |= SOCK_NONBLOCK; } // check if passed by parent x0d first if ((fd = x0::getSocketUnix(path.c_str())) >= 0) { // socket found, but ensure our expected `flags` are set. if (flags && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags) < 0) { goto syserr; } else { goto done; } } // check if systemd created the socket for us if (sd_fd_count > 0) { fd = SD_LISTEN_FDS_START; int last = fd + sd_fd_count; for (; fd < last; ++fd) { if (sd_is_socket_unix(fd, AF_UNIX, SOCK_STREAM, path.c_str(), path.size()) > 0) { // socket found, but ensure our expected `flags` are set. if (flags && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags) < 0) { goto syserr; } else { goto done; } } } errorText_ = "Running under systemd socket unit, but we received no UNIX-socket for \"" + path + "\"."; goto err; } // create socket manually fd = ::socket(PF_UNIX, SOCK_STREAM, 0); if (fd < 0) goto syserr; if (flags && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | flags) < 0) goto syserr; struct sockaddr_un addr; addr.sun_family = AF_UNIX; if (path.size() >= sizeof(addr.sun_path)) { errno = ENAMETOOLONG; goto syserr; } addrlen = sizeof(addr.sun_family) + strlen(strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path))); if (::bind(fd, reinterpret_cast<struct sockaddr*>(&addr), addrlen) < 0) goto syserr; if (::listen(fd, backlog_)) goto syserr; if (chmod(path.c_str(), 0666) < 0) { perror("chmod"); } done: fd_ = fd; addressFamily_ = AF_UNIX; address_ = path; port_ = 0; io_.set<ServerSocket, &ServerSocket::accept>(this); start(); return true; syserr: errorText_ = strerror(errno); err: if (fd >= 0) ::close(fd); return false; }
/*! starts listening on a TCP/IP (v4 or v6) address. * * \param address the IP address to bind to * \param port the TCP/IP port number to listen to * \param flags some flags, such as O_CLOEXEC and O_NONBLOCK (the most prominent) to set on server socket and each created client socket * * \retval true successfully initialized * \retval false some failure occured during setting up the server socket. */ bool ServerSocket::open(const std::string& address, int port, int flags) { #ifndef NDEBUG setLoggingPrefix("ServerSocket(%s:%d)", address.c_str(), port); #endif TRACE("opening"); int sd_fd_count = sd_listen_fds(false); addrinfo* res = nullptr; addrinfo hints; addrinfo* ri = nullptr; int rc; int fd = -1; in6_addr saddr; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((rc = inet_pton(AF_INET, address.c_str(), &saddr)) == 1) { hints.ai_family = AF_INET; hints.ai_flags |= AI_NUMERICHOST; } else if ((rc = inet_pton(AF_INET6, address.c_str(), &saddr)) == 1) { hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; } else { switch (rc) { case -1: // errno is set properly errorText_ = strerror(errno); break; case 0: // invalid network addr format errorText_ = strerror(EINVAL); break; default: // unknown error errorText_ = strerror(EINVAL); break; } return false; } char sport[16]; snprintf(sport, sizeof(sport), "%d", port); if ((rc = getaddrinfo(address.c_str(), sport, &hints, &res)) != 0) { errorText_ = gai_strerror(rc); goto err; } typeMask_ = 0; flags_ = flags; if (flags & O_CLOEXEC) { flags_ &= ~O_CLOEXEC; typeMask_ |= SOCK_CLOEXEC; } if (flags & O_NONBLOCK) { flags_ &= ~O_NONBLOCK; typeMask_ |= SOCK_NONBLOCK; } // check if passed by parent x0d first for (ri = res; ri != nullptr; ri = ri->ai_next) { if ((fd = x0::getSocketInet(address.c_str(), port)) >= 0) { // socket found, but ensure our expected `flags` are set. if ((flags & O_NONBLOCK) && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0) goto syserr; if ((flags & O_CLOEXEC) && fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) goto syserr; goto done; } } // check if systemd created the socket for us if (sd_fd_count > 0) { fd = SD_LISTEN_FDS_START; int last = fd + sd_fd_count; for (addrinfo* ri = res; ri != nullptr; ri = ri->ai_next) { for (; fd < last; ++fd) { if (sd_is_socket_inet(fd, ri->ai_family, ri->ai_socktype, true, port) > 0) { // socket found, but ensure our expected `flags` are set. if ((flags & O_NONBLOCK) && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0) goto syserr; if ((flags & O_CLOEXEC) && fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) goto syserr; #if defined(TCP_QUICKACK) if (::setsockopt(fd, SOL_TCP, TCP_QUICKACK, &rc, sizeof(rc)) < 0) goto syserr; #endif #if defined(TCP_DEFER_ACCEPT) && defined(WITH_TCP_DEFER_ACCEPT) if (::setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &rc, sizeof(rc)) < 0) goto syserr; #endif goto done; } } } char buf[256]; snprintf(buf, sizeof(buf), "Running under systemd socket unit, but we received no socket for %s:%d.", address.c_str(), port); errorText_ = buf; goto err; } // create socket manually for (ri = res; ri != nullptr; ri = ri->ai_next) { fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd < 0) goto syserr; if ((flags & O_NONBLOCK) && fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0) goto syserr; if ((flags & O_CLOEXEC) && fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) goto syserr; rc = 1; #if defined(SO_REUSEADDR) if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)) < 0) goto syserr; #endif #if defined(TCP_QUICKACK) if (::setsockopt(fd, SOL_TCP, TCP_QUICKACK, &rc, sizeof(rc)) < 0) goto syserr; #endif #if defined(TCP_DEFER_ACCEPT) && defined(WITH_TCP_DEFER_ACCEPT) if (::setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &rc, sizeof(rc)) < 0) goto syserr; #endif // TODO so_linger(false, 0) // TODO so_keepalive(true) if (::bind(fd, res->ai_addr, res->ai_addrlen) < 0) goto syserr; if (::listen(fd, backlog_) < 0) goto syserr; goto done; } done: fd_ = fd; addressFamily_ = res->ai_family; freeaddrinfo(res); address_ = address; port_ = port; io_.set<ServerSocket, &ServerSocket::accept>(this); start(); return true; syserr: errorText_ = strerror(errno); err: if (res) freeaddrinfo(res); if (fd >= 0) ::close(fd); return false; }