/* * Process a socket readable event */ static void readEvent(MaConn *conn) { MaPacket *packet; MprBuf *content; int nbytes, len; do { if ((packet = getPacket(conn, &len)) == 0) { break; } mprAssert(len > 0); content = packet->content; nbytes = mprReadSocket(conn->sock, mprGetBufEnd(content), len); showRequest(content, nbytes, len); if (nbytes > 0) { mprAdjustBufEnd(content, nbytes); maProcessReadEvent(conn, packet); } else { if (mprIsSocketEof(conn->sock)) { conn->dedicated = 0; if (conn->request) { maProcessReadEvent(conn, packet); } } else if (nbytes < 0) { maFailConnection(conn, MPR_HTTP_CODE_COMMS_ERROR, "Communications read error"); } } } while (!conn->disconnected && conn->dedicated); }
/* Read data from the peer. This will use the existing conn->input packet or allocate a new packet if required to hold the data. The number of bytes read is stored in conn->lastRead. SSL connections are traced. Socket error messages are stored in conn->errorMsg. */ static void readPeerData(HttpConn *conn) { HttpPacket *packet; ssize size; if ((packet = getPacket(conn, &size)) != 0) { conn->lastRead = mprReadSocket(conn->sock, mprGetBufEnd(packet->content), size); if (conn->lastRead > 0) { mprAdjustBufEnd(packet->content, conn->lastRead); } else if (conn->lastRead < 0 && mprIsSocketEof(conn->sock)) { if (conn->state < HTTP_STATE_PARSED) { conn->error = 1; conn->rx->eof = 1; } conn->errorMsg = conn->sock->errorMsg ? conn->sock->errorMsg : sclone("Connection reset"); conn->keepAliveCount = 0; conn->lastRead = 0; httpTrace(conn, "connection.close", "context", "msg:'%s'", conn->errorMsg); } } }
/* * IO event handler. If multithreaded, this will be run by a worker thread. NOTE: a request is not typically permanently * assigned to a worker thread. Each io event may be serviced by a different worker thread. The exception is CGI * requests which block to wait for the child to complete (Needed on some platforms that don't permit cross thread * waiting). */ static int ioEvent(MaConn *conn, int mask) { mprAssert(conn); lock(conn); conn->time = mprGetTime(conn); mprLog(conn, 7, "ioEvent for fd %d, mask %d\n", conn->sock->fd); if (mask & MPR_WRITABLE) { maProcessWriteEvent(conn); } if (mask & MPR_READABLE) { readEvent(conn); } conn->time = mprGetTime(conn); if (mprIsSocketEof(conn->sock) || conn->disconnected || conn->connectionFailed || (conn->request == 0 && conn->keepAliveCount < 0)) { /* * This will close the connection and free all connection resources. NOTE: we compare keepAliveCount with "< 0" * so that the client can have one more keep alive request. It should respond to the "Connection: close" and * thus initiate a client-led close. This reduces TIME_WAIT states on the server. Must unlock the connection * to allow pending callbacks to run and complete. */ unlock(conn); maDestroyPipeline(conn); mprFree(conn->arena); return 1; } /* * We allow read events even if the current request is not complete and does not have body data. The pipelined * request will be buffered and be ready for when the current request completes. */ maEnableConnEvents(conn, MPR_READABLE); unlock(conn); return 0; }
/* Wait for the connection to reach a given state. Should only be used on the client side. @param state Desired state. Set to zero if you want to wait for one I/O event. @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity and duration timeouts. */ PUBLIC int httpWait(HttpConn *conn, int state, MprTicks timeout) { HttpLimits *limits; MprTicks delay, start; int64 dispatcherMark; int justOne; limits = conn->limits; if (conn->endpoint) { assert(!conn->endpoint); return MPR_ERR_BAD_STATE; } if (conn->state <= HTTP_STATE_BEGIN) { return MPR_ERR_BAD_STATE; } if (state == 0) { /* Wait for just one I/O event */ state = HTTP_STATE_FINALIZED; justOne = 1; } else { justOne = 0; } if (conn->error) { if (conn->state >= state) { return 0; } return MPR_ERR_BAD_STATE; } if (timeout < 0) { timeout = limits->requestTimeout; } else if (timeout == 0) { timeout = MPR_MAX_TIMEOUT; } if (state > HTTP_STATE_CONTENT) { httpFinalizeOutput(conn); } start = conn->http->now; dispatcherMark = mprGetEventMark(conn->dispatcher); while (conn->state < state && !conn->error && !mprIsSocketEof(conn->sock)) { if (httpRequestExpired(conn, -1)) { return MPR_ERR_TIMEOUT; } httpEnableConnEvents(conn); delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout)); delay = max(delay, 0); mprWaitForEvent(conn->dispatcher, delay, dispatcherMark); if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) { break; } dispatcherMark = mprGetEventMark(conn->dispatcher); } if (conn->error) { return MPR_ERR_NOT_READY; } if (conn->state < state) { if (mprGetRemainingTicks(start, timeout) <= 0) { return MPR_ERR_TIMEOUT; } if (!justOne) { return MPR_ERR_CANT_READ; } } conn->lastActivity = conn->http->now; return 0; }
/* Wait for the connection to reach a given state. Should only be used on the client side. @param state Desired state. Set to zero if you want to wait for one I/O event. @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity and duration timeouts. */ PUBLIC int httpWait(HttpStream *stream, int state, MprTicks timeout) { HttpLimits *limits; MprTicks delay, start; int64 dispatcherMark; int justOne; limits = stream->limits; if (httpServerStream(stream)) { return MPR_ERR_BAD_STATE; } if (stream->state <= HTTP_STATE_BEGIN) { return MPR_ERR_BAD_STATE; } if (state == 0) { /* Wait for just one I/O event */ state = HTTP_STATE_FINALIZED; justOne = 1; } else { justOne = 0; } if (stream->error) { if (stream->state >= state) { return 0; } return MPR_ERR_BAD_STATE; } if (timeout < 0) { timeout = limits->requestTimeout; } else if (timeout == 0) { timeout = MPR_MAX_TIMEOUT; } if (state > HTTP_STATE_CONTENT) { httpFinalizeOutput(stream); } start = stream->http->now; dispatcherMark = mprGetEventMark(stream->dispatcher); // TODO - how does this work with http2? while (stream->state < state && !stream->error && !mprIsSocketEof(stream->sock)) { if (httpRequestExpired(stream, -1)) { return MPR_ERR_TIMEOUT; } // TODO - review httpEnableNetEvents(stream->net); delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout)); delay = max(delay, 0); mprWaitForEvent(stream->dispatcher, delay, dispatcherMark); if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) { break; } dispatcherMark = mprGetEventMark(stream->dispatcher); } if (stream->error) { return MPR_ERR_NOT_READY; } if (stream->state < state) { if (mprGetRemainingTicks(start, timeout) <= 0) { return MPR_ERR_TIMEOUT; } if (!justOne) { return MPR_ERR_CANT_READ; } } stream->lastActivity = stream->http->now; return 0; }
/* Handle IO on the connection. Initially the conn->dispatcher will be set to the server->dispatcher and the first I/O event will be handled on the server thread (or main thread). A request handler may create a new conn->dispatcher and transfer execution to a worker thread if required. */ PUBLIC void httpIO(HttpConn *conn, int eventMask) { MprSocket *sp; sp = conn->sock; if (conn->destroyed) { /* Connection has been destroyed */ return; } if (conn->state < HTTP_STATE_PARSED && mprShouldDenyNewRequests()) { httpDestroyConn(conn); return; } assert(conn->tx); assert(conn->rx); #if DEPRECATED || 1 /* Just IO state asserting */ if (conn->io) { assert(!conn->io); return; } conn->io = 1; #endif if ((eventMask & MPR_WRITABLE) && conn->connectorq) { httpResumeQueue(conn->connectorq); } if (eventMask & MPR_READABLE) { readPeerData(conn); } if (sp->secured && !conn->secure) { conn->secure = 1; if (sp->peerCert) { httpTrace(conn, "connection.ssl", "context", "msg:'Connection secured with peer certificate'," \ "secure:true,cipher:'%s',peerName:'%s',subject:'%s',issuer:'%s',session:'%s'", sp->cipher, sp->peerName, sp->peerCert, sp->peerCertIssuer, sp->session); } else { httpTrace(conn, "connection.ssl", "context", "msg:'Connection secured without peer certificate',secure:true,cipher:'%s',session:'%s'", sp->cipher, sp->session); } if (mprGetLogLevel() >= 5) { mprLog("info http ssl", 5, "SSL State: %s", mprGetSocketState(sp)); } } /* Process one or more complete requests in the packet */ do { /* This is and must be the only place httpProtocol is ever called */ httpProtocol(conn); } while (conn->endpoint && conn->state == HTTP_STATE_COMPLETE && prepForNext(conn)); /* When a request completes, prepForNext will reset the state to HTTP_STATE_BEGIN */ if (conn->state < HTTP_STATE_PARSED && conn->endpoint && (mprIsSocketEof(conn->sock) || (conn->keepAliveCount <= 0))) { httpDestroyConn(conn); } else if (!mprIsSocketEof(conn->sock) && conn->async && !conn->delay) { httpEnableConnEvents(conn); } conn->io = 0; }