static void startFileHandler(HttpQueue *q) { HttpConn *conn; HttpRx *rx; HttpTx *tx; HttpPacket *packet; conn = q->conn; rx = conn->rx; tx = conn->tx; if (tx->finalized || conn->error) { return; } else if (rx->flags & HTTP_PUT) { handlePutRequest(q); } else if (rx->flags & HTTP_DELETE) { handleDeleteRequest(q); } else if (rx->flags & HTTP_OPTIONS) { httpHandleOptions(q->conn); } else if (!(tx->flags & HTTP_TX_NO_BODY)) { /* Create a single data packet based on the entity length */ packet = httpCreateEntityPacket(0, tx->entityLength, readFileData); if (!tx->outputRanges) { /* Can set a content length */ tx->length = tx->entityLength; } /* Add to the output service queue */ httpPutForService(q, packet, 0); } }
/* Accept incoming body data from the client destined for the CGI gateway. This is typically POST or PUT data. Note: For POST "form" requests, this will be called before the command is actually started. */ static void browserToCgiData(HttpQueue *q, HttpPacket *packet) { HttpConn *conn; Cgi *cgi; assert(q); assert(packet); if ((cgi = q->queueData) == 0) { return; } conn = q->conn; assert(q == conn->readq); if (httpGetPacketLength(packet) == 0) { /* End of input */ if (conn->rx->remainingContent > 0) { /* Short incoming body data. Just kill the CGI process */ if (cgi->cmd) { mprDestroyCmd(cgi->cmd); } httpError(conn, HTTP_CODE_BAD_REQUEST, "Client supplied insufficient body data"); } } httpPutForService(cgi->writeq, packet, HTTP_SCHEDULE_QUEUE); }
PUBLIC void httpFinalizeOutput(HttpConn *conn) { HttpTx *tx; tx = conn->tx; if (!tx || tx->finalizedOutput) { return; } tx->responded = 1; tx->finalizedOutput = 1; assert(conn->writeq); if (conn->writeq == tx->queue[HTTP_QUEUE_TX]) { /* Tx Pipeline not yet created */ tx->pendingFinalize = 1; return; } assert(conn->state >= HTTP_STATE_CONNECTED); /* This may be called from httpError when the connection fails. */ if (conn->sock) { httpPutForService(conn->writeq, httpCreateEndPacket(), HTTP_SCHEDULE_QUEUE); httpServiceQueues(conn); } }
/* Put packets on the service queue. */ static void outgoing(HttpQueue *q, HttpPacket *packet) { int enableService; /* Handlers service routines must only be auto-enabled if better than ready. */ enableService = !(q->stage->flags & HTTP_STAGE_HANDLER) || (q->conn->state >= HTTP_STATE_READY) ? 1 : 0; httpPutForService(q, packet, enableService); }
static ssize doOutput(HttpQueue *q, cchar *data, ssize len) { HttpPacket *packet; ssize count; count = min(len, q->max - q->count); count = min(count, q->packetSize); packet = httpCreateDataPacket(count); mprPutBlockToBuf(packet->content, data, len); httpPutForService(q, packet, HTTP_SCHEDULE_QUEUE); return count; }
/* Incoming data routine. Simply transfer the data upstream to the next filter or handler. */ static void incoming(HttpQueue *q, HttpPacket *packet) { assert(q); assert(packet); if (q->nextQ->put) { httpPutPacketToNext(q, packet); } else { /* This queue is the last queue in the pipeline */ if (httpGetPacketLength(packet) > 0) { if (packet->flags & HTTP_PACKET_SOLO) { httpPutForService(q, packet, HTTP_DELAY_SERVICE); } else { httpJoinPacketForService(q, packet, 0); } } else { /* Zero length packet means eof */ httpPutForService(q, packet, HTTP_DELAY_SERVICE); } HTTP_NOTIFY(q->conn, HTTP_EVENT_READABLE, 0); } }
static void startFileHandler(HttpQueue *q) { HttpConn *conn; HttpRx *rx; HttpTx *tx; HttpPacket *packet; int beint; printf("\n startFileHandler \n"); conn = q->conn; rx = conn->rx; tx = conn->tx; assure(!tx->finalized); if (rx->flags & HTTP_PUT) { handlePutRequest(q); } else if (rx->flags & HTTP_DELETE) { handleDeleteRequest(q); } else if (!(tx->flags & HTTP_TX_NO_BODY)) { /* Create a single data packet based on the entity length */ packet = httpCreateEntityPacket(0, tx->entityLength, readFileData); if (!tx->outputRanges) { /* Can set a content length */ if(strcmp(tx->filename ,"C:\\Project\\appweb-4.2.0\\windows-x86-vsdebug\\web\\movie.mp4") == 0) { tx->cacheBuffer = 0; tx->length = 400000; } else { tx->length = tx->entityLength; } beint = tx->length; printf("\n--------------------\n"); printf("beint,tx->filename : %d \t +%s+",beint,tx->filename); printf("\n--------------------\n"); } /* Add to the output service queue */ httpPutForService(q, packet, 0); } }
/* Run the handler. This is called when all input data has been received. */ static void processSimple(HttpQueue *q) { HttpConn *conn; conn = q->conn; httpSetHeader(conn, 0, "Last-Modified", conn->http->currentDate); /* Create the empty header packet. This will be filled in by the downstream network connector stage. */ httpPutForService(q, httpCreateHeaderPacket(conn), 0); /* Generate some dynamic data. If you generate a lot, this will buffer up to a configured maximum. If that limit is exceeded, the packet will be sent downstream and the response headers will be created. */ httpWrite(q, "Hello World"); /* Send an end of data packet */ httpPutForService(q, httpCreateEndPacket(conn), 1); }
static void incomingEjs(HttpQueue *q, HttpPacket *packet) { HttpConn *conn; HttpRx *rx; conn = q->conn; rx = conn->rx; if (httpGetPacketLength(packet) == 0) { if (rx->remainingContent > 0) { httpError(conn, HTTP_CODE_BAD_REQUEST, "Client supplied insufficient body data"); } httpPutForService(q, packet, 0); } else { httpJoinPacketForService(q, packet, 0); } HTTP_NOTIFY(q->conn, HTTP_EVENT_READABLE, 0); }
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); }
static void cgiToBrowserData(HttpQueue *q, HttpPacket *packet) { httpPutForService(q->conn->writeq, packet, HTTP_SCHEDULE_QUEUE); }
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 : ""); } }
PUBLIC void httpDefaultIncoming(HttpQueue *q, HttpPacket *packet) { assert(q); assert(packet); httpPutForService(q, packet, HTTP_DELAY_SERVICE); }
/* Write a block of data. This is the lowest level write routine for data. This will buffer the data and flush if the queue buffer is full. Flushing is done by calling httpFlushQueue which will service queues as required. This may call the queue outgoing service routine and disable downstream queues if they are overfull. This routine will always accept the data and never return "short". */ PUBLIC ssize httpWriteBlock(HttpQueue *q, cchar *buf, ssize len, int flags) { HttpPacket *packet; HttpConn *conn; HttpTx *tx; ssize totalWritten, packetSize, thisWrite; assert(q == q->conn->writeq); conn = q->conn; tx = conn->tx; if (flags == 0) { flags = HTTP_BUFFER; } if (tx == 0 || tx->finalizedOutput) { return MPR_ERR_CANT_WRITE; } tx->responded = 1; for (totalWritten = 0; len > 0; ) { mprTrace(7, "httpWriteBlock q_count %d, q_max %d", q->count, q->max); if (conn->state >= HTTP_STATE_FINALIZED) { return MPR_ERR_CANT_WRITE; } if (q->last && q->last != q->first && q->last->flags & HTTP_PACKET_DATA && mprGetBufSpace(q->last->content) > 0) { packet = q->last; } else { packetSize = (tx->chunkSize > 0) ? tx->chunkSize : q->packetSize; if ((packet = httpCreateDataPacket(packetSize)) == 0) { return MPR_ERR_MEMORY; } httpPutForService(q, packet, HTTP_DELAY_SERVICE); } assert(mprGetBufSpace(packet->content) > 0); thisWrite = min(len, mprGetBufSpace(packet->content)); if (flags & (HTTP_BLOCK | HTTP_NON_BLOCK)) { thisWrite = min(thisWrite, q->max - q->count); } if (thisWrite > 0) { if ((thisWrite = mprPutBlockToBuf(packet->content, buf, thisWrite)) == 0) { return MPR_ERR_MEMORY; } buf += thisWrite; len -= thisWrite; q->count += thisWrite; totalWritten += thisWrite; } if (q->count >= q->max) { httpFlushQueue(q, 0); if (q->count >= q->max) { if (flags & HTTP_NON_BLOCK) { break; } else if (flags & HTTP_BLOCK) { while (q->count >= q->max && !tx->finalized) { if (!mprWaitForSingleIO((int) conn->sock->fd, MPR_WRITABLE, conn->limits->inactivityTimeout)) { return MPR_ERR_TIMEOUT; } httpResumeQueue(conn->connectorq); httpServiceQueues(conn); } } } } } if (conn->error) { return MPR_ERR_CANT_WRITE; } return totalWritten; }
static void outgoingProxyData(HttpQueue *q) { #if UNUSED httpPutForService(q, packet, 1); #endif }
static void incomingProxyData(HttpQueue *q, HttpPacket *packet) { httpPutForService(q, packet, 1); }
static void processProxy(HttpQueue *q) { int count = 50000; q->max = 65536; #if USE0 /* May overflow q->max */ while (sofar < count) { httpWrite(q, "%d aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", sofar++); } httpFinalizeOutput(q->conn); sofar = 0; #endif #if USE1 httpWrite(q, "%d aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", sofar++); if (sofar == count) { httpFinalizeOutput(q->conn); sofar = 0; } #endif #if USE2 while (sofar < count && q->count < (q->max * 3 / 4)) { /* NOTE: httpWrite may do internal flush if count > max */ httpWrite(q, "%d aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n", sofar++); } if (sofar == count) { httpFinalizeOutput(q->conn); sofar = 0; } #endif #if USE3 /* MANUAL FLOW CONTROL */ HttpPacket *packet; int i, size; size = 1024; for (; sofar < count && q->count < q->max; sofar++) { packet = httpCreateDataPacket(size); for (i = 1; i < size - 1; i++) { mprPutCharToBuf(packet->content, 'a'); } mprPutCharToBuf(packet->content, '\n'); httpPutForService(q, packet, HTTP_DELAY_SERVICE); } if (sofar == count) { httpFinalizeOutput(q->conn); sofar = 0; } #endif #if USE4 || 1 httpStealConn(q->conn); #endif }