Beispiel #1
0
void maSendPackets(MaQueue *q)
{
    MaPacket    *packet;

    for (packet = maGet(q); packet; packet = maGet(q)) {
        maPutNext(q, packet);
    }
}
Beispiel #2
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);
        }
    }
}
Beispiel #3
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;
}
Beispiel #4
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);
    }
}
Beispiel #5
0
void maSendEndPacket(MaQueue *q)
{
    maPutNext(q, maCreateEndPacket(q));
    q->flags |= MA_QUEUE_EOF;
}