Merge "DO NOT MERGE Support for "chunked" HTTP transfer encoding." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
30c818444d
@@ -67,7 +67,9 @@ NuHTTPDataSource::NuHTTPDataSource()
|
|||||||
mPort(0),
|
mPort(0),
|
||||||
mOffset(0),
|
mOffset(0),
|
||||||
mContentLength(0),
|
mContentLength(0),
|
||||||
mContentLengthValid(false) {
|
mContentLengthValid(false),
|
||||||
|
mHasChunkedTransferEncoding(false),
|
||||||
|
mChunkDataBytesLeft(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
NuHTTPDataSource::~NuHTTPDataSource() {
|
NuHTTPDataSource::~NuHTTPDataSource() {
|
||||||
@@ -184,6 +186,30 @@ status_t NuHTTPDataSource::connect(
|
|||||||
return ERROR_IO;
|
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();
|
applyTimeoutResponse();
|
||||||
|
|
||||||
if (offset == 0) {
|
if (offset == 0) {
|
||||||
@@ -193,8 +219,17 @@ status_t NuHTTPDataSource::connect(
|
|||||||
&& ParseSingleUnsignedLong(value.c_str(), &x)) {
|
&& ParseSingleUnsignedLong(value.c_str(), &x)) {
|
||||||
mContentLength = (off_t)x;
|
mContentLength = (off_t)x;
|
||||||
mContentLengthValid = true;
|
mContentLengthValid = true;
|
||||||
|
} else {
|
||||||
|
LOGW("Server did not give us the content length!");
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
string value;
|
||||||
unsigned long x;
|
unsigned long x;
|
||||||
if (mHTTP.find_header_value(string("Content-Range"), &value)) {
|
if (mHTTP.find_header_value(string("Content-Range"), &value)) {
|
||||||
@@ -222,6 +257,71 @@ status_t NuHTTPDataSource::initCheck() const {
|
|||||||
return mState == CONNECTED ? OK : NO_INIT;
|
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) {
|
ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) {
|
||||||
LOGV("readAt offset %ld, size %d", offset, 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;
|
size_t numBytesRead = 0;
|
||||||
while (numBytesRead < size) {
|
while (numBytesRead < size) {
|
||||||
ssize_t n =
|
ssize_t n =
|
||||||
mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead);
|
internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);
|
||||||
|
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@@ -318,20 +318,41 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
|
|||||||
status_t err = source->getSize(&size);
|
status_t err = source->getSize(&size);
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return err;
|
size = 65536;
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<ABuffer> buffer = new ABuffer(size);
|
sp<ABuffer> buffer = new ABuffer(size);
|
||||||
size_t offset = 0;
|
buffer->setRange(0, 0);
|
||||||
while (offset < (size_t)size) {
|
|
||||||
ssize_t n = source->readAt(
|
|
||||||
offset, buffer->data() + offset, size - offset);
|
|
||||||
|
|
||||||
if (n <= 0) {
|
for (;;) {
|
||||||
return ERROR_IO;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += n;
|
ssize_t n = source->readAt(
|
||||||
|
buffer->size(), buffer->data() + buffer->size(),
|
||||||
|
bufferRemaining);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer->setRange(0, buffer->size() + (size_t)n);
|
||||||
}
|
}
|
||||||
|
|
||||||
*out = buffer;
|
*out = buffer;
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ public:
|
|||||||
// Pass a negative value to disable the timeout.
|
// Pass a negative value to disable the timeout.
|
||||||
void setReceiveTimeout(int seconds);
|
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:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
READY,
|
READY,
|
||||||
@@ -68,10 +72,6 @@ private:
|
|||||||
|
|
||||||
KeyedVector<string, string> mHeaders;
|
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(const HTTPStream &);
|
||||||
HTTPStream &operator=(const HTTPStream &);
|
HTTPStream &operator=(const HTTPStream &);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ private:
|
|||||||
off_t mOffset;
|
off_t mOffset;
|
||||||
off_t mContentLength;
|
off_t mContentLength;
|
||||||
bool mContentLengthValid;
|
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(
|
status_t connect(
|
||||||
const char *uri, const String8 &headers, off_t offset);
|
const char *uri, const String8 &headers, off_t offset);
|
||||||
@@ -58,6 +63,9 @@ private:
|
|||||||
const String8 &headers,
|
const String8 &headers,
|
||||||
off_t offset);
|
off_t offset);
|
||||||
|
|
||||||
|
// Read up to "size" bytes of data, respect transfer encoding.
|
||||||
|
ssize_t internalRead(void *data, size_t size);
|
||||||
|
|
||||||
void applyTimeoutResponse();
|
void applyTimeoutResponse();
|
||||||
|
|
||||||
static void MakeFullHeaders(
|
static void MakeFullHeaders(
|
||||||
|
|||||||
Reference in New Issue
Block a user