void HttpConnection::io(Socket *, int revents) { TRACE("io(revents=%04x) isHandlingRequest:%d", revents, flags_ & IsHandlingRequest); ref(); if (revents & ev::ERROR) { log(Severity::error, "Potential bug in connection I/O watching. Closing."); abort(); goto done; } if ((revents & Socket::Read) && !readSome()) goto done; if ((revents & Socket::Write) && !writeSome()) goto done; if (!process()) goto done; switch (status()) { case ReadingRequest: watchInput(worker_->server_.maxReadIdle()); break; case KeepAliveRead: watchInput(worker_->server_.maxKeepAlive()); default: break; } done: unref(); }
void HttpConnection::io(Socket *, int revents) { TRACE(1, "io(revents=%04x) isHandlingRequest:%d", revents, isHandlingRequest()); ref(); if (revents & ev::ERROR) { log(Severity::error, "Potential bug in connection I/O watching. Closing."); abort(); goto done; } // socket is ready for read? if ((revents & Socket::Read) && !readSome()) goto done; // socket is ready for write? if ((revents & Socket::Write) && !writeSome()) goto done; switch (status()) { case ReadingRequest: TRACE(1, "io(): status=%s. Watch for read.", status_str()); watchInput(worker_->server_.maxReadIdle()); break; case KeepAliveRead: if (isInputPending()) { do { // we're in keep-alive state but have (partial) request in buffer pending // so do process it right away TRACE(1, "io(): status=%s. Pipelined input pending.", status_str()); process(); } while (isInputPending() && status() == KeepAliveRead); if (status() == KeepAliveRead) { // we're still in keep-alive state but no (partial) request in buffer pending // so watch for socket input event. TRACE(1, "io(): status=%s. Watch for read (keep-alive).", status_str()); watchInput(worker_->server_.maxKeepAlive()); } } else { // we are in keep-alive state and have no (partial) request in buffer pending // so watch for socket input event. TRACE(1, "io(): status=%s. Watch for read (keep-alive).", status_str()); watchInput(worker_->server_.maxKeepAlive()); } break; case ProcessingRequest: case SendingReplyDone: case SendingReply: case Undefined: // should never be reached TRACE(1, "io(): status=%s. Do not touch I/O watcher.", status_str()); break; } done: unref(); }
/** * Writes as much as it wouldn't block of the response stream into the underlying socket. * */ bool HttpConnection::writeSome() { TRACE("writeSome()"); ref(); ssize_t rv = output_.sendto(sink_); TRACE("writeSome(): sendto().rv=%ld %s", rv, rv < 0 ? strerror(errno) : ""); if (rv > 0) { // output chunk written request_->bytesTransmitted_ += rv; goto done; } if (rv == 0) { // output fully written watchInput(); if (request_->isFinished()) { // finish() got invoked before reply was fully sent out, thus, // finalize() was delayed. request_->finalize(); } TRACE("writeSome: output fully written. closed:%d, outputPending:%ld, refCount:%d", isClosed(), output_.size(), refCount_); if (isClosed() && !isOutputPending() && refCount_ == 0) { worker_->release(handle_); } goto done; } // sendto() failed switch (errno) { case EINTR: break; case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif // complete write would block, so watch write-ready-event and be called back watchOutput(); break; default: //log(Severity::error, "Connection write error: %s", strerror(errno)); goto err; } done: unref(); return true; err: abort(); unref(); return false; }
/** 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(); }
/** 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) { setStatus(ReadingRequest); 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 (worker_->server().lingering()) socket_->setLingering(worker_->server().lingering()); TRACE(1, "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; } if (!request_) request_ = new HttpRequest(*this); ref(); if (socket_->state() == Socket::Handshake) { TRACE(1, "start: handshake."); socket_->handshake<HttpConnection, &HttpConnection::handshakeComplete>(this); } else { #if defined(TCP_DEFER_ACCEPT) && defined(ENABLE_TCP_DEFER_ACCEPT) TRACE(1, "start: processing input"); // it is ensured, that we have data pending, so directly start reading io(nullptr, ev::READ); TRACE(1, "start: processing input done"); #else TRACE(1, "start: watchInput."); // client connected, but we do not yet know if we have data pending watchInput(worker_->server_.maxReadIdle()); #endif } unref(); }
void HttpConnection::handshakeComplete(Socket *) { TRACE("handshakeComplete() socketState=%s", socket_->state_str()); if (socket_->state() == Socket::Operational) watchInput(worker_->server_.maxReadIdle()); else { TRACE("handshakeComplete(): handshake failed\n%s", StackTrace().c_str()); close(); } }
/** * This method gets invoked when there is data in our HttpConnection ready to read. * * We assume, that we are in request-parsing state. */ bool HttpConnection::readSome() { TRACE("readSome()"); ref(); if (status() == KeepAliveRead) { TRACE("readSome: status was keep-alive-read. resetting to reading-request", request_->outputState_); status_ = ReadingRequest; } ssize_t rv = socket_->read(input_); if (rv < 0) { // error switch (errno) { case EINTR: case EAGAIN: #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif watchInput(worker_->server_.maxReadIdle()); break; default: //log(Severity::error, "Connection read error: %s", strerror(errno)); goto err; } } else if (rv == 0) { // EOF TRACE("readSome: (EOF)"); goto err; } else { TRACE("readSome: read %ld bytes, status:%s, ros:%d", rv, status_str(), request_->outputState_); process(); } unref(); return true; err: abort(); unref(); return false; }