/* Add a packet to the io vector. Return the number of bytes added to the vector. */ static void addPacketForNet(HttpQueue *q, HttpPacket *packet) { HttpTx *tx; HttpConn *conn; int item; conn = q->conn; tx = conn->tx; mprAssert(q->count >= 0); mprAssert(q->ioIndex < (HTTP_MAX_IOVEC - 2)); if (packet->prefix) { addToNetVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix)); } if (httpGetPacketLength(packet) > 0) { addToNetVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content)); } item = (packet->flags & HTTP_PACKET_HEADER) ? HTTP_TRACE_HEADER : HTTP_TRACE_BODY; if (httpShouldTrace(conn, HTTP_TRACE_TX, item, tx->ext) >= 0) { httpTraceContent(conn, HTTP_TRACE_TX, item, packet, 0, (ssize) tx->bytesWritten); } }
void httpCreateTxPipeline(HttpConn *conn, HttpRoute *route) { Http *http; HttpTx *tx; HttpRx *rx; HttpQueue *q; HttpStage *stage, *filter; int next, hasOutputFilters; mprAssert(conn); mprAssert(route); http = conn->http; rx = conn->rx; tx = conn->tx; tx->outputPipeline = mprCreateList(-1, 0); if (tx->handler == 0) { tx->handler = http->passHandler; } mprAddItem(tx->outputPipeline, tx->handler); hasOutputFilters = 0; if (route->outputStages) { for (next = 0; (filter = mprGetNextItem(route->outputStages, &next)) != 0; ) { if (matchFilter(conn, filter, route, HTTP_STAGE_TX) == HTTP_ROUTE_OK) { mprAddItem(tx->outputPipeline, filter); mprLog(4, "Select output filter: \"%s\"", filter->name); hasOutputFilters = 1; } } } if (tx->connector == 0) { if (tx->handler == http->fileHandler && (rx->flags & HTTP_GET) && !hasOutputFilters && !conn->secure && httpShouldTrace(conn, HTTP_TRACE_TX, HTTP_TRACE_BODY, tx->ext) < 0) { tx->connector = http->sendConnector; } else if (route && route->connector) { tx->connector = route->connector; } else { tx->connector = http->netConnector; } } mprAddItem(tx->outputPipeline, tx->connector); mprLog(4, "Select connector: \"%s\"", tx->connector->name); /* Create the outgoing queue heads and open the queues */ q = tx->queue[HTTP_QUEUE_TX]; for (next = 0; (stage = mprGetNextItem(tx->outputPipeline, &next)) != 0; ) { q = httpCreateQueue(conn, stage, HTTP_QUEUE_TX, q); } conn->writeq = tx->queue[HTTP_QUEUE_TX]->nextQ; conn->connectorq = tx->queue[HTTP_QUEUE_TX]->prevQ; pairQueues(conn); /* Put the header before opening the queues incase an open routine actually services and completes the request httpHandleOptionsTrace does this when called from openFile() in fileHandler. */ httpPutForService(conn->writeq, httpCreateHeaderPacket(), HTTP_DELAY_SERVICE); openQueues(conn); /* Refinalize if httpFinalize was called before the Tx pipeline was created */ if (conn->refinalize) { conn->finalized = 0; httpFinalize(conn); } }
PUBLIC void httpCreateTxPipeline(HttpConn *conn, HttpRoute *route) { Http *http; HttpTx *tx; HttpRx *rx; HttpQueue *q; HttpStage *stage, *filter; int next, hasOutputFilters; assert(conn); assert(route); http = conn->http; rx = conn->rx; tx = conn->tx; tx->outputPipeline = mprCreateList(-1, 0); if (conn->endpoint) { if (tx->handler == 0 || tx->finalized) { tx->handler = http->passHandler; } mprAddItem(tx->outputPipeline, tx->handler); } hasOutputFilters = 0; if (route->outputStages) { for (next = 0; (filter = mprGetNextItem(route->outputStages, &next)) != 0; ) { if (matchFilter(conn, filter, route, HTTP_STAGE_TX) == HTTP_ROUTE_OK) { mprAddItem(tx->outputPipeline, filter); if (rx->traceLevel >= 0) { mprLog(rx->traceLevel, "Select output filter: \"%s\"", filter->name); } hasOutputFilters = 1; } } } if (tx->connector == 0) { #if !BIT_ROM if (tx->handler == http->fileHandler && (rx->flags & HTTP_GET) && !hasOutputFilters && !conn->secure && httpShouldTrace(conn, HTTP_TRACE_TX, HTTP_TRACE_BODY, tx->ext) < 0) { tx->connector = http->sendConnector; } else #endif if (route && route->connector) { tx->connector = route->connector; } else { tx->connector = http->netConnector; } } mprAddItem(tx->outputPipeline, tx->connector); if (rx->traceLevel >= 0) { mprLog(rx->traceLevel + 1, "Select connector: \"%s\"", tx->connector->name); } /* Create the outgoing queue heads and open the queues */ q = tx->queue[HTTP_QUEUE_TX]; for (next = 0; (stage = mprGetNextItem(tx->outputPipeline, &next)) != 0; ) { q = httpCreateQueue(conn, stage, HTTP_QUEUE_TX, q); } conn->connectorq = tx->queue[HTTP_QUEUE_TX]->prevQ; /* Double the connector max hi-water mark. This optimization permits connectors to accept packets without unnecesary flow control. */ conn->connectorq->max *= 2; pairQueues(conn); /* Put the header before opening the queues incase an open routine actually services and completes the request */ httpPutForService(conn->writeq, httpCreateHeaderPacket(), HTTP_DELAY_SERVICE); /* Open the pipelien stages. This calls the open entrypoints on all stages */ openQueues(conn); }
/* Accept a new client connection on a new socket. If multithreaded, this will come in on a worker thread dedicated to this connection. This is called from the listen wait handler. */ HttpConn *httpAcceptConn(HttpEndpoint *endpoint, MprEvent *event) { HttpConn *conn; MprSocket *sock; MprDispatcher *dispatcher; MprEvent e; int level; mprAssert(endpoint); mprAssert(event); /* This will block in sync mode until a connection arrives */ if ((sock = mprAcceptSocket(endpoint->sock)) == 0) { if (endpoint->sock->handler) { mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } return 0; } if (endpoint->ssl) { if (mprUpgradeSocket(sock, endpoint->ssl, 1) < 0) { mprCloseSocket(sock, 0); return 0; } } if (endpoint->sock->handler) { /* Re-enable events on the listen socket */ mprEnableSocketEvents(endpoint->sock, MPR_READABLE); } dispatcher = event->dispatcher; if (mprShouldDenyNewRequests()) { mprCloseSocket(sock, 0); return 0; } if ((conn = httpCreateConn(endpoint->http, endpoint, 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); conn->secure = (endpoint->ssl != 0); if (!httpValidateLimits(endpoint, HTTP_VALIDATE_OPEN_CONN, conn)) { conn->endpoint = 0; httpDestroyConn(conn); return 0; } mprAssert(conn->state == HTTP_STATE_BEGIN); httpSetState(conn, HTTP_STATE_CONNECTED); if ((level = httpShouldTrace(conn, HTTP_TRACE_RX, HTTP_TRACE_CONN, NULL)) >= 0) { mprLog(level, "### Incoming connection from %s:%d to %s:%d %s", conn->ip, conn->port, sock->acceptIp, sock->acceptPort, conn->secure ? "(secure)" : ""); } e.mask = MPR_READABLE; e.timestamp = conn->http->now; (conn->ioCallback)(conn, &e); return conn; }
/* OPT */ bool httpValidateLimits(HttpEndpoint *endpoint, int event, HttpConn *conn) { HttpLimits *limits; Http *http; cchar *action; int count, level, dir; limits = conn->limits; dir = HTTP_TRACE_RX; action = "unknown"; mprAssert(conn->endpoint == endpoint); http = endpoint->http; lock(http); switch (event) { case HTTP_VALIDATE_OPEN_CONN: /* This measures active client systems with unique IP addresses. */ if (endpoint->clientCount >= limits->clientMax) { unlock(http); /* Abort connection */ httpError(conn, HTTP_ABORT | HTTP_CODE_SERVICE_UNAVAILABLE, "Too many concurrent clients %d/%d", endpoint->clientCount, limits->clientMax); return 0; } count = (int) PTOL(mprLookupKey(endpoint->clientLoad, conn->ip)); mprAddKey(endpoint->clientLoad, conn->ip, ITOP(count + 1)); endpoint->clientCount = (int) mprGetHashLength(endpoint->clientLoad); action = "open conn"; dir = HTTP_TRACE_RX; break; case HTTP_VALIDATE_CLOSE_CONN: count = (int) PTOL(mprLookupKey(endpoint->clientLoad, conn->ip)); if (count > 1) { mprAddKey(endpoint->clientLoad, conn->ip, ITOP(count - 1)); } else { mprRemoveKey(endpoint->clientLoad, conn->ip); } endpoint->clientCount = (int) mprGetHashLength(endpoint->clientLoad); action = "close conn"; dir = HTTP_TRACE_TX; break; case HTTP_VALIDATE_OPEN_REQUEST: mprAssert(conn->rx); if (endpoint->requestCount >= limits->requestMax) { unlock(http); httpError(conn, HTTP_CODE_SERVICE_UNAVAILABLE, "Server overloaded"); mprLog(2, "Too many concurrent requests %d/%d", endpoint->requestCount, limits->requestMax); return 0; } endpoint->requestCount++; conn->rx->flags |= HTTP_LIMITS_OPENED; action = "open request"; dir = HTTP_TRACE_RX; break; case HTTP_VALIDATE_CLOSE_REQUEST: if (conn->rx && conn->rx->flags & HTTP_LIMITS_OPENED) { /* Requests incremented only when conn->rx is assigned */ endpoint->requestCount--; mprAssert(endpoint->requestCount >= 0); action = "close request"; dir = HTTP_TRACE_TX; conn->rx->flags &= ~HTTP_LIMITS_OPENED; } break; case HTTP_VALIDATE_OPEN_PROCESS: if (http->processCount >= limits->processMax) { unlock(http); httpError(conn, HTTP_CODE_SERVICE_UNAVAILABLE, "Server overloaded"); mprLog(2, "Too many concurrent processes %d/%d", http->processCount, limits->processMax); return 0; } http->processCount++; action = "start process"; dir = HTTP_TRACE_RX; break; case HTTP_VALIDATE_CLOSE_PROCESS: http->processCount--; mprAssert(http->processCount >= 0); break; } if (event == HTTP_VALIDATE_CLOSE_CONN || event == HTTP_VALIDATE_CLOSE_REQUEST) { if ((level = httpShouldTrace(conn, dir, HTTP_TRACE_LIMITS, NULL)) >= 0) { LOG(4, "Validate request for %s. Active connections %d, active requests: %d/%d, active client IP %d/%d", action, mprGetListLength(http->connections), endpoint->requestCount, limits->requestMax, endpoint->clientCount, limits->clientMax); } } unlock(http); return 1; }
static HttpConn *openConnection(HttpConn *conn, cchar *url, struct MprSsl *ssl) { Http *http; HttpUri *uri; MprSocket *sp; char *ip; int port, rc, level; mprAssert(conn); http = conn->http; uri = httpCreateUri(url, HTTP_COMPLETE_URI); conn->tx->parsedUri = uri; if (*url == '/') { ip = (http->proxyHost) ? http->proxyHost : http->defaultClientHost; port = (http->proxyHost) ? http->proxyPort : http->defaultClientPort; } else { ip = (http->proxyHost) ? http->proxyHost : uri->host; port = (http->proxyHost) ? http->proxyPort : uri->port; } if (port == 0) { port = (uri->secure) ? 443 : 80; } if (conn && conn->sock) { if (--conn->keepAliveCount < 0 || port != conn->port || strcmp(ip, conn->ip) != 0 || uri->secure != (conn->sock->ssl != 0) || conn->sock->ssl != ssl) { httpCloseConn(conn); } else { mprLog(4, "Http: reusing keep-alive socket on: %s:%d", ip, port); } } if (conn->sock) { return conn; } if ((sp = mprCreateSocket()) == 0) { httpError(conn, HTTP_CODE_COMMS_ERROR, "Can't create socket for %s", url); return 0; } if ((rc = mprConnectSocket(sp, ip, port, 0)) < 0) { httpError(conn, HTTP_CODE_COMMS_ERROR, "Can't open socket on %s:%d", ip, port); return 0; } #if BIT_PACK_SSL /* Must be done even if using keep alive for repeat SSL requests */ if (uri->secure) { if (ssl == 0) { ssl = mprCreateSsl(); } if (mprUpgradeSocket(sp, ssl, 0) < 0) { conn->errorMsg = sp->errorMsg; return 0; } } #endif conn->sock = sp; conn->ip = sclone(ip); conn->port = port; conn->secure = uri->secure; conn->keepAliveCount = (conn->limits->keepAliveMax) ? conn->limits->keepAliveMax : -1; if ((level = httpShouldTrace(conn, HTTP_TRACE_RX, HTTP_TRACE_CONN, NULL)) >= 0) { mprLog(level, "### Outgoing connection from %s:%d to %s:%d", conn->ip, conn->port, conn->sock->ip, conn->sock->port); } return conn; }
PUBLIC void httpWriteHeaders(HttpQueue *q, HttpPacket *packet) { Http *http; HttpConn *conn; HttpTx *tx; HttpUri *parsedUri; MprKey *kp; MprBuf *buf; int level; assert(packet->flags == HTTP_PACKET_HEADER); conn = q->conn; http = conn->http; tx = conn->tx; buf = packet->content; if (tx->flags & HTTP_TX_HEADERS_CREATED) { return; } tx->flags |= HTTP_TX_HEADERS_CREATED; tx->responded = 1; if (conn->headersCallback) { /* Must be before headers below */ (conn->headersCallback)(conn->headersCallbackArg); } if (tx->flags & HTTP_TX_USE_OWN_HEADERS && !conn->error) { conn->keepAliveCount = 0; return; } setHeaders(conn, packet); if (conn->endpoint) { mprPutStringToBuf(buf, conn->protocol); mprPutCharToBuf(buf, ' '); mprPutIntToBuf(buf, tx->status); mprPutCharToBuf(buf, ' '); mprPutStringToBuf(buf, httpLookupStatus(http, tx->status)); } else { mprPutStringToBuf(buf, tx->method); mprPutCharToBuf(buf, ' '); parsedUri = tx->parsedUri; if (http->proxyHost && *http->proxyHost) { if (parsedUri->query && *parsedUri->query) { mprPutToBuf(buf, "http://%s:%d%s?%s %s", http->proxyHost, http->proxyPort, parsedUri->path, parsedUri->query, conn->protocol); } else { mprPutToBuf(buf, "http://%s:%d%s %s", http->proxyHost, http->proxyPort, parsedUri->path, conn->protocol); } } else { if (parsedUri->query && *parsedUri->query) { mprPutToBuf(buf, "%s?%s %s", parsedUri->path, parsedUri->query, conn->protocol); } else { mprPutStringToBuf(buf, parsedUri->path); mprPutCharToBuf(buf, ' '); mprPutStringToBuf(buf, conn->protocol); } } } if ((level = httpShouldTrace(conn, HTTP_TRACE_TX, HTTP_TRACE_FIRST, tx->ext)) >= mprGetLogLevel(tx)) { mprAddNullToBuf(buf); mprLog(level, " %s", mprGetBufStart(buf)); } mprPutStringToBuf(buf, "\r\n"); /* Output headers */ kp = mprGetFirstKey(conn->tx->headers); while (kp) { mprPutStringToBuf(packet->content, kp->key); mprPutStringToBuf(packet->content, ": "); if (kp->data) { mprPutStringToBuf(packet->content, kp->data); } mprPutStringToBuf(packet->content, "\r\n"); kp = mprGetNextKey(conn->tx->headers, kp); } /* By omitting the "\r\n" delimiter after the headers, chunks can emit "\r\nSize\r\n" as a single chunk delimiter */ if (tx->length >= 0 || tx->chunkSize <= 0) { mprPutStringToBuf(buf, "\r\n"); } if (tx->altBody) { /* Error responses are emitted here */ mprPutStringToBuf(buf, tx->altBody); httpDiscardQueueData(tx->queue[HTTP_QUEUE_TX]->nextQ, 0); } tx->headerSize = mprGetBufLength(buf); tx->flags |= HTTP_TX_HEADERS_CREATED; q->count += httpGetPacketLength(packet); }