Exemple #1
0
static void freeNetPackets(HttpQueue *q, ssize bytes)
{
    HttpPacket    *packet;
    ssize         len;

    mprAssert(q->count >= 0);
    mprAssert(bytes >= 0);

    while (bytes > 0 && (packet = q->first) != 0) {
        if (packet->prefix) {
            len = mprGetBufLength(packet->prefix);
            len = min(len, bytes);
            mprAdjustBufStart(packet->prefix, len);
            bytes -= len;
            /* Prefixes don't count in the q->count. No need to adjust */
            if (mprGetBufLength(packet->prefix) == 0) {
                packet->prefix = 0;
            }
        }
        if (packet->content) {
            len = mprGetBufLength(packet->content);
            len = min(len, bytes);
            mprAdjustBufStart(packet->content, len);
            bytes -= len;
            q->count -= len;
            mprAssert(q->count >= 0);
        }
        if (packet->content == 0 || mprGetBufLength(packet->content) == 0) {
            /*
                This will remove the packet from the queue and will re-enable upstream disabled queues.
             */
            httpGetPacket(q);
        }
    }
}
Exemple #2
0
static void freeSendPackets(HttpQueue *q, MprOff bytes)
{
    HttpPacket  *packet;
    ssize       len;

    assert(q->first);
    assert(q->count >= 0);
    assert(bytes >= 0);

    /*
        Loop while data to be accounted for and we have not hit the end of data packet
        There should be 2-3 packets on the queue. A header packet for the HTTP response headers, an optional
        data packet with packet->esize set to the size of the file, and an end packet with no content.
        Must leave this routine with the end packet still on the queue and all bytes accounted for.
     */
    while ((packet = q->first) != 0 && !(packet->flags & HTTP_PACKET_END) && bytes > 0) {
        if (packet->prefix) {
            len = mprGetBufLength(packet->prefix);
            len = (ssize) min(len, bytes);
            mprAdjustBufStart(packet->prefix, len);
            bytes -= len;
            /* Prefixes don't count in the q->count. No need to adjust */
            if (mprGetBufLength(packet->prefix) == 0) {
                packet->prefix = 0;
            }
        }
        if (packet->esize) {
            len = (ssize) min(packet->esize, bytes);
            packet->esize -= len;
            packet->epos += len;
            bytes -= len;
            assert(packet->esize >= 0);

        } else if ((len = httpGetPacketLength(packet)) > 0) {
            /* Header packets come here */
            len = (ssize) min(len, bytes);
            mprAdjustBufStart(packet->content, len);
            bytes -= len;
            q->count -= len;
            assert(q->count >= 0);
        }
        if (packet->esize == 0 && httpGetPacketLength(packet) == 0) {
            /* Done with this packet - consume it */
            assert(!(packet->flags & HTTP_PACKET_END));
            httpGetPacket(q);
        } else {
            break;
        }
    }
    assert(bytes == 0);
}
Exemple #3
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;
        }
    }
}
Exemple #4
0
PUBLIC int mprFlushFile(MprFile *file)
{
    MprFileSystem   *fs;
    MprBuf          *bp;
    ssize           len, rc;

    assert(file);
    if (file == 0) {
        return MPR_ERR_BAD_HANDLE;
    }
    if (file->buf == 0) {
        return 0;
    }
    if (file->mode & (O_WRONLY | O_RDWR)) {
        fs = file->fileSystem;
        bp = file->buf;
        while (mprGetBufLength(bp) > 0) {
            len = mprGetBufLength(bp);
            rc = fs->writeFile(file, mprGetBufStart(bp), len);
            if (rc < 0) {
                return (int) rc;
            }
            mprAdjustBufStart(bp, rc);
        }
        mprFlushBuf(bp);
    }
    return 0;
}
Exemple #5
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;
}
Exemple #6
0
/*
    Read a line from the file. This will put the file into buffered mode.
    Return NULL on eof.
 */
PUBLIC char *mprReadLine(MprFile *file, ssize maxline, ssize *lenp)
{
    MprBuf          *bp;
    MprFileSystem   *fs;
    ssize           size, len, nlen, consumed;
    cchar           *eol, *newline, *start;
    char            *result;

    assert(file);

    if (file == 0) {
        return NULL;
    }
    if (lenp) {
        *lenp = 0;
    }
    if (maxline <= 0) {
        maxline = ME_MAX_BUFFER;
    }
    fs = file->fileSystem;
    newline = fs->newline;
    if (file->buf == 0) {
        file->buf = mprCreateBuf(maxline, maxline);
    }
    bp = file->buf;

    result = NULL;
    size = 0;
    do {
        if (mprGetBufLength(bp) == 0) {
            if (fillBuf(file) <= 0) {
                return result;
            }
        }
        start = mprGetBufStart(bp);
        len = mprGetBufLength(bp);
        if ((eol = findNewline(start, newline, len, &nlen)) != 0) {
            len = eol - start;
            consumed = len + nlen;
        } else {
            consumed = len;
        }
        file->pos += (MprOff) consumed;
        if (lenp) {
            *lenp += len;
        }
        if ((result = mprRealloc(result, size + len + 1)) == 0) {
            return NULL;
        }
        memcpy(&result[size], start, len);
        size += len;
        result[size] = '\0';
        mprAdjustBufStart(bp, consumed);
    } while (!eol);

    return result;
}
Exemple #7
0
void maAdjustPacketStart(MaPacket *packet, MprOff size)
{
    if (packet->esize) {
        packet->epos += size;
        packet->esize -= size;
    } else if (packet->content) {
        mprAdjustBufStart(packet->content, (int) size);
    }
}
Exemple #8
0
static void freeNetPackets(MaQueue *q, int64 bytes)
{
    MaPacket    *packet;
    int         len;

    mprAssert(q->first);
    mprAssert(q->count >= 0);
    mprAssert(bytes >= 0);

    while ((packet = q->first) != 0) {
        if (packet->prefix) {
            len = mprGetBufLength(packet->prefix);
            len = (int) min(len, bytes);
            mprAdjustBufStart(packet->prefix, 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->content) {
            len = mprGetBufLength(packet->content);
            len = (int) min(len, bytes);
            mprAdjustBufStart(packet->content, len);
            bytes -= len;
            q->count -= len;
            mprAssert(q->count >= 0);
        }
        if (packet->content == 0 || mprGetBufLength(packet->content) == 0) {
            /*
             *  This will remove the packet from the queue and will re-enable upstream disabled queues.
             */
            if ((packet = maGet(q)) != 0) {
                maFreePacket(q, packet);
            }
        }
        mprAssert(bytes >= 0);
        if (bytes == 0) {
            break;
        }
    }
}
Exemple #9
0
static void browserToCgiService(HttpQueue *q)
{
    HttpConn    *conn;
    HttpPacket  *packet;
    Cgi         *cgi;
    MprCmd      *cmd;
    MprBuf      *buf;
    ssize       rc, len;
    int         err;

    if ((cgi = q->queueData) == 0) {
        return;
    }
    assert(q == cgi->writeq);
    cmd = cgi->cmd;
    assert(cmd);
    conn = cgi->conn;

    for (packet = httpGetPacket(q); packet; packet = httpGetPacket(q)) {
        if ((buf = packet->content) == 0) {
            /* End packet */
            continue;
        }
        len = mprGetBufLength(buf);
        rc = mprWriteCmd(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len);
        if (rc < 0) {
            err = mprGetError();
            if (err == EINTR) {
                continue;
            } else if (err == EAGAIN || err == EWOULDBLOCK) {
                httpPutBackPacket(q, packet);
                break;
            }
            mprLog(2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError());
            mprCloseCmdFd(cmd, MPR_CMD_STDIN);
            httpDiscardQueueData(q, 1);
            httpError(conn, HTTP_CODE_BAD_GATEWAY, "Cannot write body data to CGI gateway");
            break;
        }
        mprTrace(6, "CGI: browserToCgiService %d/%d, qmax %d", rc, len, q->max);
        mprAdjustBufStart(buf, rc);
        if (mprGetBufLength(buf) > 0) {
            httpPutBackPacket(q, packet);
            break;
        }
    }
    if (q->count > 0) {
        /* Wait for writable event so cgiCallback can recall this routine */
        mprEnableCmdEvents(cmd, MPR_CMD_STDIN);
    } else if (conn->rx->eof) {
        mprCloseCmdFd(cmd, MPR_CMD_STDIN);
    } else {
        mprDisableCmdEvents(cmd, MPR_CMD_STDIN);
    }
}
Exemple #10
0
/*
 *  Flush all output and emit headers first if required
 */
static void flushOutput(MprBuf *buf)
{
    int     rc, len;

    if (!headersEmitted && buf != headerOutput) {
        emitHeaders();
    }

    while ((len = mprGetBufLength(buf)) > 0) {
        rc = (int) write(1, mprGetBufStart(buf), len);
        if (rc < 0) {
            return;
        }
        mprAdjustBufStart(buf, rc);
    }
    mprFlushBuf(buf);
}
Exemple #11
0
/*  
    function read(buffer: ByteArray, offset: Number = 0, count: Number = -1): Number
    Returns a count of bytes read. Non-blocking if a callback is defined. Otherwise, blocks.

    Offset: -1 then read to the buffer write position, >= 0 then read to that offset
    count: -1 then read as much as the buffer will hold. If buffer is growable, read all content. If not growable, 
        read the buffer size. If count >= 0, then read that number of bytes.
 */
static EjsNumber *http_read(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv)
{
    EjsByteArray    *buffer;
    HttpConn        *conn;
    MprOff          contentLength;
    ssize           offset, count;

    conn = hp->conn;
    buffer = (EjsByteArray*) argv[0];
    offset = (argc >= 2) ? ejsGetInt(ejs, argv[1]) : 0;
    count = (argc >= 3) ? ejsGetInt(ejs, argv[2]): -1;

    if (!waitForResponseHeaders(hp)) {
        return 0;
    }
    contentLength = httpGetContentLength(conn);
    if (conn->state >= HTTP_STATE_PARSED && contentLength == hp->readCount) {
        /* End of input */
        return ESV(null);
    }
    if (offset < 0) {
        offset = buffer->writePosition;
    } else if (offset < buffer->size) {
        ejsSetByteArrayPositions(ejs, buffer, offset, offset);
    } else {
        ejsThrowOutOfBoundsError(ejs, "Bad read offset value");
        return 0;
    }
    if (count < 0 && !buffer->resizable) {
        count = buffer->size - offset;
    }
    if ((count = readHttpData(ejs, hp, count)) < 0) {
        assert(ejs->exception);
        return 0;
    } else if (count == 0 && conn->state > HTTP_STATE_CONTENT) {
        return ESV(null);
    }
    hp->readCount += count;
    if (ejsCopyToByteArray(ejs, buffer, offset, (char*) mprGetBufStart(hp->responseContent), count) != count) {
        ejsThrowMemoryError(ejs);
    }
    ejsSetByteArrayPositions(ejs, buffer, -1, buffer->writePosition + count);
    mprAdjustBufStart(hp->responseContent, count);
    mprResetBufIfEmpty(hp->responseContent);
    return ejsCreateNumber(ejs, (MprNumber) count);
}
Exemple #12
0
static void writeToCGI(MaQueue *q)
{
    MaConn      *conn;
    MaPacket    *packet;
    MprCmd      *cmd;
    MprBuf      *buf;
    int         len, rc, err;

    cmd = (MprCmd*) q->pair->queueData;
    mprAssert(cmd);
    conn = q->conn;

    for (packet = maGet(q); packet && !conn->requestFailed; packet = maGet(q)) {
        buf = packet->content;
        len = mprGetBufLength(buf);
        mprAssert(len > 0);
        rc = mprWriteCmdPipe(cmd, MPR_CMD_STDIN, mprGetBufStart(buf), len);
        mprLog(q, 5, "CGI: write %d bytes to gateway. Rc rc %d, errno %d", len, rc, mprGetOsError());
        if (rc < 0) {
            err = mprGetError();
            if (err == EINTR) {
                continue;
            } else if (err == EAGAIN || err == EWOULDBLOCK) {
                break;
            }
            mprLog(q, 2, "CGI: write to gateway failed for %d bytes, rc %d, errno %d", len, rc, mprGetOsError());
            mprCloseCmdFd(cmd, MPR_CMD_STDIN);
            maFailRequest(conn, MPR_HTTP_CODE_BAD_GATEWAY, "Can't write body data to CGI gateway");
            break;

        } else {
            mprLog(q, 5, "CGI: write to gateway %d bytes asked to write %d", rc, len);
            mprAdjustBufStart(buf, rc);
            if (mprGetBufLength(buf) > 0) {
                maPutBack(q, packet);
            } else {
                maFreePacket(q, packet);
            }
        }
    }
}
Exemple #13
0
/*  
    function readString(count: Number = -1): String
    Read count bytes (default all) of content as a string. This always starts at the first character of content.
 */
static EjsString *http_readString(Ejs *ejs, EjsHttp *hp, int argc, EjsObj **argv)
{
    EjsString   *result;
    ssize       count;
    
    count = (argc == 1) ? ejsGetInt(ejs, argv[0]) : -1;
    if (!waitForState(hp, HTTP_STATE_CONTENT, -1, 1)) {
        return 0;
    }
    if ((count = readHttpData(ejs, hp, count)) < 0) {
        assert(ejs->exception);
        return 0;
    } else if (count == 0 && hp->conn->state > HTTP_STATE_CONTENT) {
        return ESV(null);
    }
    //  UNICODE ENCODING
    result = ejsCreateStringFromMulti(ejs, mprGetBufStart(hp->responseContent), count);
    mprAdjustBufStart(hp->responseContent, count);
    mprResetBufIfEmpty(hp->responseContent);
    return result;
}
Exemple #14
0
PUBLIC ssize mprReadFile(MprFile *file, void *buf, ssize size)
{
    MprFileSystem   *fs;
    MprBuf          *bp;
    ssize           bytes, totalRead;
    void            *bufStart;

    assert(file);
    if (file == 0) {
        return MPR_ERR_BAD_HANDLE;
    }
    fs = file->fileSystem;
    bp = file->buf;
    if (bp == 0) {
        totalRead = fs->readFile(file, buf, size);

    } else {
        bufStart = buf;
        while (size > 0) {
            if (mprGetBufLength(bp) == 0) {
                bytes = fillBuf(file);
                if (bytes <= 0) {
                    return -1;
                }
            }
            bytes = min(size, mprGetBufLength(bp));
            memcpy(buf, mprGetBufStart(bp), bytes);
            mprAdjustBufStart(bp, bytes);
            buf = (void*) (((char*) buf) + bytes);
            size -= bytes;
        }
        totalRead = ((char*) buf - (char*) bufStart);
    }
    file->pos += (MprOff) totalRead;
    return totalRead;
}
Exemple #15
0
/*
 *  Parse the request headers. Return true if the header parsed.
 */
static bool parseHeaders(MaConn *conn, MaPacket *packet)
{
    MaHostAddress   *address;
    MaRequest       *req;
    MaHost          *host, *hp;
    MaLimits        *limits;
    MprBuf          *content;
    char            keyBuf[MPR_MAX_STRING];
    char            *key, *value, *cp, *tok;
    int             count, keepAlive;

    req = conn->request;
    host = req->host;
    content = packet->content;
    conn->request->headerPacket = packet;
    limits = &conn->http->limits;
    keepAlive = 0;

    strcpy(keyBuf, "HTTP_");
    mprAssert(strstr((char*) content->start, "\r\n"));

    for (count = 0; content->start[0] != '\r' && !conn->connectionFailed; count++) {

        if (count >= limits->maxNumHeaders) {
            maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Too many headers");
            return 0;
        }
        if ((key = getToken(conn, ":")) == 0 || *key == '\0') {
            maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header format");
            return 0;
        }

        value = getToken(conn, "\r\n");
        while (isspace((int) *value)) {
            value++;
        }
        if (conn->requestFailed) {
            continue;
        }
        mprStrUpper(key);
        for (cp = key; *cp; cp++) {
            if (*cp == '-') {
                *cp = '_';
            }
        }
        mprLog(req, 8, "Key %s, value %s", key, value);
        if (strspn(key, "%<>/\\") > 0) {
            maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad header key value");
            continue;
        }

        /*
         *  Define the header with a "HTTP_" prefix
         */
        mprStrcpy(&keyBuf[5], sizeof(keyBuf) - 5, key);
        mprAddDuplicateHash(req->headers, keyBuf, value);

        switch (key[0]) {
        case 'A':
            if (strcmp(key, "AUTHORIZATION") == 0) {
                value = mprStrdup(req, value);
                req->authType = mprStrTok(value, " \t", &tok);
                req->authDetails = tok;

            } else if (strcmp(key, "ACCEPT_CHARSET") == 0) {
                req->acceptCharset = value;

            } else if (strcmp(key, "ACCEPT") == 0) {
                req->accept = value;

            } else if (strcmp(key, "ACCEPT_ENCODING") == 0) {
                req->acceptEncoding = value;
            }
            break;

        case 'C':
            if (strcmp(key, "CONTENT_LENGTH") == 0) {
                if (req->length >= 0) {
                    maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Mulitple content length headers");
                    continue;
                }
                req->length = mprAtoi(value, 10);
                if (req->length < 0) {
                    maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad content length");
                    continue;
                }
                if (req->length >= host->limits->maxBody) {
                    maFailConnection(conn, MPR_HTTP_CODE_REQUEST_TOO_LARGE, 
                        "Request content length %Ld is too big. Limit %Ld", req->length, host->limits->maxBody);
                    continue;
                }
                mprAssert(req->length >= 0);
                req->remainingContent = req->length;
                req->contentLengthStr = value;

            } else if (strcmp(key, "CONTENT_RANGE") == 0) {
                /*
                 *  This headers specifies the range of any posted body data
                 *  Format is:  Content-Range: bytes n1-n2/length
                 *  Where n1 is first byte pos and n2 is last byte pos
                 */
                char    *sp;
                int     start, end, size;

                start = end = size = -1;
                sp = value;
                while (*sp && !isdigit((int) *sp)) {
                    sp++;
                }
                if (*sp) {
                    start = (int) mprAtoi(sp, 10);

                    if ((sp = strchr(sp, '-')) != 0) {
                        end = (int) mprAtoi(++sp, 10);
                    }
                    if ((sp = strchr(sp, '/')) != 0) {
                        /*
                         *  Note this is not the content length transmitted, but the original size of the input of which 
                         *  the client is transmitting only a portion.
                         */
                        size = (int) mprAtoi(++sp, 10);
                    }
                }
                if (start < 0 || end < 0 || size < 0 || end <= start) {
                    maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad content range");
                    continue;
                }
                req->inputRange = maCreateRange(conn, start, end);

            } else if (strcmp(key, "CONTENT_TYPE") == 0) {
                req->mimeType = value;
                req->form = strstr(value, "application/x-www-form-urlencoded") != 0;

            } else if (strcmp(key, "COOKIE") == 0) {
                if (req->cookie && *req->cookie) {
                    req->cookie = mprStrcat(req, -1, req->cookie, "; ", value, NULL);
                } else {
                    req->cookie = value;
                }

            } else if (strcmp(key, "CONNECTION") == 0) {
                req->connection = value;
                if (mprStrcmpAnyCase(value, "KEEP-ALIVE") == 0) {
                    keepAlive++;

                } else if (mprStrcmpAnyCase(value, "CLOSE") == 0) {
                    conn->keepAliveCount = 0;
                }
                if (!host->keepAlive) {
                    conn->keepAliveCount = 0;
                }
            }
            break;

        case 'F':
            req->forwarded = value;
            break;

        case 'H':
            if (strcmp(key, "HOST") == 0) {
                req->hostName = value;
                address = conn->address;
                if (maIsNamedVirtualHostAddress(address)) {
                    hp = maLookupVirtualHost(address, value);
                    if (hp == 0) {
                        maFailRequest(conn, 404, "No host to serve request. Searching for %s", value);
                        mprLog(conn, 1, "Can't find virtual host %s", value);
                        continue;
                    }
                    req->host = hp;
                    /*
                     *  Reassign this request to a new host
                     */
                    maRemoveConn(host, conn);
                    host = hp;
                    conn->host = hp;
                    maAddConn(hp, conn);
                }
            }
            break;

        case 'I':
            if ((strcmp(key, "IF_MODIFIED_SINCE") == 0) || (strcmp(key, "IF_UNMODIFIED_SINCE") == 0)) {
                MprTime     newDate = 0;
                char        *cp;
                bool        ifModified = (key[3] == 'M');

                if ((cp = strchr(value, ';')) != 0) {
                    *cp = '\0';
                }
                if (mprParseTime(conn, &newDate, value, MPR_UTC_TIMEZONE, NULL) < 0) {
                    mprAssert(0);
                    break;
                }
                if (newDate) {
                    setIfModifiedDate(conn, newDate, ifModified);
                    req->flags |= MA_REQ_IF_MODIFIED;
                }

            } else if ((strcmp(key, "IF_MATCH") == 0) || (strcmp(key, "IF_NONE_MATCH") == 0)) {
                char    *word, *tok;
                bool    ifMatch = key[3] == 'M';

                if ((tok = strchr(value, ';')) != 0) {
                    *tok = '\0';
                }
                req->ifMatch = ifMatch;
                req->flags |= MA_REQ_IF_MODIFIED;

                value = mprStrdup(conn, value);
                word = mprStrTok(value, " ,", &tok);
                while (word) {
                    addMatchEtag(conn, word);
                    word = mprStrTok(0, " ,", &tok);
                }

            } else if (strcmp(key, "IF_RANGE") == 0) {
                char    *word, *tok;

                if ((tok = strchr(value, ';')) != 0) {
                    *tok = '\0';
                }
                req->ifMatch = 1;
                req->flags |= MA_REQ_IF_MODIFIED;

                value = mprStrdup(conn, value);
                word = mprStrTok(value, " ,", &tok);
                while (word) {
                    addMatchEtag(conn, word);
                    word = mprStrTok(0, " ,", &tok);
                }
            }
            break;

        case 'P':
            if (strcmp(key, "PRAGMA") == 0) {
                req->pragma = value;
            }
            break;

        case 'R':
            if (strcmp(key, "RANGE") == 0) {
                if (!parseRange(conn, value)) {
                    maFailRequest(conn, MPR_HTTP_CODE_RANGE_NOT_SATISFIABLE, "Bad range");
                }
            } else if (strcmp(key, "REFERER") == 0) {
                /* NOTE: yes the header is misspelt in the spec */
                req->referer = value;
            }
            break;

        case 'T':
            if (strcmp(key, "TRANSFER_ENCODING") == 0) {
                mprStrLower(value);
                if (strcmp(value, "chunked") == 0) {
                    req->flags |= MA_REQ_CHUNKED;
                    /*
                     *  This will be revised by the chunk filter as chunks are processed and will be set to zero when the
                     *  last chunk has been received.
                     */
                    req->remainingContent = MAXINT;
                }
            }
            break;
        
#if BLD_DEBUG
        case 'X':
            if (strcmp(key, "X_APPWEB_CHUNK_SIZE") == 0) {
                mprStrUpper(value);
                conn->response->chunkSize = atoi(value);
                if (conn->response->chunkSize <= 0) {
                    conn->response->chunkSize = 0;
                } else if (conn->response->chunkSize > conn->http->limits.maxChunkSize) {
                    conn->response->chunkSize = conn->http->limits.maxChunkSize;
                }
            }
            break;
#endif

        case 'U':
            if (strcmp(key, "USER_AGENT") == 0) {
                req->userAgent = value;
            }
            break;
        }
    }
    if (conn->protocol == 0 && !keepAlive) {
        conn->keepAliveCount = 0;
    }
    if (!(req->flags & MA_REQ_CHUNKED)) {
        /*  
         *  Step over "\r\n" after headers. As an optimization, don't do this if chunked so chunking can parse a single
         *  chunk delimiter of "\r\nSIZE ...\r\n"
         */
        mprAdjustBufStart(content, 2);
    }
    mprLog(conn, 3, "Select host \"%s\"", conn->host->name);

    if (maSetRequestUri(conn, req->url, "") < 0) {
        maFailConnection(conn, MPR_HTTP_CODE_BAD_REQUEST, "Bad URI format");
        return 0;
    }
    if (conn->host->secure) {
        req->parsedUri->scheme = mprStrdup(req, "https");
    }
    req->parsedUri->port = conn->sock->port;
    req->parsedUri->host = req->hostName ? req->hostName : conn->host->name;
    return 1;
}
Exemple #16
0
/*
    Lexical analyser for XML. Return the next token reading input as required. It uses a one token look ahead and 
    push back mechanism (LAR1 parser). Text token identifiers are left in the tokBuf parser buffer on exit. This Lex 
    has special cases for the states MPR_XML_ELT_DATA where we have an optimized read of element data, and 
    MPR_XML_AFTER_LS where we distinguish between element names, processing instructions and comments. 
 */
static MprXmlToken getXmlToken(MprXml *xp, int state)
{
    MprBuf      *tokBuf;
    char        *cp;
    int         c, rc;

    mprAssert(state >= 0);
    tokBuf = xp->tokBuf;

    if ((c = getNextChar(xp)) < 0) {
        return MPR_XMLTOK_EOF;
    }
    mprFlushBuf(tokBuf);

    /*
        Special case parsing for names and for element data. We do this for performance so we can return to the caller 
        the largest token possible.
     */
    if (state == MPR_XML_ELT_DATA) {
        /*
            Read all the data up to the start of the closing element "<" or the start of a sub-element.
         */
        if (c == '<') {
            if ((c = getNextChar(xp)) < 0) {
                return MPR_XMLTOK_EOF;
            }
            if (c == '/') {
                return MPR_XMLTOK_LS_SLASH;
            }
            putLastChar(xp, c);
            return MPR_XMLTOK_LS;
        }
        do {
            if (mprPutCharToBuf(tokBuf, c) < 0) {
                return MPR_XMLTOK_TOO_BIG;
            }
            if ((c = getNextChar(xp)) < 0) {
                return MPR_XMLTOK_EOF;
            }
        } while (c != '<');

        /*
            Put back the last look-ahead character
         */
        putLastChar(xp, c);

        /*
            If all white space, then zero the token buffer
         */
        for (cp = tokBuf->start; *cp; cp++) {
            if (!isspace((uchar) *cp & 0x7f)) {
                return MPR_XMLTOK_TEXT;
            }
        }
        mprFlushBuf(tokBuf);
        return MPR_XMLTOK_TEXT;
    }

    while (1) {
        switch (c) {
        case ' ':
        case '\n':
        case '\t':
        case '\r':
            break;

        case '<':
            if ((c = getNextChar(xp)) < 0) {
                return MPR_XMLTOK_EOF;
            }
            if (c == '/') {
                return MPR_XMLTOK_LS_SLASH;
            }
            putLastChar(xp, c);
            return MPR_XMLTOK_LS;
    
        case '=':
            return MPR_XMLTOK_EQ;

        case '>':
            return MPR_XMLTOK_GR;

        case '/':
            if ((c = getNextChar(xp)) < 0) {
                return MPR_XMLTOK_EOF;
            }
            if (c == '>') {
                return MPR_XMLTOK_SLASH_GR;
            }
            return MPR_XMLTOK_ERR;
        
        case '\"':
        case '\'':
            xp->quoteChar = c;
            /* Fall through */

        default:
            /*
                We handle element names, attribute names and attribute values 
                here. We do NOT handle data between elements here. Read the 
                token.  Stop on white space or a closing element ">"
             */
            if (xp->quoteChar) {
                if ((c = getNextChar(xp)) < 0) {
                    return MPR_XMLTOK_EOF;
                }
                while (c != xp->quoteChar) {
                    if (mprPutCharToBuf(tokBuf, c) < 0) {
                        return MPR_XMLTOK_TOO_BIG;
                    }
                    if ((c = getNextChar(xp)) < 0) {
                        return MPR_XMLTOK_EOF;
                    }
                }
                xp->quoteChar = 0;

            } else {
                while (!isspace((uchar) c) && c != '>' && c != '/' && c != '=') {
                    if (mprPutCharToBuf(tokBuf, c) < 0) {
                        return MPR_XMLTOK_TOO_BIG;
                    }
                    if ((c = getNextChar(xp)) < 0) {
                        return MPR_XMLTOK_EOF;
                    }
                }
                putLastChar(xp, c);
            }
            if (mprGetBufLength(tokBuf) < 0) {
                return MPR_XMLTOK_ERR;
            }
            mprAddNullToBuf(tokBuf);

            if (state == MPR_XML_AFTER_LS) {
                /*
                    If we are just inside an element "<", then analyze what we have to see if we have an element name, 
                    instruction or comment. Tokbuf will hold "?" for instructions or "!--" for comments.
                 */
                if (mprLookAtNextCharInBuf(tokBuf) == '?') {
                    /*  Just ignore processing instructions */
                    rc = scanFor(xp, "?>");
                    if (rc < 0) {
                        return MPR_XMLTOK_TOO_BIG;
                    } else if (rc == 0) {
                        return MPR_XMLTOK_ERR;
                    }
                    return MPR_XMLTOK_INSTRUCTIONS;

                } else if (mprLookAtNextCharInBuf(tokBuf) == '!') {
                    if (strncmp((char*) tokBuf->start, "![CDATA[", 8) == 0) {
                        mprAdjustBufStart(tokBuf, 8);
                        rc = scanFor(xp, "]]>");
                        if (rc < 0) {
                            return MPR_XMLTOK_TOO_BIG;
                        } else if (rc == 0) {
                            return MPR_XMLTOK_ERR;
                        }
                        return MPR_XMLTOK_CDATA;

                    } else {
                        mprFlushBuf(tokBuf);
                        rc = scanFor(xp, "-->");
                        if (rc < 0) {
                            return MPR_XMLTOK_TOO_BIG;
                        } else if (rc == 0) {
                            return MPR_XMLTOK_ERR;
                        }
                        return MPR_XMLTOK_COMMENT;
                    }
                }
            }
            trimToken(xp);
            return MPR_XMLTOK_TEXT;
        }
        if ((c = getNextChar(xp)) < 0) {
            return MPR_XMLTOK_EOF;
        }
    }

    /* Should never get here */
    mprAssert(0);
    return MPR_XMLTOK_ERR;
}
Exemple #17
0
/*
    function [get|put|delete|post...](uri = null, ...data): Http
 */
static EjsHttp *startHttpRequest(Ejs *ejs, EjsHttp *hp, char *method, int argc, EjsObj **argv)
{
    EjsArray        *args;
    EjsByteArray    *data;
    EjsNumber       *written;
    EjsUri          *uriObj;
    HttpConn        *conn;
    ssize           nbytes;

    conn = hp->conn;
    hp->responseCache = 0;
    hp->requestContentCount = 0;
    mprFlushBuf(hp->responseContent);

    if (argc >= 1 && !ejsIs(ejs, argv[0], Null)) {
        uriObj = (EjsUri*) argv[0];
        hp->uri = httpUriToString(uriObj->uri, HTTP_COMPLETE_URI);
    }
    if (argc == 2 && ejsIs(ejs, argv[1], Array)) {
        args = (EjsArray*) argv[1];
        if (args->length > 0) {
            data = ejsCreateByteArray(ejs, -1);
            written = ejsWriteToByteArray(ejs, data, 1, &argv[1]);
            mprPutBlockToBuf(hp->requestContent, (char*) data->value, (int) written->value);
            mprAddNullToBuf(hp->requestContent);
            assert(written > 0);
        }
    }
    if (hp->uri == 0) {
        ejsThrowArgError(ejs, "URL is not defined");
        return 0;
    }
    if (method && strcmp(hp->method, method) != 0) {
        hp->method = sclone(method);
    }
    if (hp->method == 0) {
        ejsThrowArgError(ejs, "HTTP Method is not defined");
        return 0;
    }
    if (hp->certFile) {
        if (!hp->ssl) {
            hp->ssl = mprCreateSsl(0);
        }
        mprSetSslCertFile(hp->ssl, hp->certFile);
        if (!hp->keyFile) {
            ejsThrowStateError(ejs, "Must define a Http.key to use with a certificate");
        }
        mprSetSslKeyFile(hp->ssl, hp->keyFile);
    }
    if (hp->caFile) {
        if (!hp->ssl) {
            hp->ssl = mprCreateSsl(0);
        }
        mprSetSslCaFile(hp->ssl, hp->caFile);
    }
    if (httpConnect(conn, hp->method, hp->uri, hp->ssl) < 0) {
        ejsThrowIOError(ejs, "Cannot issue request for \"%s\"", hp->uri);
        return 0;
    }
    if (mprGetBufLength(hp->requestContent) > 0) {
        nbytes = httpWriteBlock(conn->writeq, mprGetBufStart(hp->requestContent), mprGetBufLength(hp->requestContent),
            HTTP_BLOCK);
        if (nbytes < 0) {
            ejsThrowIOError(ejs, "Cannot write request data for \"%s\"", hp->uri);
            return 0;
        } else if (nbytes > 0) {
            assert(nbytes == mprGetBufLength(hp->requestContent));
            mprAdjustBufStart(hp->requestContent, nbytes);
            hp->requestContentCount += nbytes;
        }
        httpFinalize(conn);
    }
    httpNotify(conn, HTTP_EVENT_WRITABLE, 0);
    if (conn->async) {
        httpEnableConnEvents(hp->conn);
    }
    return hp;
}
Exemple #18
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);
    }
}
Exemple #19
0
/*
    Process the content data.
    Returns < 0 on error
            == 0 when more data is needed
            == 1 when data successfully written
 */
static int processUploadData(HttpQueue *q)
{
    HttpConn        *conn;
    HttpPacket      *packet;
    MprBuf          *content;
    Upload          *up;
    ssize           size, dataLen;
    bool            pureData;
    char            *data, *bp, *key;

    conn = q->conn;
    up = q->queueData;
    content = q->first->content;
    packet = 0;

    size = mprGetBufLength(content);
    if (size < up->boundaryLen) {
        /*  Incomplete boundary. Return and get more data */
        return 0;
    }
    bp = getBoundary(mprGetBufStart(content), size, up->boundary, up->boundaryLen, &pureData);
    if (bp == 0) {
        if (up->clientFilename) {
            /*
                No signature found yet. probably more data to come. Must handle split boundaries.
             */
            data = mprGetBufStart(content);
            dataLen = pureData ? size : (size - (up->boundaryLen - 1));
            if (dataLen > 0) {
                if (writeToFile(q, mprGetBufStart(content), dataLen) < 0) {
                    return MPR_ERR_CANT_WRITE;
                }
            }
            mprAdjustBufStart(content, dataLen);
            return 0;       /* Get more data */
        }
    }
    data = mprGetBufStart(content);
    dataLen = (bp) ? (bp - data) : mprGetBufLength(content);

    if (dataLen > 0) {
        mprAdjustBufStart(content, dataLen);
        /*
            This is the CRLF before the boundary
         */
        if (dataLen >= 2 && data[dataLen - 2] == '\r' && data[dataLen - 1] == '\n') {
            dataLen -= 2;
        }
        if (up->clientFilename) {
            /*
                Write the last bit of file data and add to the list of files and define environment variables
             */
            if (writeToFile(q, data, dataLen) < 0) {
                return MPR_ERR_CANT_WRITE;
            }
            defineFileFields(q, up);

        } else {
            /*
                Normal string form data variables
             */
            data[dataLen] = '\0';
#if KEEP
            httpTrace(conn, "request.upload.variables", "context", "'%s':'%s'", up->name, data);
#endif
            key = mprUriDecode(up->name);
            data = mprUriDecode(data);
            httpSetParam(conn, key, data);

            if (packet == 0) {
                packet = httpCreatePacket(ME_MAX_BUFFER);
            }
            if (httpGetPacketLength(packet) > 0) {
                /*
                    Need to add www-form-urlencoding separators
                 */
                mprPutCharToBuf(packet->content, '&');
            } else {
                conn->rx->mimeType = sclone("application/x-www-form-urlencoded");

            }
            mprPutToBuf(packet->content, "%s=%s", up->name, data);
        }
    }
    if (up->clientFilename) {
        /*
            Now have all the data (we've seen the boundary)
         */
        mprCloseFile(up->file);
        up->file = 0;
        up->clientFilename = 0;
    }
    if (packet) {
        httpPutPacketToNext(q, packet);
    }
    up->contentState = HTTP_UPLOAD_BOUNDARY;
    return 0;
}
Exemple #20
0
/*
    Incoming data acceptance routine. The service queue is used, but not a service routine as the data is processed
    immediately. Partial data is buffered on the service queue until a correct mime boundary is seen.
 */
static void incomingUpload(HttpQueue *q, HttpPacket *packet)
{
    HttpConn    *conn;
    HttpRx      *rx;
    MprBuf      *content;
    Upload      *up;
    char        *line, *nextTok;
    ssize       count;
    int         done, rc;

    assert(packet);

    conn = q->conn;
    rx = conn->rx;
    up = q->queueData;
    if (conn->error) {
        return;
    }
    if (httpGetPacketLength(packet) == 0) {
        if (up->contentState != HTTP_UPLOAD_CONTENT_END) {
            httpError(conn, HTTP_CODE_BAD_REQUEST, "Client supplied insufficient upload data");
        }
        httpPutPacketToNext(q, packet);
        return;
    }
    /*
        Put the packet data onto the service queue for buffering. This aggregates input data incase we don't have
        a complete mime record yet.
     */
    httpJoinPacketForService(q, packet, 0);

    packet = q->first;
    content = packet->content;
    count = httpGetPacketLength(packet);

    for (done = 0, line = 0; !done; ) {
        if  (up->contentState == HTTP_UPLOAD_BOUNDARY || up->contentState == HTTP_UPLOAD_CONTENT_HEADER) {
            /*
                Parse the next input line
             */
            line = mprGetBufStart(content);
            if ((nextTok = memchr(line, '\n', mprGetBufLength(content))) == 0) {
                /* Incomplete line */
                break;
            }
            *nextTok++ = '\0';
            mprAdjustBufStart(content, (int) (nextTok - line));
            line = strim(line, "\r", MPR_TRIM_END);
        }
        switch (up->contentState) {
        case HTTP_UPLOAD_BOUNDARY:
            if (processUploadBoundary(q, line) < 0) {
                done++;
            }
            break;

        case HTTP_UPLOAD_CONTENT_HEADER:
            if (processUploadHeader(q, line) < 0) {
                done++;
            }
            break;

        case HTTP_UPLOAD_CONTENT_DATA:
            rc = processUploadData(q);
            if (rc < 0) {
                done++;
            }
            if (httpGetPacketLength(packet) < up->boundaryLen) {
                /*  Incomplete boundary - return to get more data */
                done++;
            }
            break;

        case HTTP_UPLOAD_CONTENT_END:
            done++;
            break;
        }
    }
    q->count -= (count - httpGetPacketLength(packet));
    assert(q->count >= 0);

    if (httpGetPacketLength(packet) == 0) {
        /*
            Quicker to remove the buffer so the packets don't have to be joined the next time
         */
        httpGetPacket(q);
    } else {
        /*
            Compact the buffer to prevent memory growth. There is often residual data after the boundary for the next block.
         */
        if (packet != rx->headerPacket) {
            mprCompactBuf(content);
        }
    }
}