Example #1
0
/*
 *  Process a socket readable event
 */
static void readEvent(MaConn *conn)
{
    MaPacket    *packet;
    MprBuf      *content;
    int         nbytes, len;

    do {
        if ((packet = getPacket(conn, &len)) == 0) {
            break;
        }
        mprAssert(len > 0);
        content = packet->content;
        nbytes = mprReadSocket(conn->sock, mprGetBufEnd(content), len);
        showRequest(content, nbytes, len);
       
        if (nbytes > 0) {
            mprAdjustBufEnd(content, nbytes);
            maProcessReadEvent(conn, packet);
        } else {
            if (mprIsSocketEof(conn->sock)) {
                conn->dedicated = 0;
                if (conn->request) {
                    maProcessReadEvent(conn, packet);
                }
            } else if (nbytes < 0) {
                maFailConnection(conn, MPR_HTTP_CODE_COMMS_ERROR, "Communications read error");
            }
        }
    } while (!conn->disconnected && conn->dedicated);
}
Example #2
0
/*
    Read data from the peer. This will use the existing conn->input packet or allocate a new packet if required to 
    hold the data. The number of bytes read is stored in conn->lastRead. SSL connections are traced.
    Socket error messages are stored in conn->errorMsg.
 */
static void readPeerData(HttpConn *conn)
{
    HttpPacket  *packet;
    ssize       size;

    if ((packet = getPacket(conn, &size)) != 0) {
        conn->lastRead = mprReadSocket(conn->sock, mprGetBufEnd(packet->content), size);
        if (conn->lastRead > 0) {
            mprAdjustBufEnd(packet->content, conn->lastRead);
        } else if (conn->lastRead < 0 && mprIsSocketEof(conn->sock)) {
            if (conn->state < HTTP_STATE_PARSED) {
                conn->error = 1;
                conn->rx->eof = 1;
            }
            conn->errorMsg = conn->sock->errorMsg ? conn->sock->errorMsg : sclone("Connection reset");
            conn->keepAliveCount = 0;
            conn->lastRead = 0;
            httpTrace(conn, "connection.close", "context", "msg:'%s'", conn->errorMsg);
        }
    }
}
Example #3
0
/*
 *  IO event handler. If multithreaded, this will be run by a worker thread. NOTE: a request is not typically permanently 
 *  assigned to a worker thread. Each io event may be serviced by a different worker thread. The exception is CGI
 *  requests which block to wait for the child to complete (Needed on some platforms that don't permit cross thread
 *  waiting).
 */
static int ioEvent(MaConn *conn, int mask)
{
    mprAssert(conn);

    lock(conn);
    conn->time = mprGetTime(conn);

    mprLog(conn, 7, "ioEvent for fd %d, mask %d\n", conn->sock->fd);
    if (mask & MPR_WRITABLE) {
        maProcessWriteEvent(conn);
    }
    if (mask & MPR_READABLE) {
        readEvent(conn);
    }
    conn->time = mprGetTime(conn);
    if (mprIsSocketEof(conn->sock) || conn->disconnected || conn->connectionFailed || 
            (conn->request == 0 && conn->keepAliveCount < 0)) {
        /*
         *  This will close the connection and free all connection resources. NOTE: we compare keepAliveCount with "< 0" 
         *  so that the client can have one more keep alive request. It should respond to the "Connection: close" and 
         *  thus initiate a client-led close. This reduces TIME_WAIT states on the server. Must unlock the connection 
         *  to allow pending callbacks to run and complete.
         */
        unlock(conn);
        maDestroyPipeline(conn);
        mprFree(conn->arena);
        return 1;
    }

    /*
     *  We allow read events even if the current request is not complete and does not have body data. The pipelined
     *  request will be buffered and be ready for when the current request completes.
     */
    maEnableConnEvents(conn, MPR_READABLE);
    unlock(conn);
    return 0;
}
Example #4
0
/*
    Wait for the connection to reach a given state.
    Should only be used on the client side.
    @param state Desired state. Set to zero if you want to wait for one I/O event.
    @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity
        and duration timeouts.
 */
PUBLIC int httpWait(HttpConn *conn, int state, MprTicks timeout)
{
    HttpLimits  *limits;
    MprTicks    delay, start;
    int64       dispatcherMark;
    int         justOne;

    limits = conn->limits;
    if (conn->endpoint) {
        assert(!conn->endpoint);
        return MPR_ERR_BAD_STATE;
    }
    if (conn->state <= HTTP_STATE_BEGIN) {
        return MPR_ERR_BAD_STATE;
    }
    if (state == 0) {
        /* Wait for just one I/O event */
        state = HTTP_STATE_FINALIZED;
        justOne = 1;
    } else {
        justOne = 0;
    }
    if (conn->error) {
        if (conn->state >= state) {
            return 0;
        }
        return MPR_ERR_BAD_STATE;
    }
    if (timeout < 0) {
        timeout = limits->requestTimeout;
    } else if (timeout == 0) {
        timeout = MPR_MAX_TIMEOUT;
    }
    if (state > HTTP_STATE_CONTENT) {
        httpFinalizeOutput(conn);
    }
    start = conn->http->now;
    dispatcherMark = mprGetEventMark(conn->dispatcher);
    while (conn->state < state && !conn->error && !mprIsSocketEof(conn->sock)) {
        if (httpRequestExpired(conn, -1)) {
            return MPR_ERR_TIMEOUT;
        }
        httpEnableConnEvents(conn);
        delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout));
        delay = max(delay, 0);
        mprWaitForEvent(conn->dispatcher, delay, dispatcherMark);
        if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) {
            break;
        }
        dispatcherMark = mprGetEventMark(conn->dispatcher);
    }
    if (conn->error) {
        return MPR_ERR_NOT_READY;
    }
    if (conn->state < state) {
        if (mprGetRemainingTicks(start, timeout) <= 0) {
            return MPR_ERR_TIMEOUT;
        }
        if (!justOne) {
            return MPR_ERR_CANT_READ;
        }
    }
    conn->lastActivity = conn->http->now;
    return 0;
}
Example #5
0
/*
    Wait for the connection to reach a given state.
    Should only be used on the client side.
    @param state Desired state. Set to zero if you want to wait for one I/O event.
    @param timeout Timeout in msec. If timeout is zero, wait forever. If timeout is < 0, use default inactivity
        and duration timeouts.
 */
PUBLIC int httpWait(HttpStream *stream, int state, MprTicks timeout)
{
    HttpLimits  *limits;
    MprTicks    delay, start;
    int64       dispatcherMark;
    int         justOne;

    limits = stream->limits;
    if (httpServerStream(stream)) {
        return MPR_ERR_BAD_STATE;
    }
    if (stream->state <= HTTP_STATE_BEGIN) {
        return MPR_ERR_BAD_STATE;
    }
    if (state == 0) {
        /* Wait for just one I/O event */
        state = HTTP_STATE_FINALIZED;
        justOne = 1;
    } else {
        justOne = 0;
    }
    if (stream->error) {
        if (stream->state >= state) {
            return 0;
        }
        return MPR_ERR_BAD_STATE;
    }
    if (timeout < 0) {
        timeout = limits->requestTimeout;
    } else if (timeout == 0) {
        timeout = MPR_MAX_TIMEOUT;
    }
    if (state > HTTP_STATE_CONTENT) {
        httpFinalizeOutput(stream);
    }
    start = stream->http->now;
    dispatcherMark = mprGetEventMark(stream->dispatcher);

    //  TODO - how does this work with http2?
    while (stream->state < state && !stream->error && !mprIsSocketEof(stream->sock)) {
        if (httpRequestExpired(stream, -1)) {
            return MPR_ERR_TIMEOUT;
        }
        //  TODO - review
        httpEnableNetEvents(stream->net);
        delay = min(limits->inactivityTimeout, mprGetRemainingTicks(start, timeout));
        delay = max(delay, 0);
        mprWaitForEvent(stream->dispatcher, delay, dispatcherMark);
        if (justOne || (mprGetRemainingTicks(start, timeout) <= 0)) {
            break;
        }
        dispatcherMark = mprGetEventMark(stream->dispatcher);
    }
    if (stream->error) {
        return MPR_ERR_NOT_READY;
    }
    if (stream->state < state) {
        if (mprGetRemainingTicks(start, timeout) <= 0) {
            return MPR_ERR_TIMEOUT;
        }
        if (!justOne) {
            return MPR_ERR_CANT_READ;
        }
    }
    stream->lastActivity = stream->http->now;
    return 0;
}
Example #6
0
/*
    Handle IO on the connection. Initially the conn->dispatcher will be set to the server->dispatcher and the first 
    I/O event will be handled on the server thread (or main thread). A request handler may create a new 
    conn->dispatcher and transfer execution to a worker thread if required.
 */
PUBLIC void httpIO(HttpConn *conn, int eventMask)
{
    MprSocket   *sp;

    sp = conn->sock;
    if (conn->destroyed) {
        /* Connection has been destroyed */
        return;
    }
    if (conn->state < HTTP_STATE_PARSED && mprShouldDenyNewRequests()) {
        httpDestroyConn(conn);
        return;
    }
    assert(conn->tx);
    assert(conn->rx);

#if DEPRECATED || 1
    /* Just IO state asserting */
    if (conn->io) {
        assert(!conn->io);
        return;
    }
    conn->io = 1;
#endif

    if ((eventMask & MPR_WRITABLE) && conn->connectorq) {
        httpResumeQueue(conn->connectorq);
    }
    if (eventMask & MPR_READABLE) {
        readPeerData(conn);
    }
    if (sp->secured && !conn->secure) {
        conn->secure = 1;
        if (sp->peerCert) {
            httpTrace(conn, "connection.ssl", "context", "msg:'Connection secured with peer certificate'," \
                "secure:true,cipher:'%s',peerName:'%s',subject:'%s',issuer:'%s',session:'%s'",
                sp->cipher, sp->peerName, sp->peerCert, sp->peerCertIssuer, sp->session);
        } else {
            httpTrace(conn, "connection.ssl", "context",
                "msg:'Connection secured without peer certificate',secure:true,cipher:'%s',session:'%s'",
                sp->cipher, sp->session);
        }
        if (mprGetLogLevel() >= 5) {
            mprLog("info http ssl", 5, "SSL State: %s", mprGetSocketState(sp));
        }
    }
    /*
        Process one or more complete requests in the packet
     */
    do {
        /* This is and must be the only place httpProtocol is ever called */
        httpProtocol(conn);
    } while (conn->endpoint && conn->state == HTTP_STATE_COMPLETE && prepForNext(conn));

    /*
        When a request completes, prepForNext will reset the state to HTTP_STATE_BEGIN
     */
    if (conn->state < HTTP_STATE_PARSED && conn->endpoint && (mprIsSocketEof(conn->sock) || (conn->keepAliveCount <= 0))) {
        httpDestroyConn(conn);
    } else if (!mprIsSocketEof(conn->sock) && conn->async && !conn->delay) {
        httpEnableConnEvents(conn);
    }
    conn->io = 0;
}