Bug 748117 - Make sure http channel is pending in OnStartRequest & OnDataAvailable r=jduell

This commit is contained in:
Dragana Damjanovic 2014-06-26 18:51:49 -07:00
Родитель 6b53f3d634
Коммит 9c72743446
12 изменённых файлов: 103 добавлений и 29 удалений

Просмотреть файл

@ -70,6 +70,7 @@ HttpBaseChannel::HttpBaseChannel()
, mContentDispositionHint(UINT32_MAX) , mContentDispositionHint(UINT32_MAX)
, mHttpHandler(gHttpHandler) , mHttpHandler(gHttpHandler)
, mRedirectCount(0) , mRedirectCount(0)
, mForcePending(false)
{ {
LOG(("Creating HttpBaseChannel @%x\n", this)); LOG(("Creating HttpBaseChannel @%x\n", this));
@ -187,7 +188,7 @@ NS_IMETHODIMP
HttpBaseChannel::IsPending(bool *aIsPending) HttpBaseChannel::IsPending(bool *aIsPending)
{ {
NS_ENSURE_ARG_POINTER(aIsPending); NS_ENSURE_ARG_POINTER(aIsPending);
*aIsPending = mIsPending; *aIsPending = mIsPending || mForcePending;
return NS_OK; return NS_OK;
} }
@ -1616,6 +1617,13 @@ HttpBaseChannel::AddRedirect(nsIPrincipal *aRedirect)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
HttpBaseChannel::ForcePending(bool aForcePending)
{
mForcePending = aForcePending;
return NS_OK;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority // HttpBaseChannel::nsISupportsPriority
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

Просмотреть файл

@ -169,6 +169,7 @@ public:
NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable); NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable);
NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable); NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable);
NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect); NS_IMETHOD AddRedirect(nsIPrincipal *aRedirect);
NS_IMETHOD ForcePending(bool aForcePending);
inline void CleanRedirectCacheChainIfNecessary() inline void CleanRedirectCacheChainIfNecessary()
{ {
@ -369,7 +370,9 @@ protected:
// so that the timing can still be queried from OnStopRequest // so that the timing can still be queried from OnStopRequest
TimingStruct mTransactionTimings; TimingStruct mTransactionTimings;
nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIPrincipal> mPrincipal;
bool mForcePending;
}; };
// Share some code while working around C++'s absurd inability to handle casting // Share some code while working around C++'s absurd inability to handle casting
@ -412,7 +415,6 @@ nsresult HttpAsyncAborter<T>::AsyncAbort(nsresult status)
("HttpAsyncAborter::AsyncAbort [this=%p status=%x]\n", mThis, status)); ("HttpAsyncAborter::AsyncAbort [this=%p status=%x]\n", mThis, status));
mThis->mStatus = status; mThis->mStatus = status;
mThis->mIsPending = false;
// if this fails? Callers ignore our return value anyway.... // if this fails? Callers ignore our return value anyway....
return AsyncCall(&T::HandleAsyncAbort); return AsyncCall(&T::HandleAsyncAbort);

Просмотреть файл

@ -711,7 +711,7 @@ HttpChannelChild::FailedAsyncOpen(const nsresult& status)
LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%x]\n", this, status)); LOG(("HttpChannelChild::FailedAsyncOpen [this=%p status=%x]\n", this, status));
mStatus = status; mStatus = status;
mIsPending = false;
// We're already being called from IPDL, therefore already "async" // We're already being called from IPDL, therefore already "async"
HandleAsyncAbort(); HandleAsyncAbort();
} }

Просмотреть файл

@ -226,7 +226,6 @@ nsHttpChannel::nsHttpChannel()
, mIsPartialRequest(0) , mIsPartialRequest(0)
, mHasAutoRedirectVetoNotifier(0) , mHasAutoRedirectVetoNotifier(0)
, mDidReval(false) , mDidReval(false)
, mForcePending(false)
{ {
LOG(("Creating nsHttpChannel [this=%p]\n", this)); LOG(("Creating nsHttpChannel [this=%p]\n", this));
mChannelCreationTime = PR_Now(); mChannelCreationTime = PR_Now();
@ -6205,22 +6204,4 @@ nsHttpChannel::GetPerformance()
return docPerformance; return docPerformance;
} }
void
nsHttpChannel::ForcePending(bool aForcePending)
{
// Set true here so IsPending will return true.
// Required for callback diversion from child back to parent. In such cases
// OnStopRequest can be called in the parent before callbacks are diverted
// back from the child to the listener in the parent.
mForcePending = aForcePending;
}
NS_IMETHODIMP
nsHttpChannel::IsPending(bool *aIsPending)
{
NS_ENSURE_ARG_POINTER(aIsPending);
*aIsPending = mIsPending || mForcePending;
return NS_OK;
}
} } // namespace mozilla::net } } // namespace mozilla::net

Просмотреть файл

@ -103,7 +103,6 @@ public:
NS_IMETHOD Cancel(nsresult status); NS_IMETHOD Cancel(nsresult status);
NS_IMETHOD Suspend(); NS_IMETHOD Suspend();
NS_IMETHOD Resume(); NS_IMETHOD Resume();
NS_IMETHOD IsPending(bool *aIsPending);
// nsIChannel // nsIChannel
NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
@ -189,8 +188,6 @@ public: /* internal necko use only */
uint32_t mKeep : 2; uint32_t mKeep : 2;
}; };
void ForcePending(bool aForcePending);
private: private:
typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result); typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
@ -422,9 +419,6 @@ protected:
private: // cache telemetry private: // cache telemetry
bool mDidReval; bool mDidReval;
private:
bool mForcePending;
}; };
} } // namespace mozilla::net } } // namespace mozilla::net

Просмотреть файл

@ -199,4 +199,11 @@ interface nsIHttpChannelInternal : nsISupports
* write to nsIRedirectHistory.redirects. * write to nsIRedirectHistory.redirects.
*/ */
void addRedirect(in nsIPrincipal aPrincipal); void addRedirect(in nsIPrincipal aPrincipal);
/**
* ForcePending(true) overrides the normal behavior for the
* channel's IsPending(), forcing 'true' to be returned. A call to
* ForcePending(false) reverts IsPending() back to normal behavior.
*/
void ForcePending(in boolean aForcePending);
}; };

Просмотреть файл

@ -17,6 +17,7 @@
#include "nsIViewSourceChannel.h" #include "nsIViewSourceChannel.h"
#include "nsIHttpChannel.h" #include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsNetCID.h" #include "nsNetCID.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
@ -200,11 +201,24 @@ nsUnknownDecoder::OnStopRequest(nsIRequest* request, nsISupports *aCtxt,
if (mContentType.IsEmpty()) { if (mContentType.IsEmpty()) {
DetermineContentType(request); DetermineContentType(request);
// Make sure channel listeners see channel as pending while we call
// OnStartRequest/OnDataAvailable, even though the underlying channel
// has already hit OnStopRequest.
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(request);
if (httpChannel) {
httpChannel->ForcePending(true);
}
rv = FireListenerNotifications(request, aCtxt); rv = FireListenerNotifications(request, aCtxt);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
aStatus = rv; aStatus = rv;
} }
// now we need to set pending state to false before calling OnStopRequest
if (httpChannel) {
httpChannel->ForcePending(false);
}
} }
rv = mNextListener->OnStopRequest(request, aCtxt, aStatus); rv = mNextListener->OnStopRequest(request, aCtxt, aStatus);

Просмотреть файл

@ -85,6 +85,8 @@ ChannelListener.prototype = {
if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
do_throw("Could not get contentLength"); do_throw("Could not get contentLength");
} }
if (!request.isPending())
do_throw("request reports itself as not pending from onStartRequest!");
if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL))) if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
do_throw("Content length is unknown in onStartRequest!"); do_throw("Content length is unknown in onStartRequest!");

Просмотреть файл

@ -0,0 +1,57 @@
//
// tests HTTP replies that lack content-type (where we try to sniff content-type).
//
// Note: sets Cc and Ci variables
Cu.import("resource://testing-common/httpd.js");
var httpserver = new HttpServer();
var testpath = "/simple";
var httpbody = "<html><body>omg hai</body></html>";
var buffer = "";
var dbg=0
if (dbg) { print("============== START =========="); }
function run_test() {
setup_test();
do_test_pending();
}
function setup_test() {
if (dbg) { print("============== setup_test: in"); }
httpserver.registerPathHandler(testpath, serverHandler);
httpserver.start(-1);
var channel = setupChannel(testpath);
// ChannelListener defined in head_channels.js
channel.asyncOpen(new ChannelListener(checkRequest, channel), null);
if (dbg) { print("============== setup_test: out"); }
}
function setupChannel(path) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var chan = ios.newChannel("http://localhost:" +
httpserver.identity.primaryPort + path, "", null);
chan.QueryInterface(Ci.nsIHttpChannel);
chan.requestMethod = "GET";
return chan;
}
function serverHandler(metadata, response) {
if (dbg) { print("============== serverHandler: in"); }
// no content type set
// response.setHeader("Content-Type", "text/plain", false);
response.bodyOutputStream.write(httpbody, httpbody.length);
if (dbg) { print("============== serverHandler: out"); }
}
function checkRequest(request, data, context) {
if (dbg) { print("============== checkRequest: in"); }
do_check_eq(data, httpbody);
do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,"text/html");
httpserver.stop(do_test_finished);
if (dbg) { print("============== checkRequest: out"); }
}

Просмотреть файл

@ -333,3 +333,4 @@ skip-if = os == "android"
run-if = os == "win" run-if = os == "win"
[test_udp_multicast.js] [test_udp_multicast.js]
[test_redirect_history.js] [test_redirect_history.js]
[test_reply_without_content_type.js]

Просмотреть файл

@ -0,0 +1,7 @@
//
// Run test script in content process instead of chrome (xpcshell's default)
//
function run_test() {
run_test_in_child("../unit/test_reply_without_content_type.js");
}

Просмотреть файл

@ -33,3 +33,4 @@ skip-if = true
[test_xmlhttprequest_wrap.js] [test_xmlhttprequest_wrap.js]
[test_XHR_redirects.js] [test_XHR_redirects.js]
[test_redirect_history_wrap.js] [test_redirect_history_wrap.js]
[test_reply_without_content_type_wrap.js]