/* Open the handler for a new request */ static int openCgi(HttpQueue *q) { HttpConn *conn; Cgi *cgi; int nproc; conn = q->conn; if ((nproc = (int) httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_PROCESSES, 1)) >= conn->limits->processMax) { httpTrace(conn, "cgi.limit.error", "error", "msg=\"Too many concurrent processes\", activeProcesses=%d, maxProcesses=%d", nproc, conn->limits->processMax); httpError(conn, HTTP_CODE_SERVICE_UNAVAILABLE, "Server overloaded"); httpMonitorEvent(q->conn, HTTP_COUNTER_ACTIVE_PROCESSES, -1); return MPR_ERR_CANT_OPEN; } if ((cgi = mprAllocObj(Cgi, manageCgi)) == 0) { /* Normal mem handler recovery */ return MPR_ERR_MEMORY; } httpTrimExtraPath(conn); httpMapFile(conn); httpCreateCGIParams(conn); q->queueData = q->pair->queueData = cgi; cgi->conn = conn; cgi->readq = httpCreateQueue(conn, conn->http->cgiConnector, HTTP_QUEUE_RX, 0); cgi->writeq = httpCreateQueue(conn, conn->http->cgiConnector, HTTP_QUEUE_TX, 0); cgi->readq->pair = cgi->writeq; cgi->writeq->pair = cgi->readq; cgi->writeq->queueData = cgi->readq->queueData = cgi; return 0; }
/* Destroy a connection. This removes the connection from the list of connections. */ PUBLIC void httpDestroyConn(HttpConn *conn) { if (!conn->destroyed && !conn->borrowed) { HTTP_NOTIFY(conn, HTTP_EVENT_DESTROY, 0); if (httpServerConn(conn)) { httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_CONNECTIONS, -1); if (conn->activeRequest) { httpMonitorEvent(conn, HTTP_COUNTER_ACTIVE_REQUESTS, -1); conn->activeRequest = 0; } } httpRemoveConn(conn); conn->input = 0; if (conn->tx) { httpClosePipeline(conn); } if (conn->sock) { mprCloseSocket(conn->sock, 0); } if (conn->dispatcher && conn->dispatcher->flags & MPR_DISPATCHER_AUTO) { mprDestroyDispatcher(conn->dispatcher); } conn->destroyed = 1; } }
PUBLIC void httpLimitError(HttpConn *conn, int flags, cchar *fmt, ...) { va_list args; va_start(args, fmt); if (conn->endpoint) { httpMonitorEvent(conn, HTTP_COUNTER_LIMIT_ERRORS, 1); } errorv(conn, flags, fmt, args); va_end(args); }
static void closeCgi(HttpQueue *q) { Cgi *cgi; MprCmd *cmd; if ((cgi = q->queueData) != 0) { cmd = cgi->cmd; if (cmd) { mprSetCmdCallback(cmd, NULL, NULL); mprDestroyCmd(cmd); cgi->cmd = 0; } httpMonitorEvent(q->conn, HTTP_COUNTER_ACTIVE_PROCESSES, -1); } }
/* 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; }