Bug 1370177 - Treat compressed HTTP channels as having infinite length and as unseekable in media code. r=jwwang.
The problem here is that the server is setting the Content-Length to the size
of the gzipped file, which is confusing our media code as that's not the length
of the decompressed stream. Necko's decompressor is streaming, so we can't know
the length of the stream in advance unless we wait for the entire file to
download first. We also can't seek in compressed HTTP resources anyway.
So just have our code assume that compressed HTTP channels are of infinite
length and have un unseekable transport. Then we can seek in the buffered
regions at least.
MozReview-Commit-ID: 9SiLuMZGSeJ
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -174,16 +174,24 @@ ChannelMediaResource::Listener::AsyncOnC
}
nsresult
ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
{
return QueryInterface(aIID, aResult);
}
+static bool
+IsPayloadCompressed(nsIHttpChannel* aChannel)
+{
+ nsAutoCString encoding;
+ Unused << aChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), encoding);
+ return encoding.Length() > 0;
+}
+
nsresult
ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
{
NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");
MediaDecoderOwner* owner = mCallback->GetMediaOwner();
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
dom::HTMLMediaElement* element = owner->GetMediaElement();
@@ -246,30 +254,35 @@ ChannelMediaResource::OnStartRequest(nsI
nsAutoCString ranges;
Unused << hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
ranges);
bool acceptsRanges = ranges.EqualsLiteral("bytes");
// True if this channel will not return an unbounded amount of data
bool dataIsBounded = false;
int64_t contentLength = -1;
- hc->GetContentLength(&contentLength);
+ const bool isCompressed = IsPayloadCompressed(hc);
+ if (!isCompressed) {
+ hc->GetContentLength(&contentLength);
+ }
if (contentLength >= 0 &&
(responseStatus == HTTP_OK_CODE ||
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
// "OK" status means Content-Length is for the whole resource.
// Since that's bounded, we know we have a finite-length resource.
dataIsBounded = true;
}
// Assume Range requests have a bounded upper limit unless the
// Content-Range header tells us otherwise.
bool boundedSeekLimit = true;
// Check response code for byte-range requests (seeking, chunk requests).
- if (responseStatus == HTTP_PARTIAL_RESPONSE_CODE) {
+ // We don't expect to get a 206 response for a compressed stream, but
+ // double check just to be sure.
+ if (!isCompressed && responseStatus == HTTP_PARTIAL_RESPONSE_CODE) {
// Parse Content-Range header.
int64_t rangeStart = 0;
int64_t rangeEnd = 0;
int64_t rangeTotal = 0;
rv = ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal);
// We received 'Content-Range', so the server accepts range requests.
bool gotRangeHeader = NS_SUCCEEDED(rv);
@@ -308,18 +321,18 @@ ChannelMediaResource::OnStartRequest(nsI
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
mCacheStream.NotifyDataLength(contentLength);
}
// XXX we probably should examine the Content-Range header in case
// the server gave us a range which is not quite what we asked for
// If we get an HTTP_OK_CODE response to our byte range request,
// and the server isn't sending Accept-Ranges:bytes then we don't
- // support seeking.
- seekable = acceptsRanges;
+ // support seeking. We also can't seek in compressed streams.
+ seekable = !isCompressed && acceptsRanges;
if (seekable && boundedSeekLimit) {
// If range requests are supported, and we did not see an unbounded
// upper range limit, we assume the resource is bounded.
dataIsBounded = true;
}
mCallback->SetInfinite(!dataIsBounded);
}
@@ -526,17 +539,17 @@ ChannelMediaResource::OnDataAvailable(ns
nsresult ChannelMediaResource::Open(nsIStreamListener **aStreamListener)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
int64_t cl = -1;
if (mChannel) {
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
- if (hc) {
+ if (hc && !IsPayloadCompressed(hc)) {
if (NS_FAILED(hc->GetContentLength(&cl))) {
cl = -1;
}
}
}
nsresult rv = mCacheStream.Init(cl);
if (NS_FAILED(rv))
@@ -564,17 +577,17 @@ nsresult ChannelMediaResource::OpenChann
*aStreamListener = nullptr;
}
// Set the content length, if it's available as an HTTP header.
// This ensures that MediaResource wrapping objects for platform libraries
// that expect to know the length of a resource can get it before
// OnStartRequest() fires.
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
- if (hc) {
+ if (hc && !IsPayloadCompressed(hc)) {
int64_t cl = -1;
if (NS_SUCCEEDED(hc->GetContentLength(&cl)) && cl != -1) {
mCacheStream.NotifyDataLength(cl);
}
}
mListener = new Listener(this);
if (aStreamListener) {