/* Write data back to the client (browser). Must be locked when called. */ static int writeToClient(MaQueue *q, MprCmd *cmd, MprBuf *buf, int channel) { MaConn *conn; int rc, len; conn = q->conn; /* Write to the browser. We write as much as we can. Service queues to get the filters and connectors pumping. */ while ((len = mprGetBufLength(buf)) > 0) { if (!conn->requestFailed) { rc = maWriteBlock(q, mprGetBufStart(buf), len, 1); mprLog(q, 5, "CGI: write %d bytes to client. Rc rc %d, errno %d", len, rc, mprGetOsError()); } else { /* Request has failed so just eat the data */ rc = len; mprAssert(len > 0); } if (rc < 0) { return MPR_ERR_CANT_WRITE; } mprAssert(rc == len); mprAdjustBufStart(buf, rc); mprResetBufIfEmpty(buf); maServiceQueues(conn); } return 0; }
/* * * Flush write data back to the client */ static void flushOutput(void *server_context) { MaConn *conn; conn = (MaConn*) server_context; if (conn) { maServiceQueues(conn); } }
/* * Invoke the run routine for the handler and then pump the pipeline by servicing all scheduled queues. */ bool maRunPipeline(MaConn *conn) { MaQueue *q; q = conn->response->queue[MA_QUEUE_SEND].nextQ; if (q->stage->run) { q->stage->run(q); } return maServiceQueues(conn); }
/* * Process a write event. These occur when a request could not be completed when it was first received. */ void maProcessWriteEvent(MaConn *conn) { mprLog(conn, 6, "maProcessWriteEvent, state %d", conn->state); if (unlikely(conn->expire <= conn->time)) { /* * Ignore the event if we have expired. */ return; } if (conn->response) { /* * Enable the queue upstream from the connector */ maEnableQueue(conn->response->queue[MA_QUEUE_SEND].prevQ); maServiceQueues(conn); if (conn->state == MPR_HTTP_STATE_COMPLETE) { maProcessCompletion(conn); } } }
/* * Process incoming requests. This will process as many requests as possible before returning. All socket I/O is * non-blocking, and this routine must not block. */ void maProcessReadEvent(MaConn *conn, MaPacket *packet) { mprAssert(conn); conn->canProceed = 1; mprLog(conn, 7, "ENTER maProcessReadEvent state %d, packet %p", conn->state, packet); while (conn->canProceed) { mprLog(conn, 7, "maProcessReadEvent, state %d, packet %p", conn->state, packet); switch (conn->state) { case MPR_HTTP_STATE_BEGIN: conn->canProceed = parseRequest(conn, packet); break; case MPR_HTTP_STATE_CONTENT: conn->canProceed = processContent(conn, packet); packet = conn->input; break; case MPR_HTTP_STATE_PROCESSING: conn->canProceed = maServiceQueues(conn); break; case MPR_HTTP_STATE_COMPLETE: conn->canProceed = maProcessCompletion(conn); packet = conn->input; break; default: conn->keepAliveCount = 0; mprAssert(0); return; } } mprLog(conn, 7, "LEAVE maProcessReadEvent state %d, packet %p, dedicated %d", conn->state, packet, conn->dedicated); }
/* * Drain a service queue by scheduling the queue and servicing all queues. Return true if there is room for more data. */ static bool drain(MaQueue *q, bool block) { MaConn *conn; MaQueue *next; int oldMode; conn = q->conn; /* * Queue is full. Need to drain the service queue if possible. */ do { oldMode = mprSetSocketBlockingMode(conn->sock, block); maScheduleQueue(q); next = q->nextQ; if (next->count >= next->max) { maScheduleQueue(next); } maServiceQueues(conn); mprSetSocketBlockingMode(conn->sock, oldMode); } while (block && q->count >= q->max); return (q->count < q->max) ? 1 : 0; }
/* * Process request body data (typically post or put content). Packet will be null if the client closed the * connection to signify end of data. */ static bool processContent(MaConn *conn, MaPacket *packet) { MaRequest *req; MaResponse *resp; MaQueue *q; MaHost *host; MprBuf *content; int64 remaining; int nbytes; req = conn->request; resp = conn->response; host = conn->host; q = &resp->queue[MA_QUEUE_RECEIVE]; mprAssert(packet); if (packet == 0) { return 0; } if (conn->connectionFailed) { conn->state = MPR_HTTP_STATE_PROCESSING; maPutForService(resp->queue[MA_QUEUE_SEND].nextQ, maCreateHeaderPacket(resp), 1); return 1; } content = packet->content; if (req->flags & MA_REQ_CHUNKED) { if ((remaining = getChunkPacketSize(conn, content)) == 0) { /* Need more data or bad chunk specification */ if (mprGetBufLength(content) > 0) { conn->input = packet; } return 0; } } else { remaining = req->remainingContent; } nbytes = (int) min(remaining, mprGetBufLength(content)); mprAssert(nbytes >= 0); mprLog(conn, 7, "processContent: packet of %d bytes, remaining %Ld", mprGetBufLength(content), remaining); if (maShouldTrace(conn, MA_TRACE_REQUEST | MA_TRACE_BODY)) { maTraceContent(conn, packet, 0, 0, MA_TRACE_REQUEST | MA_TRACE_BODY); } if (nbytes > 0) { mprAssert(maGetPacketLength(packet) > 0); remaining -= nbytes; req->remainingContent -= nbytes; req->receivedContent += nbytes; if (req->receivedContent >= host->limits->maxBody) { conn->keepAliveCount = 0; maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Request content body is too big %Ld vs limit %Ld", req->receivedContent, host->limits->maxBody); return 1; } if (packet == req->headerPacket) { /* Preserve headers if more data to come. Otherwise handlers may free the packet and destory the headers */ packet = maSplitPacket(resp, packet, 0); } else { mprStealBlock(resp, packet); } conn->input = 0; if (remaining == 0 && mprGetBufLength(packet->content) > nbytes) { /* * Split excess data belonging to the next pipelined request. */ mprLog(conn, 7, "processContent: Split packet of %d at %d", maGetPacketLength(packet), nbytes); conn->input = maSplitPacket(conn, packet, nbytes); mprAssert(mprGetParent(conn->input) == conn); } if ((q->count + maGetPacketLength(packet)) > q->max) { conn->keepAliveCount = 0; maFailConnection(q->conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Too much body data"); return 1; } maPutNext(q, packet); } else { conn->input = 0; mprStealBlock(resp, packet); } if (req->remainingContent == 0 || conn->requestFailed) { /* * End of input. Send a zero packet EOF signal and enable the handler send queue. */ if (req->remainingContent > 0 && conn->protocol > 0 && !conn->requestFailed) { maFailConnection(conn, MPR_HTTP_CODE_COMMS_ERROR, "Insufficient content data sent with request"); } else { maPutNext(q, maCreateEndPacket(resp)); conn->state = MPR_HTTP_STATE_PROCESSING; maRunPipeline(conn); } return 1; } maServiceQueues(conn); return conn->input ? mprGetBufLength(conn->input->content) : 0; }