/* * Join a packet onto the service queue */ void maJoinForService(MaQueue *q, MaPacket *packet, bool serviceQ) { MaPacket *old; if (q->first == 0) { /* * Just use the service queue as a holding queue while we aggregate the post data. */ maPutForService(q, packet, 0); maCheckQueueCount(q); } else { maCheckQueueCount(q); q->count += maGetPacketLength(packet); if (q->first && maGetPacketLength(q->first) == 0) { old = q->first; packet = q->first->next; q->first = packet; maFreePacket(q, old); } else { /* * Aggregate all data into one packet and free the packet. */ maJoinPacket(q->first, packet); maCheckQueueCount(q); maFreePacket(q, packet); } } maCheckQueueCount(q); if (serviceQ && !(q->flags & MA_QUEUE_DISABLED)) { maScheduleQueue(q); } }
/* * Remove packets from a queue which do not need to be processed. * Remove data packets if no body is required (HEAD|TRACE|OPTIONS|PUT|DELETE method, not modifed content, or error) * This actually removes and frees the data packets whereas maDiscardData will just flush the data packets. */ void maCleanQueue(MaQueue *q) { MaConn *conn; MaResponse *resp; MaPacket *packet, *next, *prev; conn = q->conn; resp = conn->response; if (!(resp->flags & MA_RESP_NO_BODY)) { return; } for (prev = 0, packet = q->first; packet; packet = next) { next = packet->next; if (packet->flags & (MA_PACKET_RANGE | MA_PACKET_DATA)) { if (prev) { prev->next = next; } else { q->first = next; } q->count -= maGetPacketLength(packet); maFreePacket(q, packet); continue; } prev = packet; } maCheckQueueCount(q); }
/* * Clear entries from the IO vector that have actually been transmitted. This supports partial writes due to the socket * being full. Don't come here if we've seen all the packets and all the data has been completely written. ie. small files * don't come here. */ static void freeSentPackets(MaQueue *q, int64 bytes) { MaPacket *packet; int64 len; mprAssert(q->first); mprAssert(q->count >= 0); mprAssert(bytes >= 0); while ((packet = q->first) != 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = min(len, bytes); mprAdjustBufStart(packet->prefix, (int) len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { mprFree(packet->prefix); packet->prefix = 0; } } if (packet->esize) { len = min(packet->esize, bytes); packet->esize -= len; packet->epos += len; bytes -= len; mprAssert(packet->esize >= 0); mprAssert(bytes == 0); if (packet->esize > 0) { break; } } else if ((len = maGetPacketLength(packet)) > 0) { len = min(len, bytes); mprAdjustBufStart(packet->content, (int) len); bytes -= len; q->count -= (int) len; mprAssert(q->count >= 0); } if (maGetPacketLength(packet) == 0) { if ((packet = maGet(q)) != 0) { maFreePacket(q, packet); } } mprAssert(bytes >= 0); if (bytes == 0) { break; } } }
void maJoinPackets(MaQueue *q) { MaPacket *first, *packet, *next; if (q->first) { first = (q->first->flags & MA_PACKET_HEADER) ? q->first->next : q->first; for (packet = first->next; packet; packet = next) { next = packet->next; maJoinPacket(first, packet); maCheckQueueCount(q); maFreePacket(q, packet); } } }
static void freeNetPackets(MaQueue *q, int64 bytes) { MaPacket *packet; int len; mprAssert(q->first); mprAssert(q->count >= 0); mprAssert(bytes >= 0); while ((packet = q->first) != 0) { if (packet->prefix) { len = mprGetBufLength(packet->prefix); len = (int) min(len, bytes); mprAdjustBufStart(packet->prefix, len); bytes -= len; /* Prefixes don't count in the q->count. No need to adjust */ if (mprGetBufLength(packet->prefix) == 0) { mprFree(packet->prefix); packet->prefix = 0; } } if (packet->content) { len = mprGetBufLength(packet->content); len = (int) min(len, bytes); mprAdjustBufStart(packet->content, len); bytes -= len; q->count -= len; mprAssert(q->count >= 0); } if (packet->content == 0 || mprGetBufLength(packet->content) == 0) { /* * This will remove the packet from the queue and will re-enable upstream disabled queues. */ if ((packet = maGet(q)) != 0) { maFreePacket(q, packet); } } mprAssert(bytes >= 0); if (bytes == 0) { break; } } }
/* * Get the next packet from the queue */ MaPacket *maGet(MaQueue *q) { MaConn *conn; MaQueue *prev; MaPacket *packet; maCheckQueueCount(q); conn = q->conn; while (q->first) { if ((packet = q->first) != 0) { if (packet->flags & MA_PACKET_DATA && conn->requestFailed) { q->first = packet->next; q->count -= maGetPacketLength(packet); maFreePacket(q, packet); continue; } q->first = packet->next; packet->next = 0; q->count -= maGetPacketLength(packet); mprAssert(q->count >= 0); if (packet == q->last) { q->last = 0; mprAssert(q->first == 0); } } maCheckQueueCount(q); if (q->flags & MA_QUEUE_FULL && q->count < q->low) { /* * This queue was full and now is below the low water mark. Back-enable the previous queue. */ q->flags &= ~MA_QUEUE_FULL; prev = findPreviousQueue(q); if (prev && prev->flags & MA_QUEUE_DISABLED) { maEnableQueue(prev); } } maCheckQueueCount(q); return packet; } return 0; }
static void writeToCGI(MaQueue *q) { MaConn *conn; MaPacket *packet; MprCmd *cmd; MprBuf *buf; int len, rc, err; cmd = (MprCmd*) q->pair->queueData; mprAssert(cmd); conn = q->conn; for (packet = maGet(q); packet && !conn->requestFailed; packet = maGet(q)) { buf = packet->content; len = mprGetBufLength(buf); mprAssert(len > 0); rc = mprWriteCmdPipe(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len); mprLog(q, 5, "CGI: write %d bytes to gateway. Rc rc %d, errno %d", len, rc, mprGetOsError()); if (rc < 0) { err = mprGetError(); if (err == EINTR) { continue; } else if (err == EAGAIN || err == EWOULDBLOCK) { break; } mprLog(q, 2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError()); mprCloseCmdFd(cmd, MPR_CMD_STDIN); maFailRequest(conn, MPR_HTTP_CODE_BAD_GATEWAY, "Can't write body data to CGI gateway"); break; } else { mprLog(q, 5, "CGI: write to gateway %d bytes asked to write %d", rc, len); mprAdjustBufStart(buf, rc); if (mprGetBufLength(buf) > 0) { maPutBack(q, packet); } else { maFreePacket(q, packet); } } } }
/* * 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); } }