/* Initialize the send connector for a request */ PUBLIC int httpSendOpen(HttpQueue *q) { HttpConn *conn; HttpTx *tx; conn = q->conn; tx = conn->tx; if (tx->connector != conn->http->sendConnector) { httpAssignQueue(q, tx->connector, HTTP_QUEUE_TX); tx->connector->open(q); return 0; } if (!(tx->flags & HTTP_TX_NO_BODY)) { assert(tx->fileInfo.valid); if (tx->fileInfo.size > conn->limits->txBodySize && conn->limits->txBodySize < HTTP_UNLIMITED) { httpLimitError(conn, HTTP_ABORT | HTTP_CODE_REQUEST_TOO_LARGE, "Http transmission aborted. File size exceeds max body of %lld bytes", conn->limits->txBodySize); return MPR_ERR_CANT_OPEN; } tx->file = mprOpenFile(tx->filename, O_RDONLY | O_BINARY, 0); if (tx->file == 0) { httpError(conn, HTTP_CODE_NOT_FOUND, "Cannot open document: %s, err %d", tx->filename, mprGetError()); } } return 0; }
/* Create a queue associated with a connection. Prev may be set to the previous queue in a pipeline. If so, then the Conn.readq and writeq are updated. */ PUBLIC HttpQueue *httpCreateQueue(HttpConn *conn, HttpStage *stage, int dir, HttpQueue *prev) { HttpQueue *q; if ((q = mprAllocObj(HttpQueue, manageQueue)) == 0) { return 0; } q->conn = conn; httpInitQueue(conn, q, sfmt("%s-%s", stage->name, dir == HTTP_QUEUE_TX ? "tx" : "rx")); httpInitSchedulerQueue(q); httpAssignQueue(q, stage, dir); if (prev) { httpAppendQueue(prev, q); if (dir == HTTP_QUEUE_RX) { conn->readq = conn->tx->queue[HTTP_QUEUE_RX]->prevQ; } else { conn->writeq = conn->tx->queue[HTTP_QUEUE_TX]->nextQ; } } return q; }
PUBLIC void httpCreateTxPipeline(HttpConn *conn, HttpRoute *route) { Http *http; HttpTx *tx; HttpRx *rx; HttpQueue *q; HttpStage *stage, *filter; int next; assert(conn); assert(route); http = conn->http; rx = conn->rx; tx = conn->tx; tx->outputPipeline = mprCreateList(-1, MPR_LIST_STABLE); if (httpServerConn(conn)) { if (tx->handler == 0 || tx->finalized) { tx->handler = http->passHandler; } mprAddItem(tx->outputPipeline, tx->handler); } 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); tx->flags |= HTTP_TX_HAS_FILTERS; } } } if (tx->connector == 0) { #if !ME_ROM if (tx->handler == http->fileHandler && (rx->flags & HTTP_GET) && !(tx->flags & HTTP_TX_HAS_FILTERS) && !conn->secure && !httpTracing(conn)) { tx->connector = http->sendConnector; } else #endif tx->connector = (route && route->connector) ? route->connector : http->netConnector; } mprAddItem(tx->outputPipeline, tx->connector); /* 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 pipeline stages. This calls the open entrypoints on all stages. */ openQueues(conn); if (conn->error) { if (tx->handler != http->passHandler) { tx->handler = http->passHandler; httpAssignQueue(conn->writeq, tx->handler, HTTP_QUEUE_TX); } } tx->flags |= HTTP_TX_PIPELINE; if (conn->endpoint) { httpTrace(conn, "request.pipeline", "context", "route:'%s',handler:'%s',target:'%s',endpoint:'%s:%d',host:'%s',referrer:'%s',filename:'%s'", rx->route->pattern, tx->handler->name, rx->route->targetRule, conn->endpoint->ip, conn->endpoint->port, conn->host->name ? conn->host->name : "default", rx->referrer ? rx->referrer : "", tx->filename ? tx->filename : ""); } }