void maSendPackets(MaQueue *q) { MaPacket *packet; for (packet = maGet(q); packet; packet = maGet(q)) { maPutNext(q, packet); } }
/* * Apply chunks to dynamic outgoing data. */ static void outgoingChunkService(MaQueue *q) { MaConn *conn; MaPacket *packet; MaResponse *resp; conn = q->conn; resp = conn->response; if (!(q->flags & MA_QUEUE_SERVICED)) { /* * If the last packet is the end packet, we have all the data. Thus we know the actual content length * and can bypass the chunk handler. */ if (q->last->flags & MA_PACKET_END) { if (resp->chunkSize < 0 && resp->length <= 0) { /* * Set the response content length and thus disable chunking -- not needed as we know the entity length. */ resp->length = q->count; } } else { if (resp->chunkSize < 0 && resp->entityLength < 0) { resp->chunkSize = (int) min(conn->http->limits.maxChunkSize, q->max); } } } if (resp->chunkSize <= 0) { maDefaultOutgoingServiceStage(q); } else { for (packet = maGet(q); packet; packet = maGet(q)) { if (!(packet->flags & MA_PACKET_HEADER)) { if (maGetPacketLength(packet) > resp->chunkSize) { maResizePacket(q, packet, resp->chunkSize); } } if (!maWillNextQueueAccept(q, packet)) { maPutBack(q, packet); return; } if (!(packet->flags & MA_PACKET_HEADER)) { setChunkPrefix(q, packet); } maPutNext(q, packet); } } }
/* * 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; }
/* * Get the next chunk size. Chunked data format is: * Chunk spec <CRLF> * Data <CRLF> * Chunk spec (size == 0) <CRLF> * <CRLF> * Chunk spec is: "HEX_COUNT; chunk length DECIMAL_COUNT\r\n". The "; chunk length DECIMAL_COUNT is optional. * As an optimization, use "\r\nSIZE ...\r\n" as the delimiter so that the CRLF after data does not special consideration. * Achive this by parseHeaders reversing the input start by 2. */ static void incomingChunkData(MaQueue *q, MaPacket *packet) { MaConn *conn; MaRequest *req; MprBuf *buf; char *start, *cp; int bad; conn = q->conn; req = conn->request; buf = packet->content; mprAssert(req->flags & MA_REQ_CHUNKED); if (packet->content == 0) { if (req->chunkState == MA_CHUNK_DATA) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk state"); return; } req->chunkState = MA_CHUNK_EOF; } /* * NOTE: the request head ensures that packets are correctly sized by packet inspection. The packet will never * have more data than the chunk state expects. */ switch (req->chunkState) { case MA_CHUNK_START: /* * Validate: "\r\nSIZE.*\r\n" */ if (mprGetBufLength(buf) < 5) { break; } start = mprGetBufStart(buf); bad = (start[0] != '\r' || start[1] != '\n'); for (cp = &start[2]; cp < buf->end && *cp != '\n'; cp++) {} if (*cp != '\n' && (cp - start) < 80) { break; } bad += (cp[-1] != '\r' || cp[0] != '\n'); if (bad) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return; } req->chunkSize = (int) mprAtoi(&start[2], 16); if (!isxdigit((int) start[2]) || req->chunkSize < 0) { maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad chunk specification"); return; } mprAdjustBufStart(buf, (int) (cp - start + 1)); req->remainingContent = req->chunkSize; if (req->chunkSize == 0) { req->chunkState = MA_CHUNK_EOF; /* * We are lenient if the request does not have a trailing "\r\n" after the last chunk */ cp = mprGetBufStart(buf); if (mprGetBufLength(buf) == 2 && *cp == '\r' && cp[1] == '\n') { mprAdjustBufStart(buf, 2); } } else { req->chunkState = MA_CHUNK_DATA; } mprAssert(mprGetBufLength(buf) == 0); maFreePacket(q, packet); mprLog(q, 5, "chunkFilter: start incoming chunk of %d bytes", req->chunkSize); break; case MA_CHUNK_DATA: mprAssert(maGetPacketLength(packet) <= req->chunkSize); mprLog(q, 5, "chunkFilter: data %d bytes, req->remainingContent %d", maGetPacketLength(packet), req->remainingContent); maPutNext(q, packet); if (req->remainingContent == 0) { req->chunkState = MA_CHUNK_START; req->remainingContent = MA_BUFSIZE; } break; case MA_CHUNK_EOF: mprAssert(maGetPacketLength(packet) == 0); maPutNext(q, packet); mprLog(q, 5, "chunkFilter: last chunk"); break; default: mprAssert(0); } }
void maSendEndPacket(MaQueue *q) { maPutNext(q, maCreateEndPacket(q)); q->flags |= MA_QUEUE_EOF; }