Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30c81844 authored by Andreas Huber's avatar Andreas Huber Committed by Android (Google) Code Review
Browse files

Merge "DO NOT MERGE Support for "chunked" HTTP transfer encoding." into gingerbread

parents 66cff624 8d023ddc
Loading
Loading
Loading
Loading
+102 −2
Original line number Diff line number Diff line
@@ -67,7 +67,9 @@ NuHTTPDataSource::NuHTTPDataSource()
      mPort(0),
      mOffset(0),
      mContentLength(0),
      mContentLengthValid(false) {
      mContentLengthValid(false),
      mHasChunkedTransferEncoding(false),
      mChunkDataBytesLeft(0) {
}

NuHTTPDataSource::~NuHTTPDataSource() {
@@ -184,6 +186,30 @@ status_t NuHTTPDataSource::connect(
            return ERROR_IO;
        }

        mHasChunkedTransferEncoding = false;

        {
            string value;
            if (mHTTP.find_header_value("Transfer-Encoding", &value)
                    || mHTTP.find_header_value("Transfer-encoding", &value)) {
                // We don't currently support any transfer encodings but
                // chunked.

                if (!strcasecmp(value.c_str(), "chunked")) {
                    LOGI("Chunked transfer encoding applied.");
                    mHasChunkedTransferEncoding = true;
                    mChunkDataBytesLeft = 0;
                } else {
                    mState = DISCONNECTED;
                    mHTTP.disconnect();

                    LOGE("We don't support '%s' transfer encoding.", value.c_str());

                    return ERROR_UNSUPPORTED;
                }
            }
        }

        applyTimeoutResponse();

        if (offset == 0) {
@@ -193,8 +219,17 @@ status_t NuHTTPDataSource::connect(
                    && ParseSingleUnsignedLong(value.c_str(), &x)) {
                mContentLength = (off_t)x;
                mContentLengthValid = true;
            } else {
                LOGW("Server did not give us the content length!");
            }
        } else {
            if (httpStatus != 206 /* Partial Content */) {
                // We requested a range but the server didn't support that.
                LOGE("We requested a range but the server didn't "
                     "support that.");
                return ERROR_UNSUPPORTED;
            }

            string value;
            unsigned long x;
            if (mHTTP.find_header_value(string("Content-Range"), &value)) {
@@ -222,6 +257,71 @@ status_t NuHTTPDataSource::initCheck() const {
    return mState == CONNECTED ? OK : NO_INIT;
}

ssize_t NuHTTPDataSource::internalRead(void *data, size_t size) {
    if (!mHasChunkedTransferEncoding) {
        return mHTTP.receive(data, size);
    }

    if (mChunkDataBytesLeft < 0) {
        return 0;
    } else if (mChunkDataBytesLeft == 0) {
        char line[1024];
        status_t err = mHTTP.receive_line(line, sizeof(line));

        if (err != OK) {
            return err;
        }

        LOGV("line = '%s'", line);

        char *end;
        unsigned long n = strtoul(line, &end, 16);

        if (end == line || (*end != ';' && *end != '\0')) {
            LOGE("malformed HTTP chunk '%s'", line);
            return ERROR_MALFORMED;
        }

        mChunkDataBytesLeft = n;
        LOGV("chunk data size = %lu", n);

        if (mChunkDataBytesLeft == 0) {
            mChunkDataBytesLeft = -1;
            return 0;
        }

        // fall through
    }

    if (size > (size_t)mChunkDataBytesLeft) {
        size = mChunkDataBytesLeft;
    }

    ssize_t n = mHTTP.receive(data, size);

    if (n < 0) {
        return n;
    }

    mChunkDataBytesLeft -= (size_t)n;

    if (mChunkDataBytesLeft == 0) {
        char line[1024];
        status_t err = mHTTP.receive_line(line, sizeof(line));

        if (err != OK) {
            return err;
        }

        if (line[0] != '\0') {
            LOGE("missing HTTP chunk terminator.");
            return ERROR_MALFORMED;
        }
    }

    return n;
}

ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
    LOGV("readAt offset %ld, size %d", offset, size);

@@ -250,7 +350,7 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
    size_t numBytesRead = 0;
    while (numBytesRead < size) {
        ssize_t n =
            mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead);
            internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);

        if (n < 0) {
            return n;
+28 −7
Original line number Diff line number Diff line
@@ -318,20 +318,41 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
    status_t err = source->getSize(&size);

    if (err != OK) {
        return err;
        size = 65536;
    }

    sp<ABuffer> buffer = new ABuffer(size);
    size_t offset = 0;
    while (offset < (size_t)size) {
    buffer->setRange(0, 0);

    for (;;) {
        size_t bufferRemaining = buffer->capacity() - buffer->size();

        if (bufferRemaining == 0) {
            bufferRemaining = 32768;

            LOGV("increasing download buffer to %d bytes",
                 buffer->size() + bufferRemaining);

            sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);
            memcpy(copy->data(), buffer->data(), buffer->size());
            copy->setRange(0, buffer->size());

            buffer = copy;
        }

        ssize_t n = source->readAt(
                offset, buffer->data() + offset, size - offset);
                buffer->size(), buffer->data() + buffer->size(),
                bufferRemaining);

        if (n <= 0) {
            return ERROR_IO;
        if (n < 0) {
            return err;
        }

        if (n == 0) {
            break;
        }

        offset += n;
        buffer->setRange(0, buffer->size() + (size_t)n);
    }

    *out = buffer;
+4 −4
Original line number Diff line number Diff line
@@ -55,6 +55,10 @@ public:
    // Pass a negative value to disable the timeout.
    void setReceiveTimeout(int seconds);

    // Receive a line of data terminated by CRLF, line will be '\0' terminated
    // _excluding_ the termianting CRLF.
    status_t receive_line(char *line, size_t size);

private:
    enum State {
        READY,
@@ -68,10 +72,6 @@ private:

    KeyedVector<string, string> mHeaders;

    // Receive a line of data terminated by CRLF, line will be '\0' terminated
    // _excluding_ the termianting CRLF.
    status_t receive_line(char *line, size_t size);

    HTTPStream(const HTTPStream &);
    HTTPStream &operator=(const HTTPStream &);
};
+8 −0
Original line number Diff line number Diff line
@@ -49,6 +49,11 @@ private:
    off_t mOffset;
    off_t mContentLength;
    bool mContentLengthValid;
    bool mHasChunkedTransferEncoding;

    // The number of data bytes in the current chunk before any subsequent
    // chunk header (or -1 if no more chunks).
    ssize_t mChunkDataBytesLeft;

    status_t connect(
            const char *uri, const String8 &headers, off_t offset);
@@ -58,6 +63,9 @@ private:
            const String8 &headers,
            off_t offset);

    // Read up to "size" bytes of data, respect transfer encoding.
    ssize_t internalRead(void *data, size_t size);

    void applyTimeoutResponse();

    static void MakeFullHeaders(