Ejemplo n.º 1
0
/*
 *  Get the packet into which to read data. This may be owned by the connection or if mid-request, may be owned by the
 *  request. Also return in *bytesToRead the length of data to attempt to read.
 */
static inline MaPacket *getPacket(MaConn *conn, int *bytesToRead)
{
    MaPacket    *packet;
    MaRequest   *req;
    MprBuf      *content;
    MprOff      remaining;
    int         len;

    req = conn->request;
    len = MA_BUFSIZE;

    /*
     *  The input packet may have pipelined headers and data left from the prior request. It may also have incomplete
     *  chunk boundary data.
     */
    if ((packet = conn->input) == NULL) {
        conn->input = packet = maCreateConnPacket(conn, len);

    } else {
        content = packet->content;
        mprResetBufIfEmpty(content);
        if (req) {
            /*
             *  Don't read more than the remainingContent unless chunked. We do this to minimize requests split 
             *  across packets.
             */
            if (req->remainingContent) {
                remaining = req->remainingContent;
                if (req->flags & MA_REQ_CHUNKED) {
                    remaining = max(remaining, MA_BUFSIZE);
                }
                len = (int) min(remaining, MAXINT);
            }
            len = min(len, MA_BUFSIZE);
            mprAssert(len > 0);
            if (mprGetBufSpace(content) < len) {
                mprGrowBuf(content, len);
            }

        } else {
            /*
             *  Still reading the request headers
             */
            if (mprGetBufLength(content) >= conn->http->limits.maxHeader) {
                maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, "Header too big");
                return 0;
            }
            if (mprGetBufSpace(content) < MA_BUFSIZE) {
                mprGrowBuf(content, MA_BUFSIZE);
            }
            len = mprGetBufSpace(content);
        }
    }
    mprAssert(len > 0);
    *bytesToRead = len;
    return packet;
}
Ejemplo n.º 2
0
int mprPutBlockToBuf(MprBuf *bp, cchar *str, int size)
{
    int     thisLen, bytes, space;

    mprAssert(str);
    mprAssert(size >= 0);

    /*
        Add the max we can in one copy
     */
    bytes = 0;
    while (size > 0) {
        space = mprGetBufSpace(bp);
        thisLen = min(space, size);
        if (thisLen <= 0) {
            if (mprGrowBuf(bp, size) < 0) {
                break;
            }
            space = mprGetBufSpace(bp);
            thisLen = min(space, size);
        }

        memcpy(bp->end, str, thisLen);
        str += thisLen;
        bp->end += thisLen;
        size -= thisLen;
        bytes += thisLen;
    }
    if (bp->end < bp->endbuf) {
        *((char*) bp->end) = (char) '\0';
    }
    return bytes;
}
Ejemplo n.º 3
0
/*  
    Read the required number of bytes into the response content buffer. Count < 0 means transfer the entire content.
    Returns the number of bytes read. Returns null on EOF.
 */ 
static ssize readHttpData(Ejs *ejs, EjsHttp *hp, ssize count)
{
    MprBuf      *buf;
    HttpConn    *conn;
    ssize       len, space, nbytes;

    conn = hp->conn;
    buf = hp->responseContent;
    mprResetBufIfEmpty(buf);
    while (count < 0 || mprGetBufLength(buf) < count) {
        len = (count < 0) ? BIT_MAX_BUFFER : (count - mprGetBufLength(buf));
        space = mprGetBufSpace(buf);
        if (space < len) {
            mprGrowBuf(buf, len - space);
        }
        if ((nbytes = httpRead(conn, mprGetBufEnd(buf), len)) < 0) {
            ejsThrowIOError(ejs, "Cannot read required data");
            return MPR_ERR_CANT_READ;
        }
        mprAdjustBufEnd(buf, nbytes);
        if (hp->conn->async || (nbytes == 0 && conn->state > HTTP_STATE_CONTENT)) {
            break;
        }
    }
    if (count < 0) {
        return mprGetBufLength(buf);
    }
    return min(count, mprGetBufLength(buf));
}
Ejemplo n.º 4
0
/*
    Get the packet into which to read data. Return in *size the length of data to attempt to read.
 */
static HttpPacket *getPacket(HttpConn *conn, ssize *size)
{
    HttpPacket  *packet;
    MprBuf      *content;
    ssize       psize;

    if ((packet = conn->input) == NULL) {
        /*
            Boost the size of the packet if we have already read a largish amount of data
         */
        psize = (conn->rx && conn->rx->bytesRead > ME_MAX_BUFFER) ? ME_MAX_BUFFER * 8 : ME_MAX_BUFFER;
        conn->input = packet = httpCreateDataPacket(psize);
    } else {
        content = packet->content;
        mprResetBufIfEmpty(content);
        if (mprGetBufSpace(content) < ME_MAX_BUFFER && mprGrowBuf(content, ME_MAX_BUFFER) < 0) {
            conn->keepAliveCount = 0;
            conn->state = HTTP_STATE_BEGIN;
            return 0;
        }
    }
    *size = mprGetBufSpace(packet->content);
    assert(*size > 0);
    return packet;
}
Ejemplo n.º 5
0
/*
 *  Default callback routine for the mprRunCmd routines. Uses may supply their own callback instead of this routine. 
 *  The callback is run whenever there is I/O to read/write to the CGI gateway.
 */
static int cmdCallback(MprCmd *cmd, int channel, void *data)
{
    MprBuf      *buf;
    int         len, space;

    /*
     *  Note: stdin, stdout and stderr are named from the client's perspective
     */
    buf = 0;
    switch (channel) {
    case MPR_CMD_STDIN:
        return 0;
    case MPR_CMD_STDOUT:
        buf = cmd->stdoutBuf;
        break;
    case MPR_CMD_STDERR:
        buf = cmd->stderrBuf;
        break;
    }

    /*
     *  Read and aggregate the result into a single string
     */
    space = mprGetBufSpace(buf);
    if (space < (MPR_BUFSIZE / 4)) {
        if (mprGrowBuf(buf, MPR_BUFSIZE) < 0) {
            mprCloseCmdFd(cmd, channel);
            return 0;
        }
        space = mprGetBufSpace(buf);
    }

    len = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space);
    if (len <= 0) {
        if (len == 0 || (len < 0 && !(errno == EAGAIN || errno == EWOULDBLOCK))) {
            if (channel == MPR_CMD_STDOUT && cmd->flags & MPR_CMD_ERR) {
                /*
                 *  Now that stdout is complete, enable stderr to receive an EOF or any error output.
                 *  This is serialized to eliminate both stdin and stdout events on different threads at the same time.
                 *  Do before closing as the stderr event may come on another thread and we want to ensure avoid locking.
                 */
                mprCloseCmdFd(cmd, channel);
            } else {
                mprCloseCmdFd(cmd, channel);
            }
            return 0;
        }
    } else {
        mprAdjustBufEnd(buf, len);
    }
    return 0;
}
Ejemplo n.º 6
0
/*
 *  This appends a silent null. It does not count as one of the actual bytes in the buffer
 */
void mprAddNullToBuf(MprBuf *bp)
{
    int     space;

    space = (int) (bp->endbuf - bp->end);
    if (space < (int) sizeof(char)) {
        if (mprGrowBuf(bp, 1) < 0) {
            return;
        }
    }
    mprAssert(bp->end < bp->endbuf);
    if (bp->end < bp->endbuf) {
        *((char*) bp->end) = (char) '\0';
    }
}
Ejemplo n.º 7
0
Archivo: cmd.c Proyecto: embedthis/mpr
/*
    Default callback routine for the mprRunCmd routines. Uses may supply their own callback instead of this routine.
    The callback is run whenever there is I/O to read/write to the CGI gateway.
 */
static void defaultCmdCallback(MprCmd *cmd, int channel, void *data)
{
    MprBuf      *buf;
    ssize       len, space;
    int         errCode;

    /*
        Note: stdin, stdout and stderr are named from the client's perspective
     */
    buf = 0;
    switch (channel) {
    case MPR_CMD_STDIN:
        return;
    case MPR_CMD_STDOUT:
        buf = cmd->stdoutBuf;
        break;
    case MPR_CMD_STDERR:
        buf = cmd->stderrBuf;
        break;
    default:
        /* Child death notification */
        return;
    }
    /*
        Read and aggregate the result into a single string
     */
    space = mprGetBufSpace(buf);
    if (space < (ME_BUFSIZE / 4)) {
        if (mprGrowBuf(buf, ME_BUFSIZE) < 0) {
            mprCloseCmdFd(cmd, channel);
            return;
        }
        space = mprGetBufSpace(buf);
    }
    len = mprReadCmd(cmd, channel, mprGetBufEnd(buf), space);
    errCode = mprGetError();
    if (len <= 0) {
        if (len == 0 || (len < 0 && !(errCode == EAGAIN || errCode == EWOULDBLOCK))) {
            mprCloseCmdFd(cmd, channel);
            return;
        }
    } else {
        mprAdjustBufEnd(buf, len);
    }
    mprAddNullToBuf(buf);
    mprEnableCmdEvents(cmd, channel);
}
Ejemplo n.º 8
0
/*
 *  Set the current buffer size and maximum size limit.
 */
int mprSetBufSize(MprBuf *bp, int initialSize, int maxSize)
{
    mprAssert(bp);

    if (initialSize <= 0) {
        if (maxSize > 0) {
            bp->maxsize = maxSize;
        }
        return 0;
    }
    if (maxSize > 0 && initialSize > maxSize) {
        initialSize = maxSize;
    }
    mprAssert(initialSize > 0);

    if (bp->data) {
        /*
         *  Buffer already exists
         */
        if (bp->buflen < initialSize) {
            if (mprGrowBuf(bp, initialSize - bp->buflen) < 0) {
                return MPR_ERR_NO_MEMORY;
            }
        }
        bp->maxsize = maxSize;
        return 0;
    }

    /*
     *  New buffer - create storage for the data
     */
    if ((bp->data = mprAlloc(bp, initialSize)) == 0) {
        mprAssert(!MPR_ERR_NO_MEMORY);
        return MPR_ERR_NO_MEMORY;
    }
    bp->growBy = initialSize;
    bp->maxsize = maxSize;
    bp->buflen = initialSize;
    bp->endbuf = &bp->data[bp->buflen];
    bp->start = bp->data;
    bp->end = bp->data;
    *bp->start = '\0';
    return 0;
}
Ejemplo n.º 9
0
int mprPutCharToBuf(MprBuf *bp, int c)
{
    char    *cp;
    int     space;

    mprAssert(bp->buflen == (bp->endbuf - bp->data));

    space = bp->buflen - mprGetBufLength(bp);
    if (space < (int) sizeof(char)) {
        if (mprGrowBuf(bp, 1) < 0) {
            return MPR_ERR_NO_MEMORY;
        }
    }
    cp = (char*) bp->end;
    *cp++ = (char) c;
    bp->end = (char*) cp;

    if (bp->end < bp->endbuf) {
        *((char*) bp->end) = (char) '\0';
    }
    return 1;
}
Ejemplo n.º 10
0
static int getPostData(MprCtx ctx, MprBuf *buf)
{
    char    *contentLength;
    int     bytes, len, space;

    if ((contentLength = getenv("CONTENT_LENGTH")) != 0) {
        len = atoi(contentLength);
    } else {
        len = MAXINT;
    }
    while (len > 0) {
        space = mprGetBufSpace(buf);
        if (space < MPR_BUFSIZE) {
            if (mprGrowBuf(buf, MPR_BUFSIZE) < 0) {
                error(mpr, "Couldn't allocate memory to read post data");
                return -1;
            }
        }
        space = mprGetBufSpace(buf);
        bytes = (int) read(0, mprGetBufEnd(buf), space);
        if (bytes < 0) {
            error(mpr, "Couldn't read CGI input %d", errno);
            return -1;

        } else if (bytes == 0) {
            /* EOF */
            if (len > 0) {
                error(mpr, "Missing content (length %s)", contentLength);
            }
            break;
        }
        mprAdjustBufEnd(buf, bytes);
        len -= bytes;
    }
    mprAddNullToBuf(buf);
    return 0;
}
Ejemplo n.º 11
0
static void readFromCgi(Cgi *cgi, int channel)
{
    HttpConn    *conn;
    HttpPacket  *packet;
    HttpTx      *tx;
    HttpQueue   *q, *writeq;
    MprCmd      *cmd;
    ssize       nbytes;
    int         err;

    cmd = cgi->cmd;
    conn = cgi->conn;
    tx = conn->tx;
    q = cgi->readq;
    writeq = conn->writeq;
    assert(conn->sock);
    assert(conn->state > HTTP_STATE_BEGIN);

    if (tx->finalized) {
        mprCloseCmdFd(cmd, channel);
    }
    while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) {
        if ((packet = cgi->headers) != 0) {
            if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) {
                break;
            }
        } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) {
            break;
        }
        nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER);
        if (nbytes < 0) {
            err = mprGetError();
            if (err == EINTR) {
                continue;
            } else if (err == EAGAIN || err == EWOULDBLOCK) {
                break;
            }
            mprCloseCmdFd(cmd, channel);
            break;
            
        } else if (nbytes == 0) {
            mprCloseCmdFd(cmd, channel);
            break;

        } else {
            traceData(cmd, mprGetBufEnd(packet->content), nbytes);
            mprAdjustBufEnd(packet->content, nbytes);
        }
        if (channel == MPR_CMD_STDERR) {
            mprLog("error cgi", 0, "CGI failed uri=\"%s\", details: %s", conn->rx->uri, mprGetBufStart(packet->content));
            httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE);
            cgi->seenHeader = 1;
        }
        if (!cgi->seenHeader) {
            if (!parseCgiHeaders(cgi, packet)) {
                cgi->headers = packet;
                return;
            }
            cgi->headers = 0;
            cgi->seenHeader = 1;
        } 
        if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) {
            /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */
            httpPutPacket(q, packet);
        }
    }
}
Ejemplo n.º 12
0
static void cgiEvent(MaQueue *q, MprCmd *cmd, int channel)
{
    MaConn      *conn;
    MaResponse  *resp;
    MprBuf      *buf;
    int         space, nbytes, err;

    mprLog(cmd, 6, "CGI callback channel %d", channel);
    
    buf = 0;
    conn = q->conn;
    resp = conn->response;
    mprAssert(resp);

    cmd->lastActivity = mprGetTime(cmd);

    switch (channel) {
    case MPR_CMD_STDIN:
        writeToCGI(q->pair);
        return;

    case MPR_CMD_STDOUT:
        buf = cmd->stdoutBuf;
        break;

    case MPR_CMD_STDERR:
        buf = cmd->stderrBuf;
        break;
    }
    mprAssert(buf);
    mprResetBufIfEmpty(buf);

    /*
        Come here for CGI stdout, stderr events. ie. reading data from the CGI program.
     */
    while (mprGetCmdFd(cmd, channel) >= 0) {
        /*
            Read as much data from the CGI as possible
         */
        do {
            if ((space = mprGetBufSpace(buf)) == 0) {
                mprGrowBuf(buf, MA_BUFSIZE);
                if ((space = mprGetBufSpace(buf)) == 0) {
                    break;
                }
            }
            nbytes = mprReadCmdPipe(cmd, channel, mprGetBufEnd(buf), space);
            mprLog(q, 5, "CGI: read from gateway %d on channel %d. errno %d", nbytes, channel, 
                    nbytes >= 0 ? 0 : mprGetOsError());
            if (nbytes < 0) {
                err = mprGetError();
                if (err == EINTR) {
                    continue;
                } else if (err == EAGAIN || err == EWOULDBLOCK) {
                    break;
                }
                mprLog(cmd, 5, "CGI read error %d for %", mprGetError(), (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr");
                mprCloseCmdFd(cmd, channel);
                
            } else if (nbytes == 0) {
                /*
                    This may reap the terminated child and thus clear cmd->process if both stderr and stdout are closed.
                 */
                mprLog(cmd, 5, "CGI EOF for %s", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr");
                mprCloseCmdFd(cmd, channel);
                break;

            } else {
                mprLog(cmd, 5, "CGI read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr");
                mprAdjustBufEnd(buf, nbytes);
                traceData(cmd, mprGetBufStart(buf), nbytes);
            }
        } while ((space = mprGetBufSpace(buf)) > 0);

        if (mprGetBufLength(buf) == 0) {
            return;
        }
        if (channel == MPR_CMD_STDERR) {
            /*
                If we have an error message, send that to the client
             */
            if (mprGetBufLength(buf) > 0) {
                mprAddNullToBuf(buf);
                mprLog(conn, 4, mprGetBufStart(buf));
                if (writeToClient(q, cmd, buf, channel) < 0) {
                    return;
                }
                maSetResponseCode(conn, MPR_HTTP_CODE_SERVICE_UNAVAILABLE);
                cmd->userFlags |= MA_CGI_SEEN_HEADER;
                cmd->status = 0;
            }
        } else {
            if (!(cmd->userFlags & MA_CGI_SEEN_HEADER) && !parseHeader(conn, cmd)) {
                return;
            } 
            if (cmd->userFlags & MA_CGI_SEEN_HEADER) {
                if (writeToClient(q, cmd, buf, channel) < 0) {
                    return;
                }
            }
        }
    }
}
Ejemplo n.º 13
0
static void readFromCgi(Cgi *cgi, int channel)
{
    HttpConn    *conn;
    HttpPacket  *packet;
    HttpTx      *tx;
    HttpQueue   *q, *writeq;
    MprCmd      *cmd;
    ssize       nbytes;
    int         err;

    cmd = cgi->cmd;
    conn = cgi->conn;
    tx = conn->tx;
    q = cgi->readq;
    writeq = conn->writeq;
    assert(conn->sock);
    assert(conn->state > HTTP_STATE_BEGIN);

    if (tx->finalized) {
        mprCloseCmdFd(cmd, channel);
    }
    while (mprGetCmdFd(cmd, channel) >= 0 && !tx->finalized && writeq->count < writeq->max) {
        if ((packet = cgi->headers) != 0) {
            if (mprGetBufSpace(packet->content) < ME_MAX_BUFFER && mprGrowBuf(packet->content, ME_MAX_BUFFER) < 0) {
                break;
            }
        } else if ((packet = httpCreateDataPacket(ME_MAX_BUFFER)) == 0) {
            break;
        }
        nbytes = mprReadCmd(cmd, channel, mprGetBufEnd(packet->content), ME_MAX_BUFFER);
        if (nbytes < 0) {
            err = mprGetError();
            if (err == EINTR) {
                continue;
            } else if (err == EAGAIN || err == EWOULDBLOCK) {
                break;
            }
            mprTrace(6, "CGI: Gateway read error %d for %s", err, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr");
            mprCloseCmdFd(cmd, channel);
            break;
            
        } else if (nbytes == 0) {
            mprTrace(6, "CGI: Gateway EOF for %s, pid %d", (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr", cmd->pid);
            mprCloseCmdFd(cmd, channel);
            break;

        } else {
            mprTrace(6, "CGI: Gateway read %d bytes from %s", nbytes, (channel == MPR_CMD_STDOUT) ? "stdout" : "stderr");
            traceData(cmd, mprGetBufEnd(packet->content), nbytes);
            mprAdjustBufEnd(packet->content, nbytes);
        }
        if (channel == MPR_CMD_STDERR) {
            //  FUTURE - should be an option to keep going despite stderr output
            mprError("CGI: Error for \"%s\"\n\n%s", conn->rx->uri, mprGetBufStart(packet->content));
            httpSetStatus(conn, HTTP_CODE_SERVICE_UNAVAILABLE);
            cgi->seenHeader = 1;
        }
        if (!cgi->seenHeader) {
            if (!parseCgiHeaders(cgi, packet)) {
                cgi->headers = packet;
                return;
            }
            cgi->headers = 0;
            cgi->seenHeader = 1;
        } 
        if (!tx->finalizedOutput && httpGetPacketLength(packet) > 0) {
            /* Put the data to the CGI readq, then cgiToBrowserService will take care of it */
            httpPutPacket(q, packet);
        }
    }
}