예제 #1
0
/*
    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;
}
예제 #2
0
/*
 *
 *  Flush write data back to the client
 */
static void flushOutput(void *server_context)
{
    MaConn      *conn;

    conn = (MaConn*) server_context;
    if (conn) {
        maServiceQueues(conn);
    }
}
예제 #3
0
파일: pipeline.c 프로젝트: jsjohnst/appweb
/*
 *  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);
}
예제 #4
0
파일: request.c 프로젝트: gamman/appweb-3
/*
 *  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);
        }
    }
}
예제 #5
0
파일: request.c 프로젝트: gamman/appweb-3
/*
 *  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);
}
예제 #6
0
파일: queue.c 프로젝트: gamman/appweb-3
/*
 *  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;
}
예제 #7
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;
}