diff --git a/netwerk/base/public/nsISocketTransport.idl b/netwerk/base/public/nsISocketTransport.idl index d0de9e41db5..f5c0d7abc23 100644 --- a/netwerk/base/public/nsISocketTransport.idl +++ b/netwerk/base/public/nsISocketTransport.idl @@ -26,4 +26,10 @@ interface nsISocketTransport : nsISupports { attribute boolean reuseConnection; + + /** + * Is used to tell the channel to stop reading data after a certain point; + * needed by HTTP/1.1 + */ + attribute long bytesExpected; }; diff --git a/netwerk/base/public/nsISocketTransportService.idl b/netwerk/base/public/nsISocketTransportService.idl index f9fff27ceb8..40c7f02bccc 100644 --- a/netwerk/base/public/nsISocketTransportService.idl +++ b/netwerk/base/public/nsISocketTransportService.idl @@ -56,7 +56,8 @@ interface nsISocketTransportService : nsISupports */ boolean reuseTransport(in nsIChannel i_Transport); - void shutdown(); + void shutdown (); + void wakeup (in nsIChannel i_Transport); }; %{C++ diff --git a/netwerk/base/src/nsSocketTransport.cpp b/netwerk/base/src/nsSocketTransport.cpp index 2814e337ad7..08c5e4c80de 100644 --- a/netwerk/base/src/nsSocketTransport.cpp +++ b/netwerk/base/src/nsSocketTransport.cpp @@ -137,7 +137,8 @@ nsSocketTransport::nsSocketTransport(): mWriteCount(0), mWriteBuffer(nsnull), mWriteBufferIndex(0), - mWriteBufferLength(0) + mWriteBufferLength(0), + mBytesExpected(-1) { NS_INIT_REFCNT(); @@ -490,6 +491,8 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags) if (mCloseConnectionOnceDone) CloseConnection(); + + mBytesExpected = -1; } // @@ -526,12 +529,21 @@ nsresult nsSocketTransport::Process(PRInt16 aSelectFlags) case eSocketState_WaitReadWrite: // Process the read request... - if (GetReadType() != eSocketRead_None) { - mStatus = doRead(aSelectFlags); - if (NS_SUCCEEDED(mStatus)) { - SetFlag(eSocketRead_Done); - break; - } + if (GetReadType() != eSocketRead_None) + { + if (mBytesExpected == 0) + { + mStatus = NS_OK; + mSelectFlags &= (~PR_POLL_READ); + } + else + mStatus = doRead (aSelectFlags); + + if (NS_SUCCEEDED(mStatus)) + { + SetFlag(eSocketRead_Done); + break; + } } // Process the write request... if ((NS_SUCCEEDED(mStatus) || mStatus == NS_BASE_STREAM_WOULD_BLOCK) @@ -837,11 +849,14 @@ nsReadFromSocket(void* closure, info->bEOF = PR_FALSE; *readCount = 0; - if (count > 0) { - len = PR_Read(info->fd, toRawSegment, count); - if (len >= 0) { + if (count > 0) + { + len = PR_Read (info -> fd, toRawSegment, count); + + if (len >= 0) + { *readCount = (PRUint32)len; - info->bEOF = (0 == len); + info -> bEOF = (0 == len); } // // Error... @@ -947,7 +962,7 @@ nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) // totalBytesWritten = 0; info.fd = mSocketFD; - // + // Release the transport lock... WriteSegments(...) aquires the nsBuffer // lock which could cause a deadlock by blocking the socket transport // thread @@ -961,11 +976,37 @@ nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) ("WriteSegments [fd=%x]. rv = %x. Bytes read =%d\n", mSocketFD, rv, totalBytesWritten)); + // + // Fire a single OnDataAvaliable(...) notification once as much data has + // been filled into the stream as possible... + // + if (totalBytesWritten) + { + if (mReadListener) + { + nsresult rv1; + + rv1 = mReadListener->OnDataAvailable(this, mReadContext, mReadPipeIn, + mReadOffset, + totalBytesWritten); + + // + // If the consumer returns failure, then cancel the operation... + // + if (NS_FAILED(rv1)) + rv = rv1; + + } + mReadOffset += totalBytesWritten; + } + // // Deal with the possible return values... // if (NS_SUCCEEDED(rv)) { - if (info.bEOF) { // EOF condition + if (info.bEOF || mBytesExpected == 0) + { + // EOF condition mSelectFlags &= (~PR_POLL_READ); rv = NS_OK; } @@ -974,27 +1015,6 @@ nsresult nsSocketTransport::doRead(PRInt16 aSelectFlags) } } - // - // Fire a single OnDataAvaliable(...) notification once as much data has - // been filled into the stream as possible... - // - if (totalBytesWritten) { - if (mReadListener) { - nsresult rv1; - - rv1 = mReadListener->OnDataAvailable(this, mReadContext, mReadPipeIn, - mReadOffset, - totalBytesWritten); - // - // If the consumer returns failure, then cancel the operation... - // - if (NS_FAILED(rv1)) { - rv = rv1; - } - } - mReadOffset += totalBytesWritten; - } - PR_LOG(gSocketLog, PR_LOG_DEBUG, ("--- Leaving nsSocketTransport::doRead() [%s:%d %x]. rv = %x.\t" "Total bytes read: %d\n\n", @@ -1254,6 +1274,31 @@ nsSocketTransport::SetReuseConnection(PRBool aReuse) return NS_OK; } +NS_IMETHODIMP +nsSocketTransport::GetBytesExpected (PRInt32 * bytes) +{ + if (bytes != NULL) + { + *bytes = mBytesExpected; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsSocketTransport::SetBytesExpected (PRInt32 bytes) +{ + if (mCurrentState == eSocketState_WaitReadWrite) + { + mBytesExpected = bytes; + + if (mBytesExpected == 0) + mService -> Wakeup (this); + } + + return NS_OK; +} + // // -------------------------------------------------------------------------- diff --git a/netwerk/base/src/nsSocketTransport.h b/netwerk/base/src/nsSocketTransport.h index b378c4c11b4..c52362ed98a 100644 --- a/netwerk/base/src/nsSocketTransport.h +++ b/netwerk/base/src/nsSocketTransport.h @@ -226,6 +226,7 @@ protected: PRInt32 mSuspendCount; PRInt32 mWriteCount; nsCOMPtr mWriteContext; + PRInt32 mBytesExpected; // The following four members are used when AsyncWrite(...) is called // with an nsIInputStream which does not also support the diff --git a/netwerk/base/src/nsSocketTransportService.cpp b/netwerk/base/src/nsSocketTransportService.cpp index 345910d7515..01a3aaa5275 100644 --- a/netwerk/base/src/nsSocketTransportService.cpp +++ b/netwerk/base/src/nsSocketTransportService.cpp @@ -412,6 +412,7 @@ nsSocketTransportService::Run(void) int i; count = PR_Poll(mSelectFDSet, mSelectFDSetCount, pollTimeout); + if (-1 == count) { // XXX: PR_Poll failed... What should happen? } @@ -587,6 +588,29 @@ nsSocketTransportService::ReuseTransport(nsIChannel* i_Transport, return NS_OK; } +/** + * wakeup the transport from PR_Poll () + */ + +NS_IMETHODIMP +nsSocketTransportService::Wakeup (nsIChannel* i_Transport) +{ + nsSocketTransport *transport = NS_STATIC_CAST (nsSocketTransport *, i_Transport); + + if (transport == NULL) + return NS_ERROR_NULL_POINTER; + + AddToWorkQ (transport); + +#ifdef USE_POLLABLE_EVENT + PR_SetPollableEvent (mThreadEvent); +#else + // XXX/ruslan: normally we would call PR_Interrupt (), but since it did work + // wait till NSPR fixes it one day +#endif + return NS_OK; +} + NS_IMETHODIMP nsSocketTransportService::Shutdown(void) { diff --git a/netwerk/protocol/http/public/nsIHTTPProtocolHandler.idl b/netwerk/protocol/http/public/nsIHTTPProtocolHandler.idl index 2db82aea0be..584c920d9e7 100644 --- a/netwerk/protocol/http/public/nsIHTTPProtocolHandler.idl +++ b/netwerk/protocol/http/public/nsIHTTPProtocolHandler.idl @@ -62,6 +62,7 @@ interface nsIHTTPProtocolHandler : nsIProtocolHandler in unsigned long encodeFlags); attribute string acceptLanguages; + attribute unsigned long httpVersion; /* i'll file a bug on the http guys to make nsAuthEngine scriptable and then we can get rid of this no script code! */ [noscript] void getAuthEngine(out nsAuthEnginePtr authEngine); diff --git a/netwerk/protocol/http/src/nsHTTPHandler.cpp b/netwerk/protocol/http/src/nsHTTPHandler.cpp index e8f20d85851..fa4f312a950 100644 --- a/netwerk/protocol/http/src/nsHTTPHandler.cpp +++ b/netwerk/protocol/http/src/nsHTTPHandler.cpp @@ -370,6 +370,23 @@ nsHTTPHandler::GetAcceptLanguages(char* *o_AcceptLanguages) } } +NS_IMETHODIMP +nsHTTPHandler::SetHttpVersion (unsigned int i_HttpVersion) +{ + mHttpVersion = i_HttpVersion; + return NS_OK; +} + +NS_IMETHODIMP +nsHTTPHandler::GetHttpVersion (unsigned int * o_HttpVersion) +{ + if (!o_HttpVersion) + return NS_ERROR_NULL_POINTER; + + *o_HttpVersion = mHttpVersion; + return NS_OK; +} + NS_IMETHODIMP nsHTTPHandler::GetAuthEngine(nsAuthEngine** o_AuthEngine) { @@ -532,6 +549,7 @@ nsHTTPHandler::SetMisc(const PRUnichar* aMisc) nsHTTPHandler::nsHTTPHandler(): mAcceptLanguages(nsnull), + mHttpVersion(HTTP_ONE_ZERO), mDoKeepAlive(PR_FALSE), mReferrerLevel(0) { @@ -764,7 +782,7 @@ nsresult nsHTTPHandler::RequestTransport(nsIURI* i_Uri, if (port == -1) GetDefaultPort(&port); - nsIChannel* trans; + nsIChannel* trans = nsnull; // Check in the idle transports for a host/port match count = 0; PRInt32 index = 0; @@ -772,7 +790,7 @@ nsresult nsHTTPHandler::RequestTransport(nsIURI* i_Uri, { mIdleTransports->Count(&count); - for (index=count-1; index >= 0; --index) + for (index=count-1; index >= 0; --index, trans = nsnull) { nsCOMPtr uri; trans = (nsIChannel*) mIdleTransports->ElementAt(index); @@ -796,7 +814,7 @@ nsresult nsHTTPHandler::RequestTransport(nsIURI* i_Uri, NS_ADDREF(trans); // Remove it from the idle mIdleTransports->RemoveElement(trans); - //break;// break out of the for loop + break;// break out of the for loop } } } @@ -806,7 +824,7 @@ nsresult nsHTTPHandler::RequestTransport(nsIURI* i_Uri, } } // if we didn't find any from the keep-alive idlelist - if (*o_pTrans == nsnull) + if (trans == nsnull) { // Ask the channel for proxy info... since that overrides PRBool usingProxy = PR_FALSE; @@ -1055,6 +1073,22 @@ nsHTTPHandler::PrefsChanged(const char* pref) mReferrerLevel = referrerLevel; } + nsXPIDLCString httpVersion; + rv = mPrefs -> CopyCharPref("network.http.version", getter_Copies(httpVersion)); + if (NS_SUCCEEDED (rv) && httpVersion) + { + if (!PL_strcmp (httpVersion, "1.1")) + mHttpVersion = HTTP_ONE_ONE; + else + if (!PL_strcmp (httpVersion, "0.9")) + mHttpVersion = HTTP_ZERO_NINE; + } + else + mHttpVersion = HTTP_ONE_ZERO; + + if (mHttpVersion == HTTP_ONE_ONE) + mDoKeepAlive = PR_TRUE; + // Things read only during initialization... if (bChangedAll) // intl.accept_languages { diff --git a/netwerk/protocol/http/src/nsHTTPHandler.h b/netwerk/protocol/http/src/nsHTTPHandler.h index 07cfad3952b..c68e29f6c26 100644 --- a/netwerk/protocol/http/src/nsHTTPHandler.h +++ b/netwerk/protocol/http/src/nsHTTPHandler.h @@ -104,6 +104,7 @@ protected: nsCOMPtr mIdleTransports; char* mAcceptLanguages; + PRUint32 mHttpVersion; nsAuthEngine mAuthEngine; PRBool mDoKeepAlive; nsCOMPtr mPrefs; diff --git a/netwerk/protocol/http/src/nsHTTPRequest.cpp b/netwerk/protocol/http/src/nsHTTPRequest.cpp index 78e82fbb44e..674817defe6 100644 --- a/netwerk/protocol/http/src/nsHTTPRequest.cpp +++ b/netwerk/protocol/http/src/nsHTTPRequest.cpp @@ -40,6 +40,7 @@ #include "nsIIOService.h" #include "nsAuthEngine.h" #include "nsIServiceManager.h" +#include "plstr.h" #if defined(PR_LOGGING) extern PRLogModuleInfo* gHTTPLog; @@ -120,6 +121,8 @@ nsHTTPRequest::nsHTTPRequest(nsIURI* i_URL, HTTPMethod i_Method): if (acceptLanguages && *acceptLanguages) SetHeader(nsHTTPAtoms::Accept_Language, acceptLanguages); } + + httpHandler -> GetHttpVersion (&mVersion); } @@ -292,7 +295,18 @@ nsresult nsHTTPRequest::WriteRequest() if (-1 != refLocation) mRequestBuffer.Truncate(refLocation); - mRequestBuffer.Append(" HTTP/1.0"CRLF); + char * httpVersion = " HTTP/1.0" CRLF; + + switch (mVersion) + { + case HTTP_ZERO_NINE: + httpVersion = " HTTP/0.9"CRLF; + break; + case HTTP_ONE_ONE: + httpVersion = " HTTP/1.1"CRLF; + } + + mRequestBuffer.Append (httpVersion); // // Write the request headers, if any... @@ -407,13 +421,13 @@ nsresult nsHTTPRequest::GetHeader(nsIAtom* i_Header, char* *o_Value) } -nsresult nsHTTPRequest::SetHTTPVersion(HTTPVersion i_Version) +nsresult nsHTTPRequest::SetHTTPVersion (PRUint32 i_Version) { mVersion = i_Version; return NS_OK; } -nsresult nsHTTPRequest::GetHTTPVersion(HTTPVersion* o_Version) +nsresult nsHTTPRequest::GetHTTPVersion (PRUint32 * o_Version) { *o_Version = mVersion; return NS_OK; diff --git a/netwerk/protocol/http/src/nsHTTPRequest.h b/netwerk/protocol/http/src/nsHTTPRequest.h index ed8648f673c..f5c829385f4 100644 --- a/netwerk/protocol/http/src/nsHTTPRequest.h +++ b/netwerk/protocol/http/src/nsHTTPRequest.h @@ -91,8 +91,8 @@ public: */ nsresult Clone(const nsHTTPRequest* *o_Copy) const; - nsresult SetHTTPVersion(HTTPVersion i_Version); - nsresult GetHTTPVersion(HTTPVersion* o_Version); + nsresult SetHTTPVersion (PRUint32 i_Version); + nsresult GetHTTPVersion (PRUint32 * o_Version); nsresult SetMethod(HTTPMethod i_Method); HTTPMethod GetMethod(void) const; @@ -142,7 +142,7 @@ protected: HTTPMethod mMethod; nsCOMPtr mURI; - HTTPVersion mVersion; + PRUint32 mVersion; nsCOMPtr mTransport; nsHTTPChannel* mConnection; diff --git a/netwerk/protocol/http/src/nsHTTPResponse.cpp b/netwerk/protocol/http/src/nsHTTPResponse.cpp index d7d32a1c23c..25cd4357339 100644 --- a/netwerk/protocol/http/src/nsHTTPResponse.cpp +++ b/netwerk/protocol/http/src/nsHTTPResponse.cpp @@ -51,6 +51,7 @@ nsHTTPResponse::nsHTTPResponse() // The content length is unknown... mContentLength = -1; + mChunkedResponse = PR_FALSE; } nsHTTPResponse::~nsHTTPResponse() @@ -433,6 +434,8 @@ nsresult nsHTTPResponse::ProcessHeader(nsIAtom* aHeader, nsCString& aValue) SetContentLength(length); } } + else if (nsHTTPAtoms::Transfer_Encoding == aHeader && !PL_strcmp (aValue, "chunked")) + mChunkedResponse = PR_TRUE; // // Set the response header... @@ -515,6 +518,11 @@ nsresult nsHTTPResponse::GetMaxAge(PRUint32* aMaxAge, PRBool* aMaxAgeIsPresent) } +PRBool nsHTTPResponse::isChunkedResponse () +{ + return mChunkedResponse; +} + // Check to see if a (cached) HTTP response is stale and, therefore, // must be revalidated with the origin server. // diff --git a/netwerk/protocol/http/src/nsHTTPResponse.h b/netwerk/protocol/http/src/nsHTTPResponse.h index 7ae1d027907..32ee5fd4f19 100644 --- a/netwerk/protocol/http/src/nsHTTPResponse.h +++ b/netwerk/protocol/http/src/nsHTTPResponse.h @@ -76,12 +76,14 @@ public: nsresult ParseHeaders(nsCString& aAllHeaders); nsresult ProcessHeader(nsIAtom* aHeader, nsCString& aValue); nsresult EmitHeaders(nsCString& aResult); - - nsresult UpdateHeaders(nsISimpleEnumerator *aEnumerator); - + PRBool IsStale(PRBool aUseHeuristicExpiration); - nsresult ParseDateHeader(nsIAtom *aAtom, PRTime *aResultTime, PRBool *aHeaderIsPresent); + PRBool isChunkedResponse (); + + nsresult UpdateHeaders(nsISimpleEnumerator *aEnumerator); + nsresult ParseDateHeader(nsIAtom *aAtom, PRTime *aResultTime, PRBool *aHeaderIsPresent); + protected: virtual ~nsHTTPResponse(); nsresult ParseDateHeader(nsIAtom *aAtom, PRUint32 *aResultTime, PRBool *aHeaderIsPresent); @@ -94,6 +96,8 @@ protected: PRUint32 mStatus; PRInt32 mContentLength; nsHTTPHeaderArray mHeaders; +private: + PRBool mChunkedResponse; }; #endif /* _nsHTTPResponse_h_ */ diff --git a/netwerk/protocol/http/src/nsHTTPResponseListener.cpp b/netwerk/protocol/http/src/nsHTTPResponseListener.cpp index 8f82a0691bc..69361d304bc 100644 --- a/netwerk/protocol/http/src/nsHTTPResponseListener.cpp +++ b/netwerk/protocol/http/src/nsHTTPResponseListener.cpp @@ -25,10 +25,13 @@ #include "nsIStreamListener.h" #include "nsHTTPResponseListener.h" #include "nsIChannel.h" +#include "nsISocketTransport.h" #include "nsIBufferInputStream.h" #include "nsHTTPChannel.h" #include "nsHTTPResponse.h" #include "nsCRT.h" +#include "nsIStreamConverterService.h" +#include "nsIStreamConverter.h" #include "nsHTTPAtoms.h" #include "nsIHttpNotify.h" @@ -53,12 +56,8 @@ extern PRLogModuleInfo* gHTTPLog; // static const int kMAX_HEADER_SIZE = 60000; +static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); -/////////////////////////////////////////////////////////////////////////////// -// -// nsHTTPResponseListener Implementation (abstract base class) -// -/////////////////////////////////////////////////////////////////////////////// nsHTTPResponseListener::nsHTTPResponseListener(nsHTTPChannel *aChannel) : mChannel(aChannel) { @@ -216,12 +215,15 @@ nsHTTPServerListener::nsHTTPServerListener(nsHTTPChannel* aChannel) mResponse(nsnull), mFirstLineParsed(PR_FALSE), mHeadersDone(PR_FALSE), - mBytesReceived(0) + mBytesReceived(0), + mBodyBytesReceived (0) { mChannel->mHTTPServerListener = this; PR_LOG(gHTTPLog, PR_LOG_ALWAYS, ("Creating nsHTTPServerListener [this=%x].\n", this)); + + mChunkConverterPushed = PR_FALSE; } nsHTTPServerListener::~nsHTTPServerListener() @@ -305,6 +307,7 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel, // rv = FinishedResponseHeaders(); if (NS_FAILED(rv)) return rv; + } // At this point we've digested headers from the server and we're @@ -334,6 +337,35 @@ nsHTTPServerListener::OnDataAvailable(nsIChannel* channel, if (NS_SUCCEEDED(rv)) { if (i_Length) { + + PRInt32 cl = -1; + mResponse -> GetContentLength (&cl); + + mBodyBytesReceived += i_Length; + + if (cl != -1) + { + nsresult rv; + nsCOMPtr trans = do_QueryInterface (channel, &rv); + + if (NS_SUCCEEDED (rv)) + trans -> SetBytesExpected (cl - mBodyBytesReceived); + } + + if (!mChunkConverterPushed && mResponse -> isChunkedResponse ()) + { + NS_WITH_SERVICE (nsIStreamConverterService, StreamConvService, kStreamConverterServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + nsString2 fromStr ( "chunked" ); + nsString2 toStr ("unchunked"); + nsCOMPtr converterListener; + rv = StreamConvService -> AsyncConvertData (fromStr.GetUnicode(), toStr.GetUnicode(), mResponseDataListener, channel, getter_AddRefs (converterListener)); + if (NS_FAILED(rv)) return rv; + mResponseDataListener = converterListener; + mChunkConverterPushed = PR_TRUE; + } + PR_LOG(gHTTPLog, PR_LOG_ALWAYS, ("\tOnDataAvailable [this=%x]. Calling consumer " "OnDataAvailable.\tlength:%d\n", this, i_Length)); diff --git a/netwerk/protocol/http/src/nsHTTPResponseListener.h b/netwerk/protocol/http/src/nsHTTPResponseListener.h index d8bc69e3512..bffe38943df 100644 --- a/netwerk/protocol/http/src/nsHTTPResponseListener.h +++ b/netwerk/protocol/http/src/nsHTTPResponseListener.h @@ -114,6 +114,8 @@ protected: nsCOMPtr mDataStream; PRUint32 mBytesReceived; + PRBool mChunkConverterPushed; + PRInt32 mBodyBytesReceived; }; diff --git a/netwerk/streamconv/converters/Makefile.in b/netwerk/streamconv/converters/Makefile.in index 3658db57999..d819610073a 100644 --- a/netwerk/streamconv/converters/Makefile.in +++ b/netwerk/streamconv/converters/Makefile.in @@ -38,6 +38,7 @@ CPPSRCS = \ nsConvFactories.cpp \ mozTXTToHTMLConv.cpp \ nsUnknownDecoder.cpp \ + nsHTTPChunkConv.cpp \ $(NULL) EXPORTS = \ diff --git a/netwerk/streamconv/converters/makefile.win b/netwerk/streamconv/converters/makefile.win index 7a8d3b013a5..a25abc4fa43 100644 --- a/netwerk/streamconv/converters/makefile.win +++ b/netwerk/streamconv/converters/makefile.win @@ -30,6 +30,7 @@ DLL=.\$(OBJDIR)\$(DLLNAME).dll EXPORTS = \ nsMultiMixedConv.h \ nsFTPDirListingConv.h \ + nsHTTPChunkConv.h \ $(NULL) LLIBS= $(LLIBS) \ @@ -55,6 +56,7 @@ CPP_OBJS = \ .\$(OBJDIR)\nsFTPDirListingConv.obj \ .\$(OBJDIR)\nsConvFactories.obj \ .\$(OBJDIR)\mozTXTToHTMLConv.obj \ + .\$(OBJDIR)\nsHTTPChunkConv.obj \ .\$(OBJDIR)\nsUnknownDecoder.obj \ $(NULL) diff --git a/netwerk/streamconv/converters/nsConvFactories.cpp b/netwerk/streamconv/converters/nsConvFactories.cpp index c9eb07b1145..07cdf3a6405 100644 --- a/netwerk/streamconv/converters/nsConvFactories.cpp +++ b/netwerk/streamconv/converters/nsConvFactories.cpp @@ -26,12 +26,14 @@ #include "nsFTPDirListingConv.h" #include "nsMultiMixedConv.h" +#include "nsHTTPChunkConv.h" #include "mozTXTToHTMLConv.h" #include "nsUnknownDecoder.h" nsresult NS_NewFTPDirListingConv(nsFTPDirListingConv** result); -nsresult NS_NewMultiMixedConv(nsMultiMixedConv** result); -nsresult MOZ_NewTXTToHTMLConv(mozTXTToHTMLConv** result); +nsresult NS_NewMultiMixedConv (nsMultiMixedConv** result); +nsresult MOZ_NewTXTToHTMLConv (mozTXTToHTMLConv** result); +nsresult NS_NewHTTPChunkConv (nsHTTPChunkConv ** result); static NS_IMETHODIMP @@ -106,6 +108,30 @@ CreateNewTXTToHTMLConvFactory(nsISupports* aOuter, REFNSIID aIID, void **aResult return rv; } +static NS_IMETHODIMP +CreateNewHTTPChunkConvFactory (nsISupports* aOuter, REFNSIID aIID, void **aResult) +{ + if (!aResult) { + return NS_ERROR_INVALID_POINTER; + } + if (aOuter) { + *aResult = nsnull; + return NS_ERROR_NO_AGGREGATION; + } + nsHTTPChunkConv* inst = nsnull; + nsresult rv = NS_NewHTTPChunkConv (&inst); + if (NS_FAILED(rv)) { + *aResult = nsnull; + return rv; + } + rv = inst->QueryInterface(aIID, aResult); + if (NS_FAILED(rv)) { + *aResult = nsnull; + } + NS_RELEASE(inst); /* get rid of extra refcnt */ + return rv; +} + static NS_IMETHODIMP CreateNewUnknownDecoderFactory(nsISupports *aOuter, REFNSIID aIID, void **aResult) { @@ -172,6 +198,19 @@ static nsModuleComponentInfo components[] = NS_ISTREAMCONVERTER_KEY "?from=application/x-unknown-content-type?to=*/*", CreateNewUnknownDecoderFactory }, + + { "HttpChunkConverter", + NS_HTTPCHUNKCONVERTER_CID, + NS_ISTREAMCONVERTER_KEY "?from=chunked?to=unchunked", + CreateNewHTTPChunkConvFactory + }, + + { "HttpChunkConverter", + NS_HTTPCHUNKCONVERTER_CID, + NS_ISTREAMCONVERTER_KEY "?from=unchunked?to=chunked", + CreateNewHTTPChunkConvFactory + }, + }; NS_IMPL_NSGETMODULE("nsConvModule", components); diff --git a/netwerk/streamconv/converters/nsHTTPChunkConv.cpp b/netwerk/streamconv/converters/nsHTTPChunkConv.cpp index 5ccb57de620..ecaf2de8618 100644 --- a/netwerk/streamconv/converters/nsHTTPChunkConv.cpp +++ b/netwerk/streamconv/converters/nsHTTPChunkConv.cpp @@ -25,6 +25,7 @@ #include "plstr.h" #include "prlog.h" #include "nsIChannel.h" +#include "nsISocketTransport.h" #include "nsCOMPtr.h" #include "nsIStringStream.h" #include "nsIStreamListener.h" @@ -78,6 +79,8 @@ nsHTTPChunkConv::AsyncConvertData ( // hook ourself up with the receiving listener. mListener = aListener; NS_ADDREF (mListener); + + mAsyncConvContext = aCtxt; return NS_OK; } @@ -175,24 +178,35 @@ nsHTTPChunkConv::OnDataAvailable ( // send data upstream { - nsIInputStream * convertedStream = nsnull; - nsCString convertedString (mChunkBuffer); - nsISupports * convertedStreamSup = nsnull; + if (mChunkBufferLength > 0) + { + nsIInputStream * convertedStream = nsnull; + nsCString convertedString (mChunkBuffer); + nsISupports * convertedStreamSup = nsnull; - rv = NS_NewStringInputStream (&convertedStreamSup, convertedString); - if (NS_FAILED (rv)) - return rv; + rv = NS_NewStringInputStream (&convertedStreamSup, convertedString); + if (NS_FAILED (rv)) + return rv; - rv = convertedStreamSup -> QueryInterface (NS_GET_IID(nsIInputStream), (void**)&convertedStream); - NS_RELEASE (convertedStreamSup); + rv = convertedStreamSup -> QueryInterface (NS_GET_IID(nsIInputStream), (void**)&convertedStream); + NS_RELEASE (convertedStreamSup); - if (NS_FAILED (rv)) - return rv; + if (NS_FAILED (rv)) + return rv; - rv = mListener -> OnDataAvailable (aChannel, aContext, convertedStream, aSourceOffset, mChunkBufferLength); + rv = mListener -> OnDataAvailable (aChannel, aContext, convertedStream, aSourceOffset, mChunkBufferLength); - if (NS_FAILED (rv)) - return rv; + if (NS_FAILED (rv)) + return rv; + } + else + { + nsresult rv; + nsCOMPtr trans = do_QueryInterface (mAsyncConvContext, &rv); + + if (NS_SUCCEEDED (rv)) + trans -> SetBytesExpected (0); + } mState = CHUNK_STATE_INIT; @@ -231,8 +245,13 @@ nsHTTPChunkConv::OnDataAvailable ( streamLen--; if (mState == CHUNK_STATE_LF) { - mChunkBuffer = (char * )nsAllocator::Alloc (mChunkBufferLength + 1); - mState = CHUNK_STATE_DATA; + if (mChunkBufferLength > 0) + { + mChunkBuffer = (char * )nsAllocator::Alloc (mChunkBufferLength + 1); + mState = CHUNK_STATE_DATA; + } + else + mState = CHUNK_STATE_CR_FINAL; } else mState = CHUNK_STATE_FINAL; @@ -266,15 +285,14 @@ nsHTTPChunkConv::OnDataAvailable ( case CHUNK_STATE_DATA: if (mChunkBufferLength - mChunkBufferPos <= streamLen) { - // entire chunk - rv = iStr -> Read (&mChunkBuffer[mChunkBufferPos], mChunkBufferLength - mChunkBufferPos, &rl); - if (NS_FAILED (rv)) - return rv; - - mChunkBufferPos += rl; - mChunkBuffer[mChunkBufferPos++] = 0; - streamLen -= rl; + // entire chunk + rv = iStr -> Read (&mChunkBuffer[mChunkBufferPos], mChunkBufferLength - mChunkBufferPos, &rl); + if (NS_FAILED (rv)) + return rv; + mChunkBufferPos += rl; + mChunkBuffer[mChunkBufferPos++] = 0; + streamLen -= rl; mState = CHUNK_STATE_CR_FINAL; } else diff --git a/netwerk/streamconv/converters/nsHTTPChunkConv.h b/netwerk/streamconv/converters/nsHTTPChunkConv.h index a6231ae7e33..7db38fc6d69 100644 --- a/netwerk/streamconv/converters/nsHTTPChunkConv.h +++ b/netwerk/streamconv/converters/nsHTTPChunkConv.h @@ -25,6 +25,7 @@ #include "nsIStreamConverter.h" #include "nsIFactory.h" +#include "nsCOMPtr.h" #define NS_HTTPCHUNKCONVERTER_CID \ { \ @@ -51,7 +52,7 @@ typedef enum enum_ChunkState CHUNK_STATE_DATA, CHUNK_STATE_CR_FINAL, CHUNK_STATE_LF_FINAL, - CHUNK_STATE_FINAL, + CHUNK_STATE_FINAL } ChunkState; #define HTTP_CHUNK_TYPE "chunked" @@ -83,7 +84,9 @@ private: char mLenBuf[20]; PRUint32 mLenBufCnt; + + nsCOMPtr mAsyncConvContext; }; -#endif \ No newline at end of file +#endif