static void connTimeout(HttpConn *conn, MprEvent *mprEvent) { HttpLimits *limits; cchar *event, *msg, *prefix; if (conn->destroyed) { return; } assert(conn->tx); assert(conn->rx); msg = 0; event = 0; limits = conn->limits; assert(limits); if (conn->timeoutCallback) { (conn->timeoutCallback)(conn); } if (!conn->connError) { prefix = (conn->state == HTTP_STATE_BEGIN) ? "Idle connection" : "Request"; if (conn->timeout == HTTP_PARSE_TIMEOUT) { msg = sfmt("%s exceeded parse headers timeout of %lld sec", prefix, limits->requestParseTimeout / 1000); event = "timeout.parse"; } else if (conn->timeout == HTTP_INACTIVITY_TIMEOUT) { msg = sfmt("%s exceeded inactivity timeout of %lld sec", prefix, limits->inactivityTimeout / 1000); event = "timeout.inactivity"; } else if (conn->timeout == HTTP_REQUEST_TIMEOUT) { msg = sfmt("%s exceeded timeout %lld sec", prefix, limits->requestTimeout / 1000); event = "timeout.duration"; } if (conn->state < HTTP_STATE_FIRST) { httpDisconnect(conn); if (msg) { httpTrace(conn, event, "error", "msg:'%s'", msg); } } else { httpError(conn, HTTP_CODE_REQUEST_TIMEOUT, "%s", msg); } } if (httpClientConn(conn)) { httpDestroyConn(conn); } else { httpEnableConnEvents(conn); } }
/* The current request has an error and cannot complete as normal. This call sets the Http response status and overrides the normal output with an alternate error message. If the output has alread started (headers sent), then the connection MUST be closed so the client can get some indication the request failed. */ static void errorv(HttpConn *conn, int flags, cchar *fmt, va_list args) { HttpRx *rx; HttpTx *tx; cchar *uri; int status; assert(fmt); rx = conn->rx; tx = conn->tx; if (conn == 0) { return; } status = flags & HTTP_CODE_MASK; if (status == 0) { status = HTTP_CODE_INTERNAL_SERVER_ERROR; } if (flags & (HTTP_ABORT | HTTP_CLOSE)) { conn->keepAliveCount = 0; } if (flags & HTTP_ABORT) { conn->connError = 1; if (rx) { rx->eof = 1; } } if (!conn->error) { conn->error = 1; httpOmitBody(conn); conn->errorMsg = formatErrorv(conn, status, fmt, args); mprLog(2, "Error: %s", conn->errorMsg); HTTP_NOTIFY(conn, HTTP_EVENT_ERROR, 0); if (conn->endpoint) { if (status == HTTP_CODE_NOT_FOUND) { httpMonitorEvent(conn, HTTP_COUNTER_NOT_FOUND_ERRORS, 1); } httpMonitorEvent(conn, HTTP_COUNTER_ERRORS, 1); } httpAddHeaderString(conn, "Cache-Control", "no-cache"); if (conn->endpoint && tx && rx) { if (tx->flags & HTTP_TX_HEADERS_CREATED) { /* If the response headers have been sent, must let the other side of the failure ... aborting the request is the only way as the status has been sent. */ flags |= HTTP_ABORT; } else { if (rx->route && (uri = httpLookupRouteErrorDocument(rx->route, tx->status)) && !smatch(uri, rx->uri)) { errorRedirect(conn, uri); } else { makeAltBody(conn, status); } } } httpFinalize(conn); } if (flags & HTTP_ABORT) { httpDisconnect(conn); } }
/* Accept a new client connection on a new socket. This will come in on a worker thread with a new dispatcher dedicated to this connection. */ PUBLIC HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { Http *http; HttpConn *conn; HttpAddress *address; MprSocket *sock; int64 value; assert(event); assert(event->dispatcher); assert(endpoint); sock = event->sock; http = endpoint->http; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint, event->dispatcher)) == 0) { mprCloseSocket(sock, 0); return 0; } conn->notifier = endpoint->notifier; conn->async = endpoint->async; conn->endpoint = endpoint; conn->sock = sock; conn->port = sock->port; conn->ip = sclone(sock->ip); if ((value = httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_CONNECTIONS, 1)) > conn->limits->connectionsMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent connections',active:%d,max:%d", (int) value, conn->limits->connectionsMax); httpDestroyConn(conn); return 0; } if (mprGetHashLength(http->addresses) > conn->limits->clientMax) { httpTrace(conn, "connection.accept.error", "error", "msg:'Too many concurrent clients',active:%d,max:%d", mprGetHashLength(http->addresses), conn->limits->clientMax); httpDestroyConn(conn); return 0; } address = conn->address; if (address && address->banUntil) { if (address->banUntil < http->now) { httpTrace(conn, "monitor.ban.stop", "context", "client:'%s'", conn->ip); address->banUntil = 0; } else { if (address->banStatus) { httpError(conn, HTTP_CLOSE | address->banStatus, "Connection refused, client banned: %s", address->banMsg ? address->banMsg : ""); } else { httpDestroyConn(conn); return 0; } } } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 0) < 0) { httpDisconnect(conn); httpTrace(conn, "connection.upgrade.error", "error", "msg:'Cannot upgrade socket. %s'", sock->errorMsg); httpMonitorEvent(conn, HTTP_COUNTER_SSL_ERRORS, 1); httpDestroyConn(conn); return 0; } } assert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); httpTrace(conn, "connection.accept.new", "context", "peer:'%s',endpoint:'%s:%d'", conn->ip, sock->acceptIp, sock->acceptPort); event->mask = MPR_READABLE; event->timestamp = conn->http->now; (conn->ioCallback)(conn, event); return conn; }
PUBLIC void httpSendOutgoingService(HttpQueue *q) { HttpConn *conn; HttpTx *tx; MprFile *file; MprOff written; int errCode; conn = q->conn; tx = conn->tx; conn->lastActivity = conn->http->now; if (tx->finalizedConnector) { return; } if (tx->flags & HTTP_TX_NO_BODY) { httpDiscardQueueData(q, 1); } if ((tx->bytesWritten + q->ioCount) > conn->limits->txBodySize && conn->limits->txBodySize < HTTP_UNLIMITED) { httpLimitError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE | ((tx->bytesWritten) ? HTTP_ABORT : 0), "Http transmission aborted. Exceeded max body of %lld bytes", conn->limits->txBodySize); if (tx->bytesWritten) { httpFinalizeConnector(conn); return; } } tx->writeBlocked = 0; if (q->ioIndex == 0) { buildSendVec(q); } /* No need to loop around as send file tries to write as much of the file as possible. If not eof, will always have the socket blocked. */ file = q->ioFile ? tx->file : 0; written = mprSendFileToSocket(conn->sock, file, q->ioPos, q->ioCount, q->iovec, q->ioIndex, NULL, 0); if (written < 0) { errCode = mprGetError(); if (errCode == EAGAIN || errCode == EWOULDBLOCK) { /* Socket full, wait for an I/O event */ tx->writeBlocked = 1; } else { if (errCode != EPIPE && errCode != ECONNRESET && errCode != ECONNABORTED && errCode != ENOTCONN) { httpError(conn, HTTP_ABORT | HTTP_CODE_COMMS_ERROR, "sendConnector: error, errCode %d", errCode); } else { httpDisconnect(conn); } httpFinalizeConnector(conn); } httpTrace(conn, "connection.io.error", "error", "msg:'Connector write error',errno:%d", errCode); } else if (written > 0) { tx->bytesWritten += written; freeSendPackets(q, written); adjustSendVec(q, written); } if (q->first && q->first->flags & HTTP_PACKET_END) { httpFinalizeConnector(conn); httpGetPacket(q); } }