示例#1
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Split a packet at a given offset and return a new packet containing the data after the offset.
 *  The suffix data migrates to the new packet. 
 */
MaPacket *maSplitPacket(MprCtx ctx, MaPacket *orig, int offset)
{
    MaPacket    *packet;
    int         count, size;

    if (orig->esize) {
        if ((packet = maCreateEntityPacket(ctx, orig->epos + offset, orig->esize - offset, orig->fill)) == 0) {
            return 0;
        }
        orig->esize = offset;

    } else {
        if (offset >= maGetPacketLength(orig)) {
            mprAssert(offset < maGetPacketLength(orig));
            return 0;
        }
        count = maGetPacketLength(orig) - offset;
        size = max(count, MA_BUFSIZE);
        size = MA_PACKET_ALIGN(size);
        if ((packet = maCreateDataPacket(ctx, size)) == 0) {
            return 0;
        }
        mprAdjustBufEnd(orig->content, -count);
        if (mprPutBlockToBuf(packet->content, mprGetBufEnd(orig->content), count) != count) {
            return 0;
        }
#if BLD_DEBUG
        mprAddNullToBuf(orig->content);
#endif
    }
    packet->flags = orig->flags;
    return packet;
}
示例#2
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  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);
    }
}
示例#3
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Return true if the next queue will accept this packet. If not, then disable the queue's service procedure.
 *  This may split the packet if it exceeds the downstreams maximum packet size.
 */
bool maWillNextQueueAccept(MaQueue *q, MaPacket *packet)
{
    MaQueue     *next;
    int64       size;

    next = q->nextQ;
    size = maGetPacketLength(packet);
    if (size <= next->packetSize && (size + next->count) <= next->max) {
        return 1;
    }
    if (maResizePacket(q, packet, 0) < 0) {
        return 0;
    }
    size = maGetPacketLength(packet);
    if (size <= next->packetSize && (size + next->count) <= next->max) {
        return 1;
    }

    /*
     *  The downstream queue is full, so disable the queue and mark the downstream queue as full and service immediately. 
     */
    maDisableQueue(q);
    next->flags |= MA_QUEUE_FULL;
    maScheduleQueue(next);
    return 0;
}
示例#4
0
/*
 *  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;
        }
    }
}
示例#5
0
static void setChunkPrefix(MaQueue *q, MaPacket *packet)
{
    if (packet->prefix) {
        return;
    }
    packet->prefix = mprCreateBuf(packet, 32, 32);
    /*
     *  NOTE: prefixes don't count in the queue length. No need to adjust q->count
     */
    if (maGetPacketLength(packet)) {
        mprPutFmtToBuf(packet->prefix, "\r\n%x\r\n", maGetPacketLength(packet));
    } else {
        mprPutStringToBuf(packet->prefix, "\r\n0\r\n\r\n");
    }
}
示例#6
0
/*
 *  Add a packet to the io vector. Return the number of bytes added to the vector.
 */
static void addPacketForSend(MaQueue *q, MaPacket *packet)
{
    MaResponse  *resp;
    MaConn      *conn;
    MprIOVec    *iovec;
    int         mask;

    conn = q->conn;
    resp = conn->response;
    iovec = q->iovec;
    
    mprAssert(q->count >= 0);
    mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2));

    if (packet->prefix) {
        addToSendVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix));
    }
    if (packet->esize > 0) {
        mprAssert(q->ioFile == 0);
        q->ioFile = 1;
        q->ioCount += packet->esize;

    } else if (maGetPacketLength(packet) > 0) {
        /*
         *  Header packets have actual content. File data packets are virtual and only have a count.
         */
        addToSendVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content));
        mask = (packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY;
        if (maShouldTrace(conn, mask)) {
            maTraceContent(conn, packet, 0, resp->bytesWritten, mask);
        }
    }
}
示例#7
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Remove all data from non-header packets in the queue. Don't worry about freeing. Will happen automatically at 
 *  the request end. See also maCleanQueue above.
 */
void maDiscardData(MaQueue *q, bool removePackets)
{
    MaPacket    *packet;
    int         len;

    if (q->first) {
        /*
         *  Skip the header packet
         */
        if (q->first->flags & MA_PACKET_HEADER) {
            packet = q->first->next;
        } else {
            packet = q->first;
        }

        /*
         *  Just flush each packet. Don't remove so the EOF packet is preserved
         */
        for (; packet; packet = packet->next) {
            if (packet->content) {
                len = maGetPacketLength(packet);
                q->conn->response->length -= len;
                q->count -= len;
                mprFlushBuf(packet->content);
            }
        }
        maCheckQueueCount(q);
    }
}
示例#8
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Split a packet if required so it fits in the downstream queue. Put back the 2nd portion of the split packet on the queue.
 *  Ensure that the packet is not larger than "size" if it is greater than zero.
 */
int maResizePacket(MaQueue *q, MaPacket *packet, int size)
{
    MaPacket    *tail;
    MaConn      *conn;
    MprCtx      ctx;
    int         len;
    
    conn = q->conn;
    if (size <= 0) {
        size = MAXINT;
    }
    ctx = conn->request ? (MprCtx) conn->request : (MprCtx) conn;

    if (packet->esize > size) {
        if ((tail = maSplitPacket(ctx, packet, size)) == 0) {
            return MPR_ERR_NO_MEMORY;
        }
    } else {
        /*
         *  Calculate the size that will fit
         */
        len = packet->content ? maGetPacketLength(packet) : 0;
        size = min(size, len);
        size = min(size, q->nextQ->max);
        size = min(size, q->nextQ->packetSize);
        if (size == 0 || size == len) {
            return 0;
        }
        if ((tail = maSplitPacket(ctx, packet, size)) == 0) {
            return MPR_ERR_NO_MEMORY;
        }
    }
    maPutBack(q, tail);
    return 0;
}
示例#9
0
/*
 *  Add a packet to the io vector. Return the number of bytes added to the vector.
 */
static void addPacketForNet(MaQueue *q, MaPacket *packet)
{
    MaResponse  *resp;
    MaConn      *conn;
    MprIOVec    *iovec;
    int         index, mask;

    conn = q->conn;
    resp = conn->response;
    iovec = q->iovec;
    index = q->ioIndex;

    mprAssert(q->count >= 0);
    mprAssert(q->ioIndex < (MA_MAX_IOVEC - 2));

    if (packet->prefix) {
        addToNetVector(q, mprGetBufStart(packet->prefix), mprGetBufLength(packet->prefix));
    }
    if (maGetPacketLength(packet) > 0) {
        addToNetVector(q, mprGetBufStart(packet->content), mprGetBufLength(packet->content));
    }
    mask = MA_TRACE_RESPONSE | ((packet->flags & MA_PACKET_HEADER) ? MA_TRACE_HEADERS : MA_TRACE_BODY);
    if (maShouldTrace(conn, mask)) {
        maTraceContent(conn, packet, 0, resp->bytesWritten, mask);
    }
}
示例#10
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  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);
}
示例#11
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Put the packet back at the front of the queue
 */
void maPutBack(MaQueue *q, MaPacket *packet)
{
    mprAssert(packet);
    mprAssert(packet->next == 0);
    
    packet->next = q->first;

    if (q->first == 0) {
        q->last = packet;
    }
    q->first = packet;

    mprAssert(maGetPacketLength(packet) >= 0);
    q->count += maGetPacketLength(packet);
    mprAssert(q->count >= 0);
    maCheckQueueCount(q);
}
示例#12
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  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;
}
示例#13
0
文件: queue.c 项目: gamman/appweb-3
void maCheckQueueCount(MaQueue *q)
{
    MaPacket    *packet;
    int64       count;

    count = 0;
    for (packet = q->first; packet; packet = packet->next) {
        count += maGetPacketLength(packet);
    }
    mprAssert(count == q->count);
}
示例#14
0
/*
 *  Build the IO vector. This connector uses the send file API which permits multiple IO blocks to be written with 
 *  file data. This is used to write transfer the headers and chunk encoding boundaries. Return the count of bytes to 
 *  be written.
 */
static int64 buildSendVec(MaQueue *q)
{
    MaConn      *conn;
    MaResponse  *resp;
    MaPacket    *packet;

    conn = q->conn;
    resp = conn->response;

    mprAssert(q->ioIndex == 0);
    q->ioCount = 0;
    q->ioFile = 0;

    /*
     *  Examine each packet and accumulate as many packets into the I/O vector as possible. Can only have one data packet at
     *  a time due to the limitations of the sendfile API (on Linux). And the data packet must be after all the 
     *  vector entries. Leave the packets on the queue for now, they are removed after the IO is complete for the 
     *  entire packet.
     */
    for (packet = q->first; packet; packet = packet->next) {
        if (packet->flags & MA_PACKET_HEADER) {
            maFillHeaders(conn, packet);
            q->count += maGetPacketLength(packet);

        } else if (maGetPacketLength(packet) == 0 && packet->esize == 0) {
            q->flags |= MA_QUEUE_EOF;
            if (packet->prefix == NULL) {
                break;
            }
        } else if (resp->flags & MA_RESP_NO_BODY) {
            maDiscardData(q, 0);
            continue;
        }
        if (q->ioFile || q->ioIndex >= (MA_MAX_IOVEC - 2)) {
            break;
        }
        addPacketForSend(q, packet);
    }
    return q->ioCount;
}
示例#15
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Join two packets by pulling the content from the second into the first.
 */
int maJoinPacket(MaPacket *packet, MaPacket *p)
{
    int     len;

    mprAssert(packet->esize == 0);
    mprAssert(p->esize == 0);

    len = maGetPacketLength(p);
    if (mprPutBlockToBuf(packet->content, mprGetBufStart(p->content), len) != len) {
        return MPR_ERR_NO_MEMORY;
    }
    return 0;
}
示例#16
0
/*
 *  Build the IO vector. Return the count of bytes to be written. Return -1 for EOF.
 */
static int64 buildNetVec(MaQueue *q)
{
    MaConn      *conn;
    MaResponse  *resp;
    MaPacket    *packet;

    conn = q->conn;
    resp = conn->response;

    /*
     *  Examine each packet and accumulate as many packets into the I/O vector as possible. Leave the packets on the queue 
     *  for now, they are removed after the IO is complete for the entire packet.
     */
    for (packet = q->first; packet; packet = packet->next) {
        if (packet->flags & MA_PACKET_HEADER) {
            if (resp->chunkSize <= 0 && q->count > 0 && resp->length < 0) {
                /* Incase no chunking filter and we've not seen all the data yet */
                conn->keepAliveCount = 0;
            }
            maFillHeaders(conn, packet);
            q->count += maGetPacketLength(packet);

        } else if (maGetPacketLength(packet) == 0) {
            q->flags |= MA_QUEUE_EOF;
            if (packet->prefix == NULL) {
                break;
            }
            
        } else if (resp->flags & MA_RESP_NO_BODY) {
            maDiscardData(q, 0);
            continue;
        }
        if (q->ioIndex >= (MA_MAX_IOVEC - 2)) {
            break;
        }
        addPacketForNet(q, packet);
    }
    return q->ioCount;
}
示例#17
0
/*
 *  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);
        }
    }
}
示例#18
0
/*
    Accept incoming body data from the client (via the pipeline) destined for the CGI gateway. This is typically
    POST or PUT data.
 */
static void incomingCgiData(MaQueue *q, MaPacket *packet)
{
    MaConn      *conn;
    MaResponse  *resp;
    MaRequest   *req;
    MprCmd      *cmd;

    mprAssert(q);
    mprAssert(packet);
    
    conn = q->conn;
    resp = conn->response;
    req = conn->request;
    cmd = (MprCmd*) q->pair->queueData;
    if (cmd) {
        cmd->lastActivity = mprGetTime(cmd);
    }
    if (maGetPacketLength(packet) == 0) {
        /*
            End of input
         */
        if (req->remainingContent > 0) {
            /*
                Short incoming body data. Just kill the CGI process.
             */
            mprFree(cmd);
            q->queueData = 0;
            maFailRequest(conn, MPR_HTTP_CODE_BAD_REQUEST, "Client supplied insufficient body data");
        }
        if (req->form) {
            maAddVarsFromQueue(req->formVars, q);
        }

    } else {
        /*
            No service routine, we just need it to be queued for writeToCGI
         */
        if (req->form) {
            maJoinForService(q, packet, 0);
        } else {
            maPutForService(q, packet, 0);
        }
    }
    if (cmd) {
        writeToCGI(q);
    }
}
示例#19
0
文件: queue.c 项目: gamman/appweb-3
/*
 *  Put a packet on the service queue.
 */
void maPutForService(MaQueue *q, MaPacket *packet, bool serviceQ)
{
    mprAssert(packet);
   
    maCheckQueueCount(q);
    q->count += maGetPacketLength(packet);
    packet->next = 0;
    
    if (q->first) {
        q->last->next = packet;
        q->last = packet;
        
    } else {
        q->first = packet;
        q->last = packet;
    }
    maCheckQueueCount(q);
    if (serviceQ && !(q->flags & MA_QUEUE_DISABLED))  {
        maScheduleQueue(q);
    }
}
示例#20
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);
    }
}
示例#21
0
文件: request.c 项目: gamman/appweb-3
/*
 *  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;
}