From b247c6b53db49f2f568ef3be9080bbb0af2dc292 Mon Sep 17 00:00:00 2001 From: "darin%netscape.com" Date: Fri, 11 May 2001 21:04:09 +0000 Subject: [PATCH] Http branch landing: changes to netwerk/ (bug 76866) r=gagan,sr=dougt,a=chofmann git-svn-id: svn://10.0.0.236/trunk@94681 18797224-902f-48f8-a5cc-f745e15eee43 --- .../base/public/nsIProtocolProxyService.idl | 4 +- mozilla/netwerk/base/public/nsIProxy.idl | 8 +- mozilla/netwerk/base/public/nsITransport.idl | 22 +- mozilla/netwerk/base/public/nsNetUtil.h | 27 +- mozilla/netwerk/base/src/nsFileTransport.cpp | 52 +- mozilla/netwerk/base/src/nsFileTransport.h | 6 +- mozilla/netwerk/base/src/nsIOService.cpp | 54 +- mozilla/netwerk/base/src/nsIOService.h | 4 +- .../base/src/nsProtocolProxyService.cpp | 96 +- .../netwerk/base/src/nsSocketTransport.cpp | 84 +- mozilla/netwerk/base/src/nsSocketTransport.h | 7 +- .../netwerk/base/src/nsStorageTransport.cpp | 67 +- .../base/src/nsStreamListenerProxy.cpp | 4 +- .../base/src/nsStreamProviderProxy.cpp | 4 +- mozilla/netwerk/build/nsNetCID.h | 25 +- mozilla/netwerk/build/nsNetModule.cpp | 28 + mozilla/netwerk/cache/src/nsCacheEntry.cpp | 2 +- .../cache/src/nsCacheEntryDescriptor.cpp | 2 +- .../protocol/ftp/public/nsIFTPChannel.idl | 2 - .../netwerk/protocol/ftp/src/nsFTPChannel.cpp | 107 +- .../netwerk/protocol/ftp/src/nsFTPChannel.h | 10 - .../ftp/src/nsFtpConnectionThread.cpp | 38 - .../ftp/src/nsFtpControlConnection.cpp | 4 +- .../protocol/ftp/src/nsFtpProtocolHandler.cpp | 70 +- .../protocol/ftp/src/nsFtpProtocolHandler.h | 2 - .../protocol/gopher/src/nsGopherChannel.cpp | 103 +- .../protocol/gopher/src/nsGopherChannel.h | 11 +- .../protocol/gopher/src/nsGopherHandler.cpp | 80 +- mozilla/netwerk/protocol/http/public/MANIFEST | 1 - .../netwerk/protocol/http/public/MANIFEST_IDL | 8 +- .../netwerk/protocol/http/public/Makefile.in | 16 +- .../netwerk/protocol/http/public/makefile.win | 13 +- .../http/public/nsIHttpAuthenticator.idl | 44 + .../protocol/http/public/nsIHttpChannel.idl | 106 + .../protocol/http/public/nsIHttpEventSink.idl | 53 + .../protocol/http/public/nsIHttpNotify.idl | 54 + .../http/public/nsIHttpProtocolHandler.idl | 127 + mozilla/netwerk/protocol/http/src/Makefile.in | 26 +- .../netwerk/protocol/http/src/makefile.win | 33 +- mozilla/netwerk/protocol/http/src/nsHttp.cpp | 172 ++ mozilla/netwerk/protocol/http/src/nsHttp.h | 120 + .../protocol/http/src/nsHttpAtomList.h | 117 + .../protocol/http/src/nsHttpAuthCache.cpp | 383 +++ .../protocol/http/src/nsHttpAuthCache.h | 138 ++ .../protocol/http/src/nsHttpBasicAuth.cpp | 98 + .../protocol/http/src/nsHttpBasicAuth.h | 47 + .../protocol/http/src/nsHttpChannel.cpp | 2174 +++++++++++++++++ .../netwerk/protocol/http/src/nsHttpChannel.h | 153 ++ .../http/src/nsHttpChunkedDecoder.cpp | 133 + .../protocol/http/src/nsHttpChunkedDecoder.h | 53 + .../protocol/http/src/nsHttpConnection.cpp | 642 +++++ .../protocol/http/src/nsHttpConnection.h | 191 ++ .../protocol/http/src/nsHttpHandler.cpp | 1633 +++++++++++++ .../netwerk/protocol/http/src/nsHttpHandler.h | 246 ++ .../protocol/http/src/nsHttpHeaderArray.cpp | 171 ++ .../protocol/http/src/nsHttpHeaderArray.h | 71 + .../protocol/http/src/nsHttpRequestHead.cpp | 57 + .../protocol/http/src/nsHttpRequestHead.h | 65 + .../protocol/http/src/nsHttpResponseHead.cpp | 472 ++++ .../protocol/http/src/nsHttpResponseHead.h | 104 + .../protocol/http/src/nsHttpTransaction.cpp | 606 +++++ .../protocol/http/src/nsHttpTransaction.h | 117 + .../netwerk/socket/base/nsSOCKSIOLayer.cpp | 11 +- .../converters/nsFTPDirListingConv.cpp | 1 - .../converters/nsMultiMixedConv.cpp | 15 +- mozilla/netwerk/test/Makefile.in | 2 +- mozilla/netwerk/test/TestProtocols.cpp | 291 +-- mozilla/netwerk/test/makefile.win | 5 +- 68 files changed, 8835 insertions(+), 857 deletions(-) create mode 100644 mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl create mode 100644 mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl create mode 100644 mozilla/netwerk/protocol/http/public/nsIHttpEventSink.idl create mode 100644 mozilla/netwerk/protocol/http/public/nsIHttpNotify.idl create mode 100644 mozilla/netwerk/protocol/http/public/nsIHttpProtocolHandler.idl create mode 100644 mozilla/netwerk/protocol/http/src/nsHttp.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttp.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpAtomList.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpChannel.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpConnection.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpHandler.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpRequestHead.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp create mode 100644 mozilla/netwerk/protocol/http/src/nsHttpTransaction.h diff --git a/mozilla/netwerk/base/public/nsIProtocolProxyService.idl b/mozilla/netwerk/base/public/nsIProtocolProxyService.idl index 88187fa2b59..df79c8a721a 100644 --- a/mozilla/netwerk/base/public/nsIProtocolProxyService.idl +++ b/mozilla/netwerk/base/public/nsIProtocolProxyService.idl @@ -22,7 +22,6 @@ #include "nsISupports.idl" #include "nsIURI.idl" -#include "nsIProxy.idl" %{C++ #define NS_PROTOCOLPROXYSERVICE_CID \ @@ -35,7 +34,8 @@ interface nsIProtocolProxyService : nsISupports { readonly attribute PRBool proxyEnabled; - void examineForProxy(in nsIURI aURI, in nsIProxy aProxy); + void examineForProxy(in nsIURI aURI, out string host, out long port, out string type); + /** * Add additional items in the "No proxy for" list. This is above * the list specified the user-pref. diff --git a/mozilla/netwerk/base/public/nsIProxy.idl b/mozilla/netwerk/base/public/nsIProxy.idl index 2bb503c303c..d5d8e47f3c8 100644 --- a/mozilla/netwerk/base/public/nsIProxy.idl +++ b/mozilla/netwerk/base/public/nsIProxy.idl @@ -34,10 +34,6 @@ [scriptable, uuid(0492D011-CD2F-11d2-B013-006097BFC036)] interface nsIProxy : nsISupports { - attribute string proxyHost; - - /* -1 on Set call indicates switch to default port */ - attribute long proxyPort; - - attribute string proxyType; + }; + diff --git a/mozilla/netwerk/base/public/nsITransport.idl b/mozilla/netwerk/base/public/nsITransport.idl index 2acbea2e33a..7d1444d70e3 100644 --- a/mozilla/netwerk/base/public/nsITransport.idl +++ b/mozilla/netwerk/base/public/nsITransport.idl @@ -40,9 +40,21 @@ interface nsITransport : nsISupports /** * Get/set notificationCallbacks for this transport. */ - readonly attribute nsIInterfaceRequestor notificationCallbacks; + nsIInterfaceRequestor getNotificationCallbacks(); void setNotificationCallbacks(in nsIInterfaceRequestor callbacks, - in boolean isBackground); + in unsigned long flags); + + /** + * If the notificationCallbacks provide a nsIProgressEventSink + * implementation, then progress is by default reported to the thread + * that called setNotificationCallbacks. The following flags provide + * finer control over progress notifications: + * + * DONT_REPORT_PROGRESS - progress notifications are not sent. + * DONT_PROXY_PROGRESS - progress notifications can occur on any thread. + */ + const unsigned long DONT_REPORT_PROGRESS = 1; + const unsigned long DONT_PROXY_PROGRESS = 2; /** * Open an input stream on this transport. @@ -104,9 +116,9 @@ interface nsITransport : nsISupports * ask the transport to not proxy the callback. The caller must then * be prepared to handle callbacks on any thread. */ - const unsigned long DONT_PROXY_STREAM_OBSERVER = 1 << 0; - const unsigned long DONT_PROXY_STREAM_LISTENER = 1 << 1; - const unsigned long DONT_PROXY_STREAM_PROVIDER = 1 << 1; + const unsigned long DONT_PROXY_OBSERVER = 1 << 0; + const unsigned long DONT_PROXY_LISTENER = 1 << 1; + const unsigned long DONT_PROXY_PROVIDER = 1 << 1; }; diff --git a/mozilla/netwerk/base/public/nsNetUtil.h b/mozilla/netwerk/base/public/nsNetUtil.h index 1e63a6f79e4..268a87ef4e0 100644 --- a/mozilla/netwerk/base/public/nsNetUtil.h +++ b/mozilla/netwerk/base/public/nsNetUtil.h @@ -41,11 +41,14 @@ #include "nsITransport.h" #include "nsMemory.h" #include "nsCOMPtr.h" -#include "nsIHTTPProtocolHandler.h" #include "nsIDownloader.h" #include "nsIStreamLoader.h" #include "nsIStreamIO.h" #include "nsIPipe.h" +#include "nsIProtocolHandler.h" +#include "nsIStringStream.h" +#include "nsILocalFile.h" +#include "nsIFileStreams.h" #include "nsXPIDLString.h" #include "prio.h" // for read/write flags, permissions, etc. @@ -221,22 +224,22 @@ NS_NewPostDataStream(nsIInputStream **result, nsIIOService* ioService = nsnull) // pass in nsIIOService to optimize callers { nsresult rv; - nsCOMPtr serv; - if (ioService == nsnull) { - serv = do_GetIOService(&rv); + if (isFile) { + nsCOMPtr file; + + rv = NS_NewLocalFile(data, PR_FALSE, getter_AddRefs(file)); if (NS_FAILED(rv)) return rv; - ioService = serv.get(); + + return NS_NewLocalFileInputStream(result, file); } - nsCOMPtr handler; - rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler)); + // otherwise, create a string stream for the data + nsCOMPtr sup; + rv = NS_NewCStringInputStream(getter_AddRefs(sup), nsCAutoString(data)); if (NS_FAILED(rv)) return rv; - nsCOMPtr http = do_QueryInterface(handler, &rv); - if (NS_FAILED(rv)) return rv; - - return http->NewPostDataStream(isFile, data, encodeFlags, result); + return CallQueryInterface(sup, result); } inline nsresult @@ -583,7 +586,7 @@ NS_AsyncWriteFromStream(nsIRequest **aRequest, // // So, let's always make this optimization. // - aFlags |= nsITransport::DONT_PROXY_STREAM_PROVIDER; + aFlags |= nsITransport::DONT_PROXY_PROVIDER; return aTransport->AsyncWrite(provider, aContext, aOffset, diff --git a/mozilla/netwerk/base/src/nsFileTransport.cpp b/mozilla/netwerk/base/src/nsFileTransport.cpp index 5c53f21277b..e6e9901304a 100644 --- a/mozilla/netwerk/base/src/nsFileTransport.cpp +++ b/mozilla/netwerk/base/src/nsFileTransport.cpp @@ -770,9 +770,9 @@ nsFileTransport::Process(void) LOG(("nsFileTransport: READING [this=%x %s] read %u bytes [offset=%u]\n", this, mStreamName.get(), total, mOffset)); - if (mProgress) - mProgress->OnProgress(this, mContext, offset, - PR_MAX(mTotalAmount, 0)); + if (mProgressSink) + mProgressSink->OnProgress(this, mContext, offset, + PR_MAX(mTotalAmount, 0)); } break; } @@ -813,12 +813,12 @@ nsFileTransport::Process(void) saveListener->OnStopRequest(this, saveContext, mStatus); saveListener = 0; } - if (mProgress) { + if (mProgressSink) { nsAutoString fileName; fileName.AssignWithConversion(mStreamName); - mProgress->OnStatus(this, saveContext, - NS_NET_STATUS_READ_FROM, - fileName.GetUnicode()); + mProgressSink->OnStatus(this, saveContext, + NS_NET_STATUS_READ_FROM, + fileName.GetUnicode()); } break; @@ -947,9 +947,9 @@ nsFileTransport::Process(void) LOG(("nsFileTransport: WRITING [this=%x %s] wrote %u bytes [offset=%u]\n", this, mStreamName.get(), total, mOffset)); - if (mProgress) - mProgress->OnProgress(this, mContext, offset, - PR_MAX(mTotalAmount, 0)); + if (mProgressSink) + mProgressSink->OnProgress(this, mContext, offset, + PR_MAX(mTotalAmount, 0)); } break; } @@ -980,10 +980,10 @@ nsFileTransport::Process(void) mProvider->OnStopRequest(this, mContext, mStatus); mProvider = 0; } - if (mProgress) { + if (mProgressSink) { nsAutoString fileName; fileName.AssignWithConversion(mStreamName); - nsresult rv = mProgress->OnStatus(this, mContext, - NS_NET_STATUS_WROTE_TO, + nsresult rv = mProgressSink->OnStatus(this, mContext, + NS_NET_STATUS_WROTE_TO, fileName.GetUnicode()); NS_ASSERTION(NS_SUCCEEDED(rv), "unexpected OnStatus failure"); } @@ -1037,7 +1037,8 @@ nsFileTransport::GetSecurityInfo(nsISupports * *aSecurityInfo) } NS_IMETHODIMP -nsFileTransport::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { +nsFileTransport::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) +{ NS_ENSURE_ARG_POINTER(aCallbacks); *aCallbacks = mNotificationCallbacks; NS_IF_ADDREF(*aCallbacks); @@ -1045,25 +1046,34 @@ nsFileTransport::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { } NS_IMETHODIMP -nsFileTransport::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks, - PRBool isBackground) { +nsFileTransport::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks, + PRUint32 aFlags) +{ mNotificationCallbacks = aCallbacks; - mProgress = 0; - if (!mNotificationCallbacks || isBackground) return NS_OK; + mProgressSink = 0; + + if (!mNotificationCallbacks || (aFlags & DONT_REPORT_PROGRESS)) + return NS_OK; nsCOMPtr sink(do_GetInterface(mNotificationCallbacks)); - if (!sink) return NS_ERROR_FAILURE; + if (!sink) return NS_OK; + if (aFlags & DONT_PROXY_PROGRESS) { + mProgressSink = sink; + return NS_OK; + } + + // Otherwise, generate a proxied event sink nsresult rv; NS_WITH_SERVICE(nsIProxyObjectManager, proxyMgr, kProxyObjectManagerCID, &rv); if (NS_FAILED(rv)) return rv; - return proxyMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ, // primordial thread - should change? + return proxyMgr->GetProxyForObject(NS_CURRENT_EVENTQ, // calling thread NS_GET_IID(nsIProgressEventSink), sink, PROXY_ASYNC | PROXY_ALWAYS, - getter_AddRefs(mProgress)); + getter_AddRefs(mProgressSink)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/netwerk/base/src/nsFileTransport.h b/mozilla/netwerk/base/src/nsFileTransport.h index 9b0ea56caf7..16d07d91fb4 100644 --- a/mozilla/netwerk/base/src/nsFileTransport.h +++ b/mozilla/netwerk/base/src/nsFileTransport.h @@ -103,7 +103,7 @@ public: }; protected: - nsCOMPtr mProgress; + nsCOMPtr mProgressSink; nsCOMPtr mNotificationCallbacks; nsCOMPtr mStreamIO; char *mContentType; @@ -150,7 +150,7 @@ protected: #endif }; -#define NS_FILE_TRANSPORT_DEFAULT_SEGMENT_SIZE (2*1024) -#define NS_FILE_TRANSPORT_DEFAULT_BUFFER_SIZE (8*1024) +#define NS_FILE_TRANSPORT_DEFAULT_SEGMENT_SIZE (4096) +#define NS_FILE_TRANSPORT_DEFAULT_BUFFER_SIZE (4*4096) // 16k max #endif // nsFileTransport_h__ diff --git a/mozilla/netwerk/base/src/nsIOService.cpp b/mozilla/netwerk/base/src/nsIOService.cpp index 53c71724d63..0087676c249 100644 --- a/mozilla/netwerk/base/src/nsIOService.cpp +++ b/mozilla/netwerk/base/src/nsIOService.cpp @@ -35,12 +35,14 @@ #include "nsIErrorService.h" #include "netCore.h" #include "nsIObserverService.h" +#include "nsIHttpProtocolHandler.h" static NS_DEFINE_CID(kFileTransportService, NS_FILETRANSPORTSERVICE_CID); static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID); static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID); +static NS_DEFINE_CID(kProtocolProxyServiceCID, NS_PROTOCOLPROXYSERVICE_CID); //////////////////////////////////////////////////////////////////////////////// @@ -75,9 +77,16 @@ nsIOService::Init() NS_GET_IID(nsIFileTransportService), getter_AddRefs(mFileTransportService)); if (NS_FAILED(rv)) return rv; + rv = nsServiceManager::GetService(kDNSServiceCID, NS_GET_IID(nsIDNSService), getter_AddRefs(mDNSService)); + if (NS_FAILED(rv)) return rv; + + rv = nsServiceManager::GetService(kProtocolProxyServiceCID, + NS_GET_IID(nsIProtocolProxyService), + getter_AddRefs(mProxyService)); + if (NS_FAILED(rv)) return rv; // XXX hack until xpidl supports error info directly (http://bugzilla.mozilla.org/show_bug.cgi?id=13423) nsCOMPtr errorService = do_GetService(kErrorServiceCID, &rv); @@ -254,8 +263,7 @@ nsIOService::ExtractScheme(const char* inURI, PRUint32 *startPos, } nsresult -nsIOService::NewURI(const char* aSpec, nsIURI* aBaseURI, - nsIURI* *result, nsIProtocolHandler* *hdlrResult) +nsIOService::NewURI(const char* aSpec, nsIURI* aBaseURI, nsIURI* *result) { nsresult rv; nsIURI* base; @@ -281,20 +289,9 @@ nsIOService::NewURI(const char* aSpec, nsIURI* aBaseURI, nsCRT::free(scheme); if (NS_FAILED(rv)) return rv; - if (hdlrResult) { - *hdlrResult = handler; - NS_ADDREF(*hdlrResult); - } return handler->NewURI(aSpec, base, result); } -NS_IMETHODIMP -nsIOService::NewURI(const char* aSpec, nsIURI* aBaseURI, - nsIURI* *result) -{ - return NewURI(aSpec, aBaseURI, result, nsnull); -} - NS_IMETHODIMP nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result) { @@ -309,6 +306,31 @@ nsIOService::NewChannelFromURI(nsIURI *aURI, nsIChannel **result) rv = GetProtocolHandler((const char*)scheme, getter_AddRefs(handler)); if (NS_FAILED(rv)) return rv; + PRBool useProxy = PR_FALSE; + if (mProxyService) { + mProxyService->GetProxyEnabled(&useProxy); + if (useProxy) { + nsXPIDLCString host, type; + PRInt32 port; + rv = mProxyService->ExamineForProxy(aURI, getter_Copies(host), &port, getter_Copies(type)); + if (NS_SUCCEEDED(rv) && type.get()) + { + // we are going to proxy this channel and the only protocol that can do proxying is http + rv = GetProtocolHandler("http", getter_AddRefs(handler)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr httpHandler = do_QueryInterface(handler, &rv); + if (NS_FAILED(rv)) + return rv; + + if (!httpHandler) + return NS_ERROR_NULL_POINTER; + + return httpHandler->NewProxyChannel(aURI, host, port, type, result); + } + } + } + rv = handler->NewChannel(aURI, result); return rv; } @@ -318,12 +340,10 @@ nsIOService::NewChannel(const char *aSpec, nsIURI *aBaseURI, nsIChannel **result { nsresult rv; nsCOMPtr uri; - nsCOMPtr handler; - rv = NewURI(aSpec, aBaseURI, getter_AddRefs(uri), getter_AddRefs(handler)); + rv = NewURI(aSpec, aBaseURI, getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; - rv = handler->NewChannel(uri, result); - return rv; + return NewChannelFromURI(uri, result); } NS_IMETHODIMP diff --git a/mozilla/netwerk/base/src/nsIOService.h b/mozilla/netwerk/base/src/nsIOService.h index 6bca79c2254..3a4493d4a47 100644 --- a/mozilla/netwerk/base/src/nsIOService.h +++ b/mozilla/netwerk/base/src/nsIOService.h @@ -28,6 +28,7 @@ #include "nsISocketTransportService.h" #include "nsIFileTransportService.h" #include "nsIDNSService.h" +#include "nsIProtocolProxyService.h" #include "nsCOMPtr.h" #include "nsURLHelper.h" #include "nsWeakPtr.h" @@ -67,8 +68,9 @@ protected: nsCOMPtr mSocketTransportService; nsCOMPtr mFileTransportService; nsCOMPtr mDNSService; + nsCOMPtr mProxyService; nsCOMPtr mEventQueueService; - + // Cached protocol handlers nsWeakPtr mWeakHandler[NS_N(gScheme)]; }; diff --git a/mozilla/netwerk/base/src/nsProtocolProxyService.cpp b/mozilla/netwerk/base/src/nsProtocolProxyService.cpp index 945871da14a..65da67db528 100644 --- a/mozilla/netwerk/base/src/nsProtocolProxyService.cpp +++ b/mozilla/netwerk/base/src/nsProtocolProxyService.cpp @@ -266,8 +266,7 @@ nsProtocolProxyService::PrefsChanged(const char* pref) { nsProtocolProxyService::DestroyPACLoadEvent); // post the event into the ui event queue - rv = eq->PostEvent(event); - if (rv == PR_FAILURE) { + if (eq->PostEvent(event) == PR_FAILURE) { NS_ERROR("Failed to post PAC load event to UI EventQueue"); NS_RELEASE_THIS(); delete event; @@ -369,19 +368,22 @@ nsProtocolProxyService::CanUseProxy(nsIURI* aURI) // nsIProtocolProxyService NS_IMETHODIMP -nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxy *aProxy) { +nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, char * *aProxyHost, PRInt32 *aProxyPort, char * *aProxyType) { nsresult rv = NS_OK; - NS_ASSERTION(aURI && aProxy, "need a uri and proxy iface folks."); + NS_ASSERTION(aURI, "need a uri folks."); + NS_ENSURE_ARG_POINTER(aProxyHost); + NS_ENSURE_ARG_POINTER(aProxyType); + + *aProxyHost = nsnull; + *aProxyType = nsnull; + *aProxyPort = -1; + // if proxies are enabled and this host:port combo is // supposed to use a proxy, check for a proxy. if ((0 == mUseProxy) || ((1 == mUseProxy) && !CanUseProxy(aURI))) { - rv = aProxy->SetProxyHost(nsnull); - if (NS_FAILED(rv)) return rv; - rv = aProxy->SetProxyPort(-1); - if (NS_FAILED(rv)) return rv; return NS_OK; } @@ -393,22 +395,11 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxy *aProxy) { return NS_ERROR_NULL_POINTER; } - nsXPIDLCString p_host; - nsXPIDLCString p_type; - PRInt32 p_port; - if (NS_SUCCEEDED(rv = mPAC->ProxyForURL(aURI, - getter_Copies(p_host), - &p_port, - getter_Copies(p_type)))) - { - if (NS_FAILED(rv = aProxy->SetProxyHost(p_host))) - return rv; - if (NS_FAILED(rv = aProxy->SetProxyPort(p_port))) - return rv; - if (NS_FAILED(rv = aProxy->SetProxyType(p_type))) - return rv; - return NS_OK; - } + rv = mPAC->ProxyForURL(aURI, + aProxyHost, + aProxyPort, + aProxyType); + return rv; } @@ -416,46 +407,45 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxy *aProxy) { rv = aURI->GetScheme(getter_Copies(scheme)); if (NS_FAILED(rv)) return rv; - if (mFTPProxyHost.get()[0] && mFTPProxyPort > 0 && - !PL_strcasecmp(scheme, "ftp")) { - rv = aProxy->SetProxyHost(mFTPProxyHost); - if (NS_FAILED(rv)) return rv; - aProxy->SetProxyType("http"); - return aProxy->SetProxyPort(mFTPProxyPort); - } - - if (mGopherProxyHost.get()[0] && mGopherProxyPort > 0 && - !PL_strcasecmp(scheme, "gopher")) { - rv = aProxy->SetProxyHost(mGopherProxyHost); - if (NS_FAILED(rv)) return rv; - aProxy->SetProxyType("http"); - return aProxy->SetProxyPort(mGopherProxyPort); - } - if (mHTTPProxyHost.get()[0] && mHTTPProxyPort > 0 && !PL_strcasecmp(scheme, "http")) { - rv = aProxy->SetProxyHost(mHTTPProxyHost); - if (NS_FAILED(rv)) return rv; - aProxy->SetProxyType("http"); - return aProxy->SetProxyPort(mHTTPProxyPort); + *aProxyHost = PL_strdup(mHTTPProxyHost); + *aProxyType = PL_strdup("http"); + *aProxyPort = mHTTPProxyPort; + return NS_OK; } if (mHTTPSProxyHost.get()[0] && mHTTPSProxyPort > 0 && !PL_strcasecmp(scheme, "https")) { - rv = aProxy->SetProxyHost(mHTTPSProxyHost); - if (NS_FAILED(rv)) return rv; - aProxy->SetProxyType("http"); - return aProxy->SetProxyPort(mHTTPSProxyPort); + *aProxyHost = PL_strdup(mHTTPSProxyHost); + *aProxyType = PL_strdup("http"); + *aProxyPort = mHTTPSProxyPort; + return NS_OK; + } + + if (mFTPProxyHost.get()[0] && mFTPProxyPort > 0 && + !PL_strcasecmp(scheme, "ftp")) { + *aProxyHost = PL_strdup(mFTPProxyHost); + *aProxyType = PL_strdup("http"); + *aProxyPort = mFTPProxyPort; + return NS_OK; + } + + if (mGopherProxyHost.get()[0] && mGopherProxyPort > 0 && + !PL_strcasecmp(scheme, "gopher")) { + *aProxyHost = PL_strdup(mGopherProxyHost); + *aProxyType = PL_strdup("http"); + *aProxyPort = mGopherProxyPort; + return NS_OK; } if (mSOCKSProxyHost.get()[0] && mSOCKSProxyPort > 0) { - rv = aProxy->SetProxyHost(mSOCKSProxyHost); - if (NS_FAILED(rv)) return rv; - aProxy->SetProxyType("socks"); - return aProxy->SetProxyPort(mSOCKSProxyPort); + *aProxyHost = PL_strdup(mSOCKSProxyHost); + *aProxyType = PL_strdup("socks"); + *aProxyPort = mSOCKSProxyPort; + return NS_OK; } - return NS_OK; } diff --git a/mozilla/netwerk/base/src/nsSocketTransport.cpp b/mozilla/netwerk/base/src/nsSocketTransport.cpp index ddad2d04c88..8ad42e06a2c 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport.cpp +++ b/mozilla/netwerk/base/src/nsSocketTransport.cpp @@ -1199,7 +1199,7 @@ nsSocketTransport::GetBytesExpected (PRInt32 * bytes) } NS_IMETHODIMP -nsSocketTransport::SetBytesExpected (PRInt32 bytes) +nsSocketTransport::SetBytesExpected(PRInt32 bytes) { nsAutoMonitor mon(mMonitor); @@ -1220,7 +1220,7 @@ nsSocketTransport::GetSecurityInfo(nsISupports **info) } NS_IMETHODIMP -nsSocketTransport::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) +nsSocketTransport::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) { NS_ENSURE_ARG_POINTER(aCallbacks); *aCallbacks = mNotificationCallbacks; @@ -1229,27 +1229,34 @@ nsSocketTransport::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) } NS_IMETHODIMP -nsSocketTransport::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks, - PRBool isBackground) +nsSocketTransport::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks, + PRUint32 aFlags) { mNotificationCallbacks = aCallbacks; - mEventSink = 0; - if (!mNotificationCallbacks || isBackground) return NS_OK; + mProgressSink = 0; + + if (!mNotificationCallbacks || (aFlags & DONT_REPORT_PROGRESS)) + return NS_OK; nsCOMPtr sink(do_GetInterface(mNotificationCallbacks)); - if (!sink) return NS_ERROR_FAILURE; + if (!sink) return NS_OK; - // Now generate a proxied event sink- + if (aFlags & DONT_PROXY_PROGRESS) { + mProgressSink = sink; + return NS_OK; + } + + // Otherwise, generate a proxied event sink nsresult rv; NS_WITH_SERVICE(nsIProxyObjectManager, proxyMgr, kProxyObjectManagerCID, &rv); if (NS_FAILED(rv)) return rv; - return proxyMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ, // primordial thread - should change? + return proxyMgr->GetProxyForObject(NS_CURRENT_EVENTQ, // calling thread NS_GET_IID(nsIProgressEventSink), sink, PROXY_ASYNC | PROXY_ALWAYS, - getter_AddRefs(mEventSink)); + getter_AddRefs(mProgressSink)); } @@ -1299,10 +1306,10 @@ nsSocketTransport::Dispatch(nsSocketRequest *req) nsresult nsSocketTransport::OnProgress(nsSocketRequest *req, nsISupports *ctx, PRUint32 offset) { - if (mEventSink) + if (mProgressSink) // we don't have content length info at the socket level // just pass 0 through. - mEventSink->OnProgress(req, ctx, offset, 0); + mProgressSink->OnProgress(req, ctx, offset, 0); return NS_OK; } @@ -1418,9 +1425,9 @@ nsSocketTransport::AsyncRead(nsIStreamListener* aListener, if (NS_SUCCEEDED(rv)) { // Proxy the stream listener and observer methods by default. - if (!(aFlags & nsITransport::DONT_PROXY_STREAM_OBSERVER)) { + if (!(aFlags & nsITransport::DONT_PROXY_OBSERVER)) { // Cannot proxy the listener unless the observer part is also proxied. - if (!(aFlags & nsITransport::DONT_PROXY_STREAM_PROVIDER)) { + if (!(aFlags & nsITransport::DONT_PROXY_PROVIDER)) { rv = NS_NewStreamListenerProxy(getter_AddRefs(listener), aListener, nsnull, mBufferSegmentSize, @@ -1495,9 +1502,9 @@ nsSocketTransport::AsyncWrite(nsIStreamProvider* aProvider, if (NS_SUCCEEDED(rv)) { // Proxy the stream provider and observer methods by default. - if (!(aFlags & nsITransport::DONT_PROXY_STREAM_OBSERVER)) { + if (!(aFlags & nsITransport::DONT_PROXY_OBSERVER)) { // Cannot proxy the provider unless the observer part is also proxied. - if (!(aFlags & nsITransport::DONT_PROXY_STREAM_PROVIDER)) { + if (!(aFlags & nsITransport::DONT_PROXY_PROVIDER)) { rv = NS_NewStreamProviderProxy(getter_AddRefs(provider), aProvider, nsnull, mBufferSegmentSize, @@ -1758,11 +1765,11 @@ nsSocketTransport::SetSocketConnectTimeout (PRUint32 a_Seconds) nsresult nsSocketTransport::OnStatus(nsSocketRequest *req, nsISupports *ctxt, nsresult message) { - if (!mEventSink) + if (!mProgressSink) return NS_ERROR_FAILURE; nsAutoString host; host.AssignWithConversion(mHostName); - return mEventSink->OnStatus(req, ctxt, message, host.GetUnicode()); + return mProgressSink->OnStatus(req, ctxt, message, host.GetUnicode()); } nsresult @@ -2191,7 +2198,9 @@ nsSocketBOS::SetObserver(nsIOutputStreamObserver *aObserver) //---------------------------------------------------------------------------- // -NS_IMPL_THREADSAFE_ISUPPORTS1(nsSocketIS, nsIInputStream) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsSocketIS, + nsIInputStream, + nsISeekableStream) nsSocketIS::nsSocketIS() : mOffset(0) @@ -2279,13 +2288,29 @@ nsSocketIS::SetObserver(nsIInputStreamObserver *aObserver) return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsSocketIS::Seek(PRInt32 whence, PRInt32 offset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsSocketIS::Tell(PRUint32 *offset) +{ + NS_ENSURE_ARG_POINTER(offset); + *offset = mOffset; + return NS_OK; +} + // //---------------------------------------------------------------------------- // nsSocketOS //---------------------------------------------------------------------------- // -NS_IMPL_THREADSAFE_ISUPPORTS1(nsSocketOS, nsIOutputStream) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsSocketOS, + nsIOutputStream, + nsISeekableStream) nsSocketOS::nsSocketOS() : mOffset(0) @@ -2390,6 +2415,20 @@ nsSocketOS::SetObserver(nsIOutputStreamObserver *aObserver) return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsSocketOS::Seek(PRInt32 whence, PRInt32 offset) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsSocketOS::Tell(PRUint32 *offset) +{ + NS_ENSURE_ARG_POINTER(offset); + *offset = mOffset; + return NS_OK; +} + // //---------------------------------------------------------------------------- // nsSocketRequest @@ -2484,8 +2523,7 @@ nsSocketRequest::Cancel(nsresult status) mCanceled = PR_TRUE; // if status is a fail, we need to force a dispatch - if (NS_FAILED(status)) - mTransport->Dispatch(this); + mTransport->Dispatch(this); return NS_OK; } @@ -2631,6 +2669,8 @@ nsSocketReadRequest::OnRead() offset, amount); + LOG(("nsSocketReadRequest: listener returned [rv=%x]\n", rv)); + // // Handle the error conditions // diff --git a/mozilla/netwerk/base/src/nsSocketTransport.h b/mozilla/netwerk/base/src/nsSocketTransport.h index bbeadfed0e0..4899c62714b 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport.h +++ b/mozilla/netwerk/base/src/nsSocketTransport.h @@ -39,6 +39,7 @@ #include "nsIPipe.h" #include "nsIProgressEventSink.h" #include "nsIInterfaceRequestor.h" +#include "nsIFileStreams.h" #define NS_SOCKET_TRANSPORT_SEGMENT_SIZE (2*1024) #define NS_SOCKET_TRANSPORT_BUFFER_SIZE (8*1024) @@ -223,7 +224,7 @@ protected: nsSocketState mCurrentState; nsCOMPtr mDNSRequest; - nsCOMPtr mEventSink; + nsCOMPtr mProgressSink; nsCOMPtr mNotificationCallbacks; char* mHostName; PRInt32 mPort; @@ -325,10 +326,12 @@ public: * input stream, passed to nsIStreamListener::OnDataAvailable() */ class nsSocketIS : public nsIInputStream + , public nsISeekableStream { public: NS_DECL_ISUPPORTS NS_DECL_NSIINPUTSTREAM + NS_DECL_NSISEEKABLESTREAM nsSocketIS(); virtual ~nsSocketIS() { } @@ -350,10 +353,12 @@ private: * output stream, passed to nsIStreamProvider::OnDataWritable() */ class nsSocketOS : public nsIOutputStream + , public nsISeekableStream { public: NS_DECL_ISUPPORTS NS_DECL_NSIOUTPUTSTREAM + NS_DECL_NSISEEKABLESTREAM nsSocketOS(); virtual ~nsSocketOS() { } diff --git a/mozilla/netwerk/base/src/nsStorageTransport.cpp b/mozilla/netwerk/base/src/nsStorageTransport.cpp index 65c49aeee1a..bd39cfce1c6 100644 --- a/mozilla/netwerk/base/src/nsStorageTransport.cpp +++ b/mozilla/netwerk/base/src/nsStorageTransport.cpp @@ -290,7 +290,8 @@ nsStorageTransport::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) } NS_IMETHODIMP -nsStorageTransport::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks, PRBool isBackground) +nsStorageTransport::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks, + PRUint32 flags) { return NS_ERROR_NOT_IMPLEMENTED; } @@ -404,7 +405,7 @@ nsStorageTransport::nsReadRequest::~nsReadRequest() nsresult nsStorageTransport::nsReadRequest::SetListener(nsIStreamListener *aListener, - nsISupports *aListenerContext) + nsISupports *aListenerContext) { nsresult rv = NS_OK; @@ -438,18 +439,24 @@ nsStorageTransport::nsReadRequest::Process() // always clear this flag initially mWaitingForWrite = PR_FALSE; - PRUint32 count = 0; - - rv = mTransport->Available(mTransferOffset, &count); - if (NS_FAILED(rv)) return rv; - if (!mOnStartFired) { // no need to proxy this callback (void) mListener->OnStartRequest(this, mListenerContext); mOnStartFired = PR_TRUE; } - count = PR_MIN(count, mTransferCount); + PRUint32 count = 0; + + if (mCanceled) { + // forcing the transfer count to zero indicates that we are done. + mTransferCount = 0; + } + else { + rv = mTransport->Available(mTransferOffset, &count); + if (NS_FAILED(rv)) return rv; + + count = PR_MIN(count, mTransferCount); + } if (count) { count = PR_MIN(count, MAX_IO_CHUNK); @@ -564,7 +571,7 @@ nsStorageTransport::nsReadRequest::SetLoadFlags(nsLoadFlags loadFlags) NS_IMETHODIMP nsStorageTransport::nsReadRequest::OnStartRequest(nsIRequest *aRequest, - nsISupports *aContext) + nsISupports *aContext) { NS_NOTREACHED("nsStorageTransport::nsReadRequest::OnStartRequest"); return NS_ERROR_FAILURE; @@ -581,19 +588,27 @@ nsStorageTransport::nsReadRequest::OnStopRequest(nsIRequest *aRequest, NS_IMETHODIMP nsStorageTransport::nsReadRequest::OnDataAvailable(nsIRequest *aRequest, - nsISupports *aContext, - nsIInputStream *aInput, - PRUint32 aOffset, - PRUint32 aCount) + nsISupports *aContext, + nsIInputStream *aInput, + PRUint32 aOffset, + PRUint32 aCount) { nsresult rv = NS_OK; + PRUint32 priorOffset = mTransferOffset; rv = mListener->OnDataAvailable(aRequest, aContext, aInput, aOffset, aCount); - NS_ASSERTION(rv != NS_BASE_STREAM_WOULD_BLOCK, "not implemented"); if (NS_FAILED(rv)) return rv; + // avoid an infinite loop + if (priorOffset == mTransferOffset) { + NS_WARNING("nsIStreamListener::OnDataAvailable implementation did not " + "consume any data!"); + // end the read + (void) Cancel(NS_ERROR_UNEXPECTED); + } + // post the next message... return Process(); } @@ -612,16 +627,18 @@ nsStorageTransport::nsReadRequest::Available(PRUint32 *aCount) } NS_IMETHODIMP -nsStorageTransport::nsReadRequest::Read(char *aBuf, PRUint32 aCount, PRUint32 *aBytesRead) +nsStorageTransport::nsReadRequest::Read(char *aBuf, + PRUint32 aCount, + PRUint32 *aBytesRead) { return ReadSegments(nsWriteToBuffer, aBuf, aCount, aBytesRead); } NS_IMETHODIMP nsStorageTransport::nsReadRequest::ReadSegments(nsWriteSegmentFun aWriter, - void *aClosure, - PRUint32 aCount, - PRUint32 *aBytesRead) + void *aClosure, + PRUint32 aCount, + PRUint32 *aBytesRead) { NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); @@ -799,25 +816,25 @@ nsStorageTransport::nsOutputStream::Flush() NS_IMETHODIMP nsStorageTransport::nsOutputStream::Write(const char *aBuf, - PRUint32 aCount, - PRUint32 *aBytesWritten) + PRUint32 aCount, + PRUint32 *aBytesWritten) { return WriteSegments(nsReadFromBuffer, (void *) aBuf, aCount, aBytesWritten); } NS_IMETHODIMP nsStorageTransport::nsOutputStream::WriteFrom(nsIInputStream *aInput, - PRUint32 aCount, - PRUint32 *aBytesWritten) + PRUint32 aCount, + PRUint32 *aBytesWritten) { return WriteSegments(nsReadFromInputStream, aInput, aCount, aBytesWritten); } NS_IMETHODIMP nsStorageTransport::nsOutputStream::WriteSegments(nsReadSegmentFun aReader, - void *aClosure, - PRUint32 aCount, - PRUint32 *aBytesWritten) + void *aClosure, + PRUint32 aCount, + PRUint32 *aBytesWritten) { NS_ENSURE_TRUE(mTransport, NS_BASE_STREAM_CLOSED); diff --git a/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp b/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp index 7791e2eaf5a..4acde6ec9e6 100644 --- a/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp +++ b/mozilla/netwerk/base/src/nsStreamListenerProxy.cpp @@ -34,8 +34,8 @@ static PRLogModuleInfo *gStreamListenerProxyLog; #define LOG(args) PR_LOG(gStreamListenerProxyLog, PR_LOG_DEBUG, args) -#define DEFAULT_BUFFER_SEGMENT_SIZE 2048 -#define DEFAULT_BUFFER_MAX_SIZE (4*2048) +#define DEFAULT_BUFFER_SEGMENT_SIZE 4096 +#define DEFAULT_BUFFER_MAX_SIZE (4*4096) //---------------------------------------------------------------------------- // Design Overview diff --git a/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp b/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp index 14a3076a7b4..47c6f502c06 100644 --- a/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp +++ b/mozilla/netwerk/base/src/nsStreamProviderProxy.cpp @@ -32,8 +32,8 @@ static PRLogModuleInfo *gStreamProviderProxyLog; #define LOG(args) PR_LOG(gStreamProviderProxyLog, PR_LOG_DEBUG, args) -#define DEFAULT_BUFFER_SEGMENT_SIZE 2048 -#define DEFAULT_BUFFER_MAX_SIZE (4*2048) +#define DEFAULT_BUFFER_SEGMENT_SIZE 4096 +#define DEFAULT_BUFFER_MAX_SIZE (4*4096) //---------------------------------------------------------------------------- // Design Overview diff --git a/mozilla/netwerk/build/nsNetCID.h b/mozilla/netwerk/build/nsNetCID.h index bc25dba3ce3..06802a2a1ac 100644 --- a/mozilla/netwerk/build/nsNetCID.h +++ b/mozilla/netwerk/build/nsNetCID.h @@ -214,4 +214,27 @@ {0x8f, 0xbc, 0xbe, 0xe8, 0xf9, 0x22, 0xea, 0x67} \ } -#endif /* !defined(nsNetCID_h__) */ + +/****************************************************************************** + * netwerk/protocol/http/ classes + */ + +// This protocol handler implements both HTTP and HTTPS +#define NS_HTTPPROTOCOLHANDLER_CID \ +{ /* 4f47e42e-4d23-4dd3-bfda-eb29255e9ea3 */ \ + 0x4f47e42e, \ + 0x4d23, \ + 0x4dd3, \ + {0xbf, 0xda, 0xeb, 0x29, 0x25, 0x5e, 0x9e, 0xa3} \ +} + +#define NS_HTTPBASICAUTH_CID \ +{ /* fca3766a-434a-4ae7-83cf-0909e18a093a */ \ + 0xfca3766a, \ + 0x434a, \ + 0x4ae7, \ + {0x83, 0xcf, 0x09, 0x09, 0xe1, 0x8a, 0x09, 0x3a} \ +} + + +#endif // nsNetCID_h__ diff --git a/mozilla/netwerk/build/nsNetModule.cpp b/mozilla/netwerk/build/nsNetModule.cpp index 36c25010240..09d7020711d 100644 --- a/mozilla/netwerk/build/nsNetModule.cpp +++ b/mozilla/netwerk/build/nsNetModule.cpp @@ -107,6 +107,15 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsStorageTransport) /////////////////////////////////////////////////////////////////////////////// +#include "nsHttpHandler.h" +//NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHttpHandler, Init) + +#include "nsHttpBasicAuth.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth) + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 #include "nsIHTTPProtocolHandler.h" #include "nsHTTPHandler.h" #include "nsHTTPSHandler.h" @@ -156,6 +165,8 @@ UnregisterBasicAuth(nsIComponentManager *aCompMgr, nsIFile *aPath, getter_Copies(basicAuth)); return NS_OK; } +#endif + /////////////////////////////////////////////////////////////////////////////// #include "nsFileChannel.h" @@ -785,6 +796,7 @@ static nsModuleComponentInfo gNetModuleInfo[] = { nsFileChannel::Create }, +#if 0 // from netwerk/protocol/http: { "HTTP Handler", NS_IHTTPHANDLER_CID, @@ -801,6 +813,22 @@ static nsModuleComponentInfo gNetModuleInfo[] = { RegisterBasicAuth, UnregisterBasicAuth }, +#endif + + { "HTTP Handler", + NS_HTTPPROTOCOLHANDLER_CID, + NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", + nsHttpHandler::Create }, + + { "HTTPS Handler", + NS_HTTPPROTOCOLHANDLER_CID, + NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "https", + nsHttpHandler::Create }, + + { "HTTP Basic Auth Encoder", + NS_HTTPBASICAUTH_CID, + NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX "basic", + nsHttpBasicAuthConstructor }, // from netwerk/protocol/data: { "Data Protocol Handler", diff --git a/mozilla/netwerk/cache/src/nsCacheEntry.cpp b/mozilla/netwerk/cache/src/nsCacheEntry.cpp index 474828a7d73..728074c3700 100644 --- a/mozilla/netwerk/cache/src/nsCacheEntry.cpp +++ b/mozilla/netwerk/cache/src/nsCacheEntry.cpp @@ -303,7 +303,7 @@ nsCacheEntry::CreateDescriptor(nsCacheRequest * request, nsICacheEntryDescriptor ** result) { NS_ENSURE_ARG_POINTER(request && result); - + nsCacheEntryDescriptor * descriptor = new nsCacheEntryDescriptor(this, accessGranted); diff --git a/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp b/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp index 729387753bf..2c2c04229ce 100644 --- a/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp +++ b/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp @@ -479,7 +479,7 @@ nsTransportWrapper::GetNotificationCallbacks(nsIInterfaceRequestor **result) NS_IMETHODIMP nsCacheEntryDescriptor:: nsTransportWrapper::SetNotificationCallbacks(nsIInterfaceRequestor *requestor, - PRBool isBackground) + PRUint32 flags) { // if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; diff --git a/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl b/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl index 8f514300c10..9dd245a8edd 100644 --- a/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl +++ b/mozilla/netwerk/protocol/ftp/public/nsIFTPChannel.idl @@ -28,8 +28,6 @@ [scriptable, uuid(3476df52-1dd2-11b2-b928-925d89b33bc0)] interface nsIFTPChannel : nsIChannel { - readonly attribute PRBool UsingProxy; - readonly attribute PRBool UsingTransparentProxy; }; [scriptable, uuid(455d4234-0330-43d2-bbfb-99afbecbfeb0)] diff --git a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp index 5c3d890dc35..874d6eb295a 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.cpp @@ -51,9 +51,7 @@ nsFTPChannel::nsFTPChannel() mFTPState(nsnull), mLock(nsnull), mStatus(NS_OK), - mCanceled(PR_FALSE), - mProxyPort(-1), - mProxyTransparent(PR_FALSE) + mCanceled(PR_FALSE) { NS_INIT_REFCNT(); } @@ -69,10 +67,9 @@ nsFTPChannel::~nsFTPChannel() if (mLock) PR_DestroyLock(mLock); } -NS_IMPL_THREADSAFE_ISUPPORTS8(nsFTPChannel, +NS_IMPL_THREADSAFE_ISUPPORTS7(nsFTPChannel, nsIChannel, nsIFTPChannel, - nsIProxy, nsIRequest, nsIInterfaceRequestor, nsIProgressEventSink, @@ -97,12 +94,6 @@ nsFTPChannel::Init(nsIURI* uri) return NS_OK; } -nsresult -nsFTPChannel::SetProxyChannel(nsIChannel *aChannel) -{ - mProxyChannel = aChannel; - return NS_OK; -} NS_METHOD nsFTPChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) @@ -128,8 +119,6 @@ nsFTPChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) NS_IMETHODIMP nsFTPChannel::GetName(PRUnichar* *result) { - if (mProxyChannel) - return mProxyChannel->GetName(result); nsresult rv; nsXPIDLCString urlStr; rv = mURL->GetSpec(getter_Copies(urlStr)); @@ -143,8 +132,6 @@ nsFTPChannel::GetName(PRUnichar* *result) NS_IMETHODIMP nsFTPChannel::IsPending(PRBool *result) { nsAutoLock lock(mLock); - if (mProxyChannel) - return mProxyChannel->IsPending(result); NS_NOTREACHED("nsFTPChannel::IsPending"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -173,11 +160,10 @@ nsFTPChannel::Cancel(nsresult status) { mCanceled = PR_TRUE; mStatus = status; - if (mProxyChannel) { - return mProxyChannel->Cancel(status); - } else if (mFTPState) { + + if (mFTPState) return mFTPState->Cancel(status); - } + return NS_OK; } @@ -188,9 +174,7 @@ nsFTPChannel::Suspend(void) { ("nsFTPChannel::Suspend() called [this=%x]\n", this)); nsAutoLock lock(mLock); - if (mProxyChannel) { - return mProxyChannel->Suspend(); - } else if (mFTPState) { + if (mFTPState) { return mFTPState->Suspend(); } return NS_OK; @@ -203,9 +187,7 @@ nsFTPChannel::Resume(void) { ("nsFTPChannel::Resume() called [this=%x]\n", this)); nsAutoLock lock(mLock); - if (mProxyChannel) { - return mProxyChannel->Resume(); - } else if (mFTPState) { + if (mFTPState) { return mFTPState->Resume(); } return NS_OK; @@ -240,8 +222,6 @@ nsFTPChannel::GetURI(nsIURI* *aURL) NS_IMETHODIMP nsFTPChannel::Open(nsIInputStream **result) { - if (mProxyChannel) - return mProxyChannel->Open(result); NS_NOTREACHED("nsFTPChannel::Open"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -262,10 +242,6 @@ nsFTPChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) if (NS_FAILED(rv)) return rv; } - if (mProxyChannel) { - return mProxyChannel->AsyncOpen(this, ctxt); - } - //////////////////////////////// //// setup the channel thread if (!mFTPState) { @@ -284,8 +260,6 @@ nsFTPChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) NS_IMETHODIMP nsFTPChannel::GetLoadFlags(PRUint32 *aLoadFlags) { - if (mProxyChannel) - return mProxyChannel->GetLoadFlags(aLoadFlags); *aLoadFlags = mLoadFlags; return NS_OK; } @@ -293,8 +267,6 @@ nsFTPChannel::GetLoadFlags(PRUint32 *aLoadFlags) NS_IMETHODIMP nsFTPChannel::SetLoadFlags(PRUint32 aLoadFlags) { - if (mProxyChannel) - return mProxyChannel->SetLoadFlags(aLoadFlags); mLoadFlags = aLoadFlags; return NS_OK; } @@ -528,68 +500,3 @@ nsFTPChannel::OnDataAvailable(nsIRequest *request, nsISupports* aContext, PRUint32 aLength) { return mListener->OnDataAvailable(this, mUserContext, aInputStream, aSourceOffset, aLength); } - -// nsIFTPChannel methods -NS_IMETHODIMP -nsFTPChannel::GetUsingProxy(PRBool *aUsingProxy) -{ - if (!aUsingProxy) - return NS_ERROR_NULL_POINTER; - *aUsingProxy = (!mProxyHost.IsEmpty() && !mProxyTransparent); - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::GetUsingTransparentProxy(PRBool *aUsingProxy) -{ - if (!aUsingProxy) - return NS_ERROR_NULL_POINTER; - *aUsingProxy = (!mProxyHost.IsEmpty() && mProxyTransparent); - return NS_OK; -} - -// nsIProxy methods -NS_IMETHODIMP -nsFTPChannel::GetProxyHost(char* *_retval) { - *_retval = mProxyHost.ToNewCString(); - if (!*_retval) return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::SetProxyHost(const char *aProxyHost) { - mProxyHost = aProxyHost; - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::GetProxyPort(PRInt32 *_retval) { - *_retval = mProxyPort; - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::SetProxyPort(PRInt32 aProxyPort) { - mProxyPort = aProxyPort; - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::GetProxyType(char * *_retval) -{ - *_retval = mProxyType.ToNewCString(); - if (!*_retval) return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - -NS_IMETHODIMP -nsFTPChannel::SetProxyType(const char * aProxyType) -{ - if (nsCRT::strcasecmp(aProxyType, "socks") == 0) { - mProxyTransparent = PR_TRUE; - } else { - mProxyTransparent = PR_FALSE; - } - mProxyType = aProxyType; - return NS_OK; -} diff --git a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h index ad52d0e23ab..08c91cebc53 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h +++ b/mozilla/netwerk/protocol/ftp/src/nsFTPChannel.h @@ -41,7 +41,6 @@ #include "nsIPrompt.h" #include "nsIAuthPrompt.h" #include "nsIFTPChannel.h" -#include "nsIProxy.h" #define FTP_COMMAND_CHANNEL_SEG_SIZE 64 #define FTP_COMMAND_CHANNEL_MAX_SIZE 512 @@ -52,7 +51,6 @@ #define FTP_CACHE_CONTROL_CONNECTION 1 class nsFTPChannel : public nsIFTPChannel, - public nsIProxy, public nsIInterfaceRequestor, public nsIProgressEventSink, public nsIStreamListener { @@ -61,7 +59,6 @@ public: NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL NS_DECL_NSIFTPCHANNEL - NS_DECL_NSIPROXY NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIPROGRESSEVENTSINK NS_DECL_NSISTREAMLISTENER @@ -78,7 +75,6 @@ public: // initializes the channel. nsresult Init(nsIURI* uri); - nsresult SetProxyChannel(nsIChannel *aChannel); protected: nsCOMPtr mOriginalURI; @@ -111,12 +107,6 @@ protected: nsCOMPtr mUserContext; nsresult mStatus; PRPackedBool mCanceled; - - nsCOMPtr mProxyChannel; // a proxy channel - nsCAutoString mProxyHost; - PRInt32 mProxyPort; - nsCAutoString mProxyType; - PRBool mProxyTransparent; }; #endif /* nsFTPChannel_h___ */ diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp index eeeda5fc38a..cfc76943002 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp @@ -41,7 +41,6 @@ #include "nsNetUtil.h" #include "nsIDNSService.h" // for host error code #include "nsIWalletService.h" -#include "nsIProxy.h" #include "nsIMemory.h" #include "nsIStringStream.h" #include "nsIPref.h" @@ -1872,44 +1871,7 @@ nsFtpState::CreateTransport(const char * host, PRInt32 port, PRUint32 bufferSegm sts, kSocketTransportServiceCID, &rv); - - if (NS_FAILED(rv)) return rv; - - PRBool usingProxy; - if (NS_SUCCEEDED(mChannel->GetUsingTransparentProxy(&usingProxy)) && usingProxy) { - nsCOMPtr channelProxy = do_QueryInterface(mChannel, &rv); - - if (NS_SUCCEEDED(rv)) { - - nsXPIDLCString proxyHost; - nsXPIDLCString proxyType; - PRInt32 proxyPort; - - rv = channelProxy->GetProxyHost(getter_Copies(proxyHost)); - if (NS_FAILED(rv)) return rv; - - rv = channelProxy->GetProxyPort(&proxyPort); - if (NS_FAILED(rv)) return rv; - - rv = channelProxy->GetProxyType(getter_Copies(proxyType)); - if (NS_SUCCEEDED(rv) && nsCRT::strcasecmp(proxyType, "socks") == 0) { - - return sts->CreateTransportOfType("socks", host, port, proxyHost, proxyPort, - bufferSegmentSize, - bufferMaxSize, - o_pTrans); - - } - - return sts->CreateTransport(host, port, proxyHost, proxyPort, - bufferSegmentSize, - bufferMaxSize, - o_pTrans); - - } - } - return sts->CreateTransport(host, port, nsnull, PRUint32(-1), bufferSegmentSize, bufferMaxSize, diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp index a23d38c582a..a8f6bfd4b77 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpControlConnection.cpp @@ -179,8 +179,8 @@ nsFtpControlConnection::Connect() rv = mCPipe->AsyncWrite(provider, NS_STATIC_CAST(nsISupports*, (nsIStreamListener*)this), 0, PRUint32(-1), - nsITransport::DONT_PROXY_STREAM_PROVIDER | - nsITransport::DONT_PROXY_STREAM_OBSERVER, + nsITransport::DONT_PROXY_PROVIDER | + nsITransport::DONT_PROXY_OBSERVER, getter_AddRefs(mWriteRequest)); if (NS_FAILED(rv)) return rv; diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp index 143f81ae586..40a73a29ca3 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp @@ -45,8 +45,7 @@ #include "nsNetUtil.h" // For proxification of FTP URLs -#include "nsIHTTPProtocolHandler.h" -#include "nsIHTTPChannel.h" +#include "nsIHttpProtocolHandler.h" #include "nsIErrorService.h" #if defined(PR_LOGGING) @@ -67,8 +66,7 @@ PRLogModuleInfo* gFTPLog = nsnull; static NS_DEFINE_IID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); -static NS_DEFINE_CID(kProtocolProxyServiceCID, NS_PROTOCOLPROXYSERVICE_CID); -static NS_DEFINE_CID(kHTTPHandlerCID, NS_IHTTPHANDLER_CID); +static NS_DEFINE_CID(kHttpHandlerCID, NS_HTTPPROTOCOLHANDLER_CID); static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID); nsSupportsHashtable* nsFtpProtocolHandler::mRootConnectionList = nsnull; @@ -96,9 +94,6 @@ nsFtpProtocolHandler::Init() { if (!gFTPLog) gFTPLog = PR_NewLogModule("nsFTPProtocol"); #endif /* PR_LOGGING */ - mProxySvc = do_GetService(kProtocolProxyServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - mIOSvc = do_GetService(kIOServiceCID, &rv); if (NS_FAILED(rv)) return rv; @@ -159,8 +154,6 @@ nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) { nsresult rv = NS_OK; - PRBool useProxy = PR_FALSE; - nsFTPChannel* channel = nsnull; rv = nsFTPChannel::Create(nsnull, NS_GET_IID(nsIChannel), (void**)&channel); if (NS_FAILED(rv)) return rv; @@ -170,65 +163,6 @@ nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFtpProtocolHandler::NewChannel() FAILED\n")); return rv; } - - if (NS_SUCCEEDED(mProxySvc->GetProxyEnabled(&useProxy)) && useProxy) - { - rv = mProxySvc->ExamineForProxy(url, channel); - if (NS_FAILED(rv)) return rv; - } - - useProxy = PR_FALSE; - if (NS_SUCCEEDED(channel->GetUsingProxy(&useProxy)) && useProxy) { - - nsCOMPtr proxyChannel; - // if an FTP proxy is enabled, push things off to HTTP. - - nsCOMPtr httpHandler = do_GetService(kHTTPHandlerCID, &rv); - if (NS_FAILED(rv)) return rv; - - // rjc says: the dummy URI (for the HTTP layer) needs to be a syntactically valid URI - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), "http://test.com/"); - if (NS_FAILED(rv)) return rv; - - rv = httpHandler->NewChannel(uri, getter_AddRefs(proxyChannel)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr httpChannel = do_QueryInterface(proxyChannel, &rv); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString spec; - rv = url->GetSpec(getter_Copies(spec)); - if (NS_FAILED(rv)) return rv; - - rv = httpChannel->SetProxyRequestURI((const char*)spec); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr proxyHTTP = do_QueryInterface(httpChannel, &rv); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString proxyHost; - rv = channel->GetProxyHost(getter_Copies(proxyHost)); - if (NS_FAILED(rv)) return rv; - - rv = proxyHTTP->SetProxyHost(proxyHost); - if (NS_FAILED(rv)) return rv; - - PRInt32 proxyPort; - rv = channel->GetProxyPort(&proxyPort); - if (NS_FAILED(rv)) return rv; - - rv = proxyHTTP->SetProxyPort(proxyPort); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString proxyType; - rv = channel->GetProxyType(getter_Copies(proxyType)); - - if (NS_SUCCEEDED(rv)) - proxyHTTP->SetProxyType(proxyType); - - rv = channel->SetProxyChannel(proxyChannel); - } *result = channel; return rv; diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.h b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.h index 933591a54a1..4b31f1df411 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.h +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpProtocolHandler.h @@ -29,7 +29,6 @@ #include "nsIIOService.h" #include "nsIThreadPool.h" #include "nsIObserverService.h" -#include "nsIProtocolProxyService.h" #include "nsAutoLock.h" // {25029490-F132-11d2-9588-00805F369F95} @@ -55,7 +54,6 @@ protected: static nsSupportsHashtable* mRootConnectionList; nsCOMPtr mIOSvc; - nsCOMPtr mProxySvc; }; #endif /* nsFtpProtocolHandler_h___ */ diff --git a/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.cpp b/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.cpp index 10e377b8684..a4a5554c68b 100644 --- a/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.cpp +++ b/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.cpp @@ -53,9 +53,7 @@ nsGopherChannel::nsGopherChannel() : mContentLength(-1), mActAsObserver(PR_TRUE), mType(-1), - mStatus(NS_OK), - mProxyPort(-1), - mProxyTransparent(PR_FALSE) + mStatus(NS_OK) { NS_INIT_REFCNT(); } @@ -69,12 +67,11 @@ nsGopherChannel::~nsGopherChannel() #endif } -NS_IMPL_THREADSAFE_ISUPPORTS5(nsGopherChannel, +NS_IMPL_THREADSAFE_ISUPPORTS4(nsGopherChannel, nsIChannel, nsIRequest, nsIStreamListener, - nsIRequestObserver, - nsIProxy); + nsIRequestObserver); nsresult nsGopherChannel::Init(nsIURI* uri) @@ -127,12 +124,6 @@ nsGopherChannel::Init(nsIURI* uri) return NS_OK; } -nsresult -nsGopherChannel::SetProxyChannel(nsIChannel *aChannel) -{ - mProxyChannel = aChannel; - return NS_OK; -} NS_METHOD nsGopherChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) @@ -146,73 +137,12 @@ nsGopherChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) return rv; } -// nsIProxy methods: -NS_IMETHODIMP -nsGopherChannel::GetProxyHost(char **aProxyHost) -{ - *aProxyHost = mProxyHost.ToNewCString(); - if (!*aProxyHost) return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - -NS_IMETHODIMP -nsGopherChannel::SetProxyHost(const char * aProxyHost) -{ - mProxyHost = aProxyHost; - return NS_OK; -} - -NS_IMETHODIMP -nsGopherChannel::GetProxyPort(PRInt32 *aProxyPort) -{ - *aProxyPort = mProxyPort; - return NS_OK; -} - -NS_IMETHODIMP -nsGopherChannel::SetProxyPort(PRInt32 aProxyPort) -{ - mProxyPort = aProxyPort; - return NS_OK; -} - -NS_IMETHODIMP -nsGopherChannel::GetProxyType(char **aProxyType) -{ - *aProxyType = mProxyType.ToNewCString(); - if (!*aProxyType) return NS_ERROR_OUT_OF_MEMORY; - return NS_OK; -} - -NS_IMETHODIMP -nsGopherChannel::SetProxyType(const char * aProxyType) -{ - if (!nsCRT::strcasecmp(aProxyType, "socks")) { - mProxyTransparent = PR_TRUE; - } else { - mProxyTransparent = PR_FALSE; - } - mProxyType = aProxyType; - return NS_OK; -} - -nsresult -nsGopherChannel::GetUsingProxy(PRBool *aUsingProxy) -{ - if (!aUsingProxy) - return NS_ERROR_NULL_POINTER; - *aUsingProxy = (!mProxyHost.IsEmpty() && !mProxyTransparent); - return NS_OK; -} - //////////////////////////////////////////////////////////////////////////////// // nsIRequest methods: NS_IMETHODIMP nsGopherChannel::GetName(PRUnichar* *result) { - if (mProxyChannel) - return mProxyChannel->GetName(result); nsString name; name.AppendWithConversion(mHost); name.AppendWithConversion(":"); @@ -224,8 +154,6 @@ nsGopherChannel::GetName(PRUnichar* *result) NS_IMETHODIMP nsGopherChannel::IsPending(PRBool *result) { - if (mProxyChannel) - return mProxyChannel->IsPending(result); NS_NOTREACHED("nsGopherChannel::IsPending"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -248,9 +176,7 @@ nsGopherChannel::Cancel(nsresult status) NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code"); mStatus = status; - if (mProxyChannel) - return mProxyChannel->Cancel(status); - else if (mTransportRequest) { + if (mTransportRequest) { return mTransportRequest->Cancel(status); } @@ -260,8 +186,6 @@ nsGopherChannel::Cancel(nsresult status) NS_IMETHODIMP nsGopherChannel::Suspend(void) { - if (mProxyChannel) - return mProxyChannel->Suspend(); NS_NOTREACHED("nsGopherChannel::Suspend"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -269,8 +193,6 @@ nsGopherChannel::Suspend(void) NS_IMETHODIMP nsGopherChannel::Resume(void) { - if (mProxyChannel) - return mProxyChannel->Resume(); NS_NOTREACHED("nsGopherChannel::Resume"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -306,9 +228,6 @@ nsGopherChannel::Open(nsIInputStream **_retval) { nsresult rv = NS_OK; - if (mProxyChannel) - return mProxyChannel->Open(_retval); - NS_WITH_SERVICE(nsISocketTransportService, socketService, kSocketTransportServiceCID, @@ -342,9 +261,6 @@ nsGopherChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt) nsresult rv; - if (mProxyChannel) - return mProxyChannel->AsyncOpen(this, ctxt); - NS_WITH_SERVICE(nsISocketTransportService, socketService, kSocketTransportServiceCID, @@ -369,8 +285,6 @@ nsGopherChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt) NS_IMETHODIMP nsGopherChannel::GetLoadFlags(PRUint32 *aLoadFlags) { - if (mProxyChannel) - mProxyChannel->GetLoadFlags(aLoadFlags); *aLoadFlags = mLoadFlags; return NS_OK; } @@ -378,8 +292,6 @@ nsGopherChannel::GetLoadFlags(PRUint32 *aLoadFlags) NS_IMETHODIMP nsGopherChannel::SetLoadFlags(PRUint32 aLoadFlags) { - if (mProxyChannel) - mProxyChannel->SetLoadFlags(aLoadFlags); mLoadFlags = aLoadFlags; return NS_OK; } @@ -387,9 +299,6 @@ nsGopherChannel::SetLoadFlags(PRUint32 aLoadFlags) NS_IMETHODIMP nsGopherChannel::GetContentType(char* *aContentType) { - if (mProxyChannel) - return mProxyChannel->GetContentType(aContentType); - if (!aContentType) return NS_ERROR_NULL_POINTER; switch(mType) { @@ -536,7 +445,7 @@ nsGopherChannel::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) ("nsGopherChannel::OnStartRequest called [this=%x, aRequest=%x]\n", this, aRequest)); - if (!mActAsObserver || mProxyChannel) { + if (!mActAsObserver) { // acting as a listener return mListener->OnStartRequest(this, aContext); } else { @@ -557,7 +466,7 @@ nsGopherChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult rv = NS_OK; - if (NS_FAILED(aStatus) || !mActAsObserver || mProxyChannel) { + if (NS_FAILED(aStatus) || !mActAsObserver) { if (mListener) { rv = mListener->OnStopRequest(this, mResponseContext, aStatus); if (NS_FAILED(rv)) return rv; diff --git a/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.h b/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.h index 0a0d1afab28..f65f8ecc80a 100644 --- a/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.h +++ b/mozilla/netwerk/protocol/gopher/src/nsGopherChannel.h @@ -39,13 +39,11 @@ #include "nsITransport.h" class nsGopherChannel : public nsIChannel, - public nsIProxy, public nsIStreamListener { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUEST NS_DECL_NSICHANNEL - NS_DECL_NSIPROXY NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER @@ -59,8 +57,6 @@ public: nsresult Init(nsIURI* uri); - nsresult SetProxyChannel(nsIChannel* aChannel); - nsresult GetUsingProxy(PRBool *aUsingProxy); protected: nsCOMPtr mOriginalURI; nsCOMPtr mCallbacks; @@ -87,13 +83,8 @@ protected: nsCOMPtr mTransportRequest; nsresult mStatus; - nsCOMPtr mProxyChannel; // a proxy channel - nsCString mProxyHost; - PRInt32 mProxyPort; - nsCString mProxyType; - PRBool mProxyTransparent; - nsresult SendRequest(nsITransport* aTransport); + }; #endif /* nsGopherChannel_h___ */ diff --git a/mozilla/netwerk/protocol/gopher/src/nsGopherHandler.cpp b/mozilla/netwerk/protocol/gopher/src/nsGopherHandler.cpp index a66f924b3ee..d777a68c0ab 100644 --- a/mozilla/netwerk/protocol/gopher/src/nsGopherHandler.cpp +++ b/mozilla/netwerk/protocol/gopher/src/nsGopherHandler.cpp @@ -1,3 +1,4 @@ + /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Mozilla Public @@ -32,8 +33,8 @@ #include "nsIServiceManager.h" #include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" -#include "nsIHTTPProtocolHandler.h" -#include "nsIHTTPChannel.h" +//#include "nsIHTTPProtocolHandler.h" +//#include "nsIHTTPChannel.h" #include "nsIErrorService.h" #include "nsNetUtil.h" #include "prlog.h" @@ -43,23 +44,16 @@ PRLogModuleInfo* gGopherLog = nsnull; #endif static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); -static NS_DEFINE_CID(kProtocolProxyServiceCID, NS_PROTOCOLPROXYSERVICE_CID); -static NS_DEFINE_CID(kHTTPHandlerCID, NS_IHTTPHANDLER_CID); +//static NS_DEFINE_CID(kHTTPHandlerCID, NS_IHTTPHANDLER_CID); //////////////////////////////////////////////////////////////////////////////// nsGopherHandler::nsGopherHandler() { NS_INIT_REFCNT(); - nsresult rv; - #ifdef PR_LOGGING if (!gGopherLog) gGopherLog = PR_NewLogModule("nsGopherProtocol"); #endif - - mProxySvc = do_GetService(kProtocolProxyServiceCID, &rv); - if (!mProxySvc) - NS_WARNING("Failed to get proxy service!\n"); } nsGopherHandler::~nsGopherHandler() { @@ -129,72 +123,6 @@ nsGopherHandler::NewChannel(nsIURI* url, nsIChannel* *result) return rv; } - // Proxy code is largly taken from the ftp and http protocol handlers - - PRBool useProxy = PR_FALSE; - - if (mProxySvc && - NS_SUCCEEDED(mProxySvc->GetProxyEnabled(&useProxy)) && useProxy) { - - rv = mProxySvc->ExamineForProxy(url, channel); - if (NS_FAILED(rv)) return rv; - } - - useProxy = PR_FALSE; - if (NS_SUCCEEDED(channel->GetUsingProxy(&useProxy)) && useProxy) { - nsCOMPtr proxyChannel; - // if an gopher proxy is enabled, push things off to HTTP. - - nsCOMPtr httpHandler = - do_GetService(kHTTPHandlerCID, &rv); - if (NS_FAILED(rv)) return rv; - - // rjc says: the dummy URI (for the HTTP layer) needs to be a - // syntactically valid URI - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), "http://example.com"); - if (NS_FAILED(rv)) return rv; - - rv = httpHandler->NewChannel(uri, getter_AddRefs(proxyChannel)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr httpChannel = do_QueryInterface(proxyChannel, &rv); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString spec; - rv = url->GetSpec(getter_Copies(spec)); - if (NS_FAILED(rv)) return rv; - - rv = httpChannel->SetProxyRequestURI(spec.get()); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr proxyHTTP = do_QueryInterface(httpChannel, &rv); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString proxyHost; - rv = channel->GetProxyHost(getter_Copies(proxyHost)); - if (NS_FAILED(rv)) return rv; - - rv = proxyHTTP->SetProxyHost(proxyHost); - if (NS_FAILED(rv)) return rv; - - PRInt32 proxyPort; - rv = channel->GetProxyPort(&proxyPort); - if (NS_FAILED(rv)) return rv; - - rv = proxyHTTP->SetProxyPort(proxyPort); - if (NS_FAILED(rv)) return rv; - - nsXPIDLCString proxyType; - rv = channel->GetProxyType(getter_Copies(proxyType)); - - if (NS_SUCCEEDED(rv)) - proxyHTTP->SetProxyType(proxyType); - - rv = channel->SetProxyChannel(proxyChannel); - } - *result = channel; return rv; } diff --git a/mozilla/netwerk/protocol/http/public/MANIFEST b/mozilla/netwerk/protocol/http/public/MANIFEST index 5e339b4791c..51fc96dd9c1 100644 --- a/mozilla/netwerk/protocol/http/public/MANIFEST +++ b/mozilla/netwerk/protocol/http/public/MANIFEST @@ -2,4 +2,3 @@ # This is a list of local files which get copied to the mozilla:dist directory # -nsHTTPEnums.h diff --git a/mozilla/netwerk/protocol/http/public/MANIFEST_IDL b/mozilla/netwerk/protocol/http/public/MANIFEST_IDL index 2d5666f4931..56bade2d1ef 100644 --- a/mozilla/netwerk/protocol/http/public/MANIFEST_IDL +++ b/mozilla/netwerk/protocol/http/public/MANIFEST_IDL @@ -1,5 +1,5 @@ -nsIHTTPChannel.idl -nsIHTTPProtocolHandler.idl -nsIHTTPHeader.idl +nsIHttpChannel.idl +nsIHttpProtocolHandler.idl +nsIHttpAuthenticator.idl nsIHttpNotify.idl -nsIHTTPEventSink.idl +nsIHttpEventSink.idl diff --git a/mozilla/netwerk/protocol/http/public/Makefile.in b/mozilla/netwerk/protocol/http/public/Makefile.in index d7b241287be..dc3c17ad9bb 100644 --- a/mozilla/netwerk/protocol/http/public/Makefile.in +++ b/mozilla/netwerk/protocol/http/public/Makefile.in @@ -26,20 +26,14 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -MODULE = necko -XPIDL_MODULE = necko_http +XPIDL_MODULE = necko_http XPIDLSRCS = \ - nsIHTTPHeader.idl \ - nsIHTTPChannel.idl \ + nsIHttpChannel.idl \ nsIHttpNotify.idl \ - nsIHTTPProtocolHandler.idl \ - nsIHTTPEventSink.idl \ - $(NULL) - -EXPORTS = \ - nsHTTPEnums.h \ + nsIHttpProtocolHandler.idl \ + nsIHttpEventSink.idl \ + nsIHttpAuthenticator.idl \ $(NULL) include $(topsrcdir)/config/rules.mk - diff --git a/mozilla/netwerk/protocol/http/public/makefile.win b/mozilla/netwerk/protocol/http/public/makefile.win index 3d574d31ce6..2dcbb39905b 100644 --- a/mozilla/netwerk/protocol/http/public/makefile.win +++ b/mozilla/netwerk/protocol/http/public/makefile.win @@ -27,16 +27,11 @@ include <$(DEPTH)/config/config.mak> XPIDL_MODULE = necko_http XPIDLSRCS = \ - .\nsIHTTPHeader.idl \ - .\nsIHTTPChannel.idl \ + .\nsIHttpChannel.idl \ .\nsIHttpNotify.idl \ - .\nsIHTTPProtocolHandler.idl \ - .\nsIHTTPEventSink.idl \ - $(NULL) - -EXPORTS = \ - nsHTTPEnums.h \ + .\nsIHttpProtocolHandler.idl \ + .\nsIHttpEventSink.idl \ + .\nsIHttpAuthenticator.idl \ $(NULL) include <$(DEPTH)/config/rules.mak> - diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl b/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl new file mode 100644 index 00000000000..dc77f6e6315 --- /dev/null +++ b/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsISupports.idl" + +interface nsIHttpChannel; + +[scriptable, uuid(5c6a8dda-0417-4694-807d-aa504699224e)] +interface nsIHttpAuthenticator : nsISupports +{ + /** + * Called to generate the authentication credentials for a particular + * server/proxy challenge. + */ + string generateCredentials(in nsIHttpChannel channel, + in string challenge, + in wstring username, + in wstring password); +}; + +%{C++ +#define NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX \ + "@mozilla.org/network/http-authenticator;1?scheme=" +%} diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl b/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl new file mode 100644 index 00000000000..76c981824b3 --- /dev/null +++ b/mozilla/netwerk/protocol/http/public/nsIHttpChannel.idl @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +#include "nsIChannel.idl" + +interface nsIHttpHeaderVisitor; + +[scriptable, uuid(d78b53c8-d03f-4fd8-b2ee-7b36fcd150d1)] +interface nsIHttpChannel : nsIChannel +{ + /************************************************************************** + * Request info... + */ + + /** + * The request method is case insensitive + */ + attribute string requestMethod; + + /** + * Get/set the referrer URI on the request. This is the address (URI) of + * the resource from which this channel's URI was obtained (see RFC2616 + * section 14.36). The referrer type may be used to block the referrer + * from being sent in certain cases. + */ + readonly attribute nsIURI referrer; + void setReferrer(in nsIURI referrer, in unsigned long referrerType); + + /* possible values for the referrerType */ + const unsigned long REFERRER_NONE = 0; + const unsigned long REFERRER_LINK_CLICK = 1; // http link clicks + const unsigned long REFERRER_INLINES = 2; // images and other inlines + const unsigned long REFERRER_NON_HTTP = 3; // e.g. news or FTP clicks + + /** + * Header strings are case insensitive + */ + string getRequestHeader(in string header); + void setRequestHeader(in string header, in string value); + void visitRequestHeaders(in nsIHttpHeaderVisitor visitor); + + /** + * Set the stream to be uploaded by this HTTP channel. Setting this causes + * the HTTP method to be changed to POST. For PUT requests, the method + * must be explicitly set. + */ + attribute nsIInputStream uploadStream; + + + /************************************************************************** + * Response info... + */ + + readonly attribute unsigned long responseStatus; + readonly attribute string responseStatusText; + + /** + * Header strings are case insensitive + */ + string getResponseHeader(in string header); + void setResponseHeader(in string header, in string value); + void visitResponseHeaders(in nsIHttpHeaderVisitor visitor); + + /** + * Get the charset for the response, which may be NULL if not specified + * by the server (ie. the Content-Type header may not specify a charset). + */ + readonly attribute string charset; + + /** + * This attribute controls whether or not content conversion should be + * done per the Content-Encoding response header. Its value is true + * by default. + */ + attribute boolean applyConversion; +}; + +/** + * Implement this interface to visit http headers. + */ +[scriptable, uuid(0cf40717-d7c1-4a94-8c1e-d6c9734101bb)] +interface nsIHttpHeaderVisitor : nsISupports +{ + void visitHeader(in string header, in string value); +}; diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpEventSink.idl b/mozilla/netwerk/protocol/http/public/nsIHttpEventSink.idl new file mode 100644 index 00000000000..06413b4c73b --- /dev/null +++ b/mozilla/netwerk/protocol/http/public/nsIHttpEventSink.idl @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +#include "nsISupports.idl" + +interface nsIChannel; +interface nsIHttpChannel; +interface nsIURI; + +/** + * nsIHttpEventSink + * + * Implement this interface to receive control over various HTTP events. The + * HTTP channel will try to get this interface from its notificationCallbacks + * attribute, and if it doesn't find it there it will look for it from its + * loadGroup's notificationCallbacks attribute. + * + * These methods are called before onStartRequest, and should be handled + * SYNCHRONOUSLY. + */ +[scriptable, uuid(9475a6af-6352-4251-90f9-d65b1cd2ea15)] +interface nsIHttpEventSink : nsISupports +{ + /** + * Called when a redirect occurs due to a HTTP response like 302. The + * redirection may be to a non-http channel. + * + * @return failure cancels redirect + */ + void onRedirect(in nsIHttpChannel httpChannel, + in nsIChannel newChannel); +}; diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpNotify.idl b/mozilla/netwerk/protocol/http/public/nsIHttpNotify.idl new file mode 100644 index 00000000000..9d8dd4fed31 --- /dev/null +++ b/mozilla/netwerk/protocol/http/public/nsIHttpNotify.idl @@ -0,0 +1,54 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +/* The internal networking library http interface. External modules + * desiring to receive notification of internal http events implement this + * interface, and pass it into the nsINetModuleMgr Register and Unregister + * methods. + */ + +#include "nsINetNotify.idl" + +interface nsIHttpChannel; + +[scriptable, uuid(5a6dec35-fb37-4460-85bb-1c572521d0cf)] +interface nsIHttpNotify : nsINetNotify +{ + /** + * Before an HTTP request is sent to the server, this method is called. + * The implementor of this method can then chose to set any additional + * headers for this request before the request is actually sent to the + * server. The HTTP protocol interpreter blocks on this call until it + * returns. + */ + void onModifyRequest(in nsIHttpChannel httpChannel); + + /** + * After an HTTP server response is received, this method is called + * asyncronously. The implementor can interrogate the nsIHttpChannel as + * it deems necessary. The HTTP protocol interpreter blocks on this call + * until it returns. + */ + void onExamineResponse(in nsIHttpChannel httpChannel); +}; diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpProtocolHandler.idl b/mozilla/netwerk/protocol/http/public/nsIHttpProtocolHandler.idl new file mode 100644 index 00000000000..25394cfc285 --- /dev/null +++ b/mozilla/netwerk/protocol/http/public/nsIHttpProtocolHandler.idl @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +#include "nsIProtocolHandler.idl" + +[scriptable, uuid(122c91c0-2485-40ba-89c9-b895934921bc)] +interface nsIHttpProtocolHandler : nsIProtocolHandler +{ + /** + * Constructs a new http proxy channel. + * + * @param uri - the desired request-URI (eg. ftp://ftp.mozilla.org/) + * @param proxyHost - the proxy hostname + * @param proxyPort - the proxy port + * @param proxyType - the proxy type (usually "http" or "socks") + */ + nsIChannel newProxyChannel(in nsIURI uri, + in string proxyHost, + in long proxyPort, + in string proxyType); + + /** + * Get the HTTP advertised user agent string. + */ + readonly attribute string userAgent; + + /** + * Get the application name. + * + * @return The name of this application (eg. "Mozilla"). + */ + readonly attribute string appName; + + /** + * Get the application version string. + * + * @return The complete version (major and minor) string. (eg. "5.0") + */ + readonly attribute string appVersion; + + /** + * @return The vendor name. + */ + attribute string vendor; + + /** + * @return The vendor sub string. + */ + attribute string vendorSub; + + /** + * @return The vendor comment. + */ + attribute string vendorComment; + + /** + * @return The product name. + */ + attribute string product; + + /** + * @return A product sub string. + */ + attribute string productSub; + + /** + * @return A product comment. + */ + attribute string productComment; + + /** + * Get the current platform. + * + * @return The platform this application is running on + * (eg. "Windows", "Macintosh", "X11") + */ + readonly attribute string platform; + + /** + * Get the current oscpu. + * + * @return The oscpu this application is running on + */ + readonly attribute string oscpu; + + /** + * Get the translation of the application. The value for language + * is usually a 2-letter code such as "en" and occasionally a + * five-character code to indicate a language subtype, such as "zh_CN". + */ + attribute string language; + + /** + * Get/Set the application comment misc portion. + */ + attribute string misc; +}; + +%{C++ +// HTTP protocol handler will do_GetService() on each ContractId that is +// registered under this category when it comes alive. +#define NS_HTTP_STARTUP_CATEGORY "http-startup-category" +// If the resulting service implements nsIObserver, then its Observer +// method will be invoked with the following topic. +#define NS_HTTP_STARTUP_TOPIC (NS_LITERAL_STRING("http-startup").get()) +%} diff --git a/mozilla/netwerk/protocol/http/src/Makefile.in b/mozilla/netwerk/protocol/http/src/Makefile.in index fd4e3b200d4..86fee7611ff 100644 --- a/mozilla/netwerk/protocol/http/src/Makefile.in +++ b/mozilla/netwerk/protocol/http/src/Makefile.in @@ -26,23 +26,21 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -MODULE = necko -LIBRARY_NAME = nkhttp_s +LIBRARY_NAME = nkhttp_s REQUIRES = xpcom string pref js nkcache exthandler mimetype wallet dom layout widget caps xpconnect CPPSRCS = \ - nsHTTPHandler.cpp \ - nsHTTPChannel.cpp \ - nsHTTPRequest.cpp \ - nsHTTPResponseListener.cpp \ - nsHTTPResponse.cpp \ - nsHTTPEncodeStream.cpp \ - nsHTTPHeaderArray.cpp \ - nsHTTPAtoms.cpp \ - nsBasicAuth.cpp \ - nsAuth.cpp \ - nsAuthEngine.cpp \ - nsHTTPSHandler.cpp \ + nsHttp.cpp \ + nsHttpHandler.cpp \ + nsHttpHeaderArray.cpp \ + nsHttpConnection.cpp \ + nsHttpTransaction.cpp \ + nsHttpChannel.cpp \ + nsHttpRequestHead.cpp \ + nsHttpResponesHead.cpp \ + nsHttpChunkedDecoder.cpp \ + nsHttpAuthCache.cpp \ + nsHttpBasicAuth.cpp \ $(NULL) LOCAL_INCLUDES=-I$(srcdir)/../../../streamconv/converters diff --git a/mozilla/netwerk/protocol/http/src/makefile.win b/mozilla/netwerk/protocol/http/src/makefile.win index 98abbbe4d48..dd134b8244b 100644 --- a/mozilla/netwerk/protocol/http/src/makefile.win +++ b/mozilla/netwerk/protocol/http/src/makefile.win @@ -19,14 +19,6 @@ # # Contributor(s): -#------------------------------------------------------------------------ -# -# Makefile to build the pluggable HTTP -# -# -Gagan Saksena 03/25/99 -# -#------------------------------------------------------------------------ - DEPTH=..\..\..\.. include <$(DEPTH)/config/config.mak> @@ -36,19 +28,18 @@ LIBRARY_NAME=nkhttp_s LCFLAGS = -DWIN32_LEAN_AND_MEAN -D_IMPL_NS_NET -CPP_OBJS= \ - .\$(OBJDIR)\nsHTTPSHandler.obj \ - .\$(OBJDIR)\nsHTTPHandler.obj \ - .\$(OBJDIR)\nsHTTPChannel.obj \ - .\$(OBJDIR)\nsHTTPRequest.obj \ - .\$(OBJDIR)\nsHTTPResponseListener.obj \ - .\$(OBJDIR)\nsHTTPResponse.obj \ - .\$(OBJDIR)\nsHTTPHeaderArray.obj \ - .\$(OBJDIR)\nsHTTPAtoms.obj \ - .\$(OBJDIR)\nsHTTPEncodeStream.obj \ - .\$(OBJDIR)\nsBasicAuth.obj \ - .\$(OBJDIR)\nsAuth.obj \ - .\$(OBJDIR)\nsAuthEngine.obj \ +CPP_OBJS= \ + .\$(OBJDIR)\nsHttp.obj \ + .\$(OBJDIR)\nsHttpHandler.obj \ + .\$(OBJDIR)\nsHttpHeaderArray.obj \ + .\$(OBJDIR)\nsHttpConnection.obj \ + .\$(OBJDIR)\nsHttpTransaction.obj \ + .\$(OBJDIR)\nsHttpChannel.obj \ + .\$(OBJDIR)\nsHttpRequestHead.obj \ + .\$(OBJDIR)\nsHttpResponseHead.obj \ + .\$(OBJDIR)\nsHttpChunkedDecoder.obj \ + .\$(OBJDIR)\nsHttpAuthCache.obj \ + .\$(OBJDIR)\nsHttpBasicAuth.obj \ $(NULL) include <$(DEPTH)\config\rules.mak> diff --git a/mozilla/netwerk/protocol/http/src/nsHttp.cpp b/mozilla/netwerk/protocol/http/src/nsHttp.cpp new file mode 100644 index 00000000000..c7beff1c44f --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttp.cpp @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttp.h" +#include "nscore.h" +#include "plhash.h" +#include "nsCRT.h" + +#if defined(PR_LOGGING) +PRLogModuleInfo *gHttpLog = nsnull; +#endif + +// define storage for all atoms +#define HTTP_ATOM(_name, _value) nsHttpAtom nsHttp::_name = { _value }; +#include "nsHttpAtomList.h" +#undef HTTP_ATOM + +// we keep a linked list of atoms allocated on the heap for easy clean up +// when the atom table is destroyed. +struct HttpHeapAtom { + char *value; + struct HttpHeapAtom *next; + + HttpHeapAtom(const char *v) : value(PL_strdup(v)), next(0) {} + ~HttpHeapAtom() { PL_strfree(value); } +}; + +static struct PLHashTable *gHttpAtomTable = nsnull; +static struct HttpHeapAtom *gHeapAtomsHead = nsnull; +static struct HttpHeapAtom *gHeapAtomsTail = nsnull; + +// Hash string ignore case, based on PL_HashString +static PLHashNumber +StringHash(const PRUint8 *key) +{ + PLHashNumber h; + const PRUint8 *s; + + h = 0; + for (s = key; *s; s++) + h = (h >> 28) ^ (h << 4) ^ nsCRT::ToLower((char)*s); + return h; +} + +static PRIntn +StringCompare(const char *a, const char *b) +{ + return PL_strcasecmp(a, b) == 0; +} + +#if 0 +#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift)) +static void +DumpAtomTable() +{ + if (gHttpAtomTable) { + PLHashEntry *he, **hep; + PRUint32 i, nbuckets = NBUCKETS(gHttpAtomTable); + for (i=0; ibuckets[i]; + while ((he = *hep) != 0) { + printf("(%s,%x,%x) ", (const char *) he->key, he->keyHash, he->value); + hep = &he->next; + } + printf("\n"); + } + } +} +#endif + +// We put the atoms in a hash table for speedy lookup.. see ResolveAtom. +static nsresult +CreateAtomTable() +{ + LOG(("CreateAtomTable\n")); + + if (gHttpAtomTable) + return NS_OK; + + gHttpAtomTable = PL_NewHashTable(128, (PLHashFunction) StringHash, + (PLHashComparator) StringCompare, + (PLHashComparator) 0, 0, 0); + if (!gHttpAtomTable) + return NS_ERROR_OUT_OF_MEMORY; + +#define HTTP_ATOM(_name, _value) \ + PL_HashTableAdd(gHttpAtomTable, _value, (void *) nsHttp::_name.get()); +#include "nsHttpAtomList.h" +#undef HTTP_ATOM + + //DumpAtomTable(); + return NS_OK; +} + +void +nsHttp::DestroyAtomTable() +{ + if (gHttpAtomTable) { + PL_HashTableDestroy(gHttpAtomTable); + gHttpAtomTable = nsnull; + } + while (gHeapAtomsHead) { + gHeapAtomsTail = gHeapAtomsHead->next; + delete gHeapAtomsHead; + gHeapAtomsHead = gHeapAtomsTail; + } + gHeapAtomsTail = nsnull; +} + +nsHttpAtom +nsHttp::ResolveAtom(const char *str) +{ + if (!gHttpAtomTable) + CreateAtomTable(); + + nsHttpAtom atom = { nsnull }; + + if (gHttpAtomTable && str) { + atom._val = (const char *) PL_HashTableLookup(gHttpAtomTable, str); + + // if the atom could not be found in the atom table, then we'll go + // and allocate a new atom on the heap. + if (!atom) { + HttpHeapAtom *heapAtom = new HttpHeapAtom(str); + if (!heapAtom) + return atom; + if (!heapAtom->value) { + delete heapAtom; + return atom; + } + + // append this heap atom to the list of all heap atoms + if (!gHeapAtomsHead) { + gHeapAtomsHead = heapAtom; + gHeapAtomsTail = heapAtom; + } + else { + gHeapAtomsTail->next = heapAtom; + gHeapAtomsTail = heapAtom; + } + + // now insert the heap atom into the atom table + PL_HashTableAdd(gHttpAtomTable, heapAtom->value, heapAtom->value); + + // now assign the value to the atom + atom._val = (const char *) heapAtom->value; + } + } + + return atom; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttp.h b/mozilla/netwerk/protocol/http/src/nsHttp.h new file mode 100644 index 00000000000..bcdb482ae2e --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttp.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttp_h__ +#define nsHttp_h__ + +#include "plstr.h" +#include "prlog.h" +#include "prtime.h" +#include "nsISupportsUtils.h" + +#if defined(PR_LOGGING) +// +// Log module for HTTP Protocol logging... +// +// To enable logging (see prlog.h for full details): +// +// set NSPR_LOG_MODULES=nsHttp2:5 +// set NSPR_LOG_FILE=http.log +// +// this enables PR_LOG_ALWAYS level information and places all output in +// the file http.log +// +extern PRLogModuleInfo *gHttpLog; +#endif + +#define LOG1(args) PR_LOG(gHttpLog, 1, args) +#define LOG2(args) PR_LOG(gHttpLog, 2, args) +#define LOG3(args) PR_LOG(gHttpLog, 3, args) +#define LOG4(args) PR_LOG(gHttpLog, 4, args) +#define LOG(args) LOG4(args) + +#define NS_HTTP_SEGMENT_SIZE 4096 +#define NS_HTTP_BUFFER_SIZE 4096*4 // 16k maximum + +enum nsHttpVersion { + NS_HTTP_VERSION_UNKNOWN, + NS_HTTP_VERSION_0_9, + NS_HTTP_VERSION_1_0, + NS_HTTP_VERSION_1_1 +}; + +//----------------------------------------------------------------------------- +// http atoms... +//----------------------------------------------------------------------------- + +struct nsHttpAtom +{ + operator const char *() { return _val; } + const char *get() { return _val; } + + // private + const char *_val; +}; + +struct nsHttp +{ + static void DestroyAtomTable(); + + // will dynamically add atoms to the table if they don't already exist + static nsHttpAtom ResolveAtom(const char *); + + /* Declare all atoms + * + * The atom names and values are stored in nsHttpAtomList.h and + * are brought to you by the magic of C preprocessing + * + * Add new atoms to nsHttpAtomList and all support logic will be auto-generated + */ +#define HTTP_ATOM(_name, _value) static nsHttpAtom _name; +#include "nsHttpAtomList.h" +#undef HTTP_ATOM +}; + +//----------------------------------------------------------------------------- +// utilities... +//----------------------------------------------------------------------------- + +static inline nsresult +DupString(const char *src, char **dst) +{ + NS_ENSURE_ARG_POINTER(dst); + *dst = PL_strdup(src); + return *dst ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +static inline PRUint32 +PRTimeToSeconds(PRTime t_usec) +{ + PRTime usec_per_sec; + PRUint32 t_sec; + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_DIV(t_usec, t_usec, usec_per_sec); + LL_L2I(t_sec, t_usec); + return t_sec; +} + +#define NowInSeconds() PRTimeToSeconds(PR_Now()) + +#endif diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAtomList.h b/mozilla/netwerk/protocol/http/src/nsHttpAtomList.h new file mode 100644 index 00000000000..40861b21dd8 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpAtomList.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + */ + +/****** + This file contains the list of all HTTP atoms + See nsHttp.h for access to the atoms + + It is designed to be used as inline input to nsHttp.cpp *only* + through the magic of C preprocessing. + + All entires must be enclosed in the macro HTTP_ATOM which will have cruel + and unusual things done to it + + The first argument to HTTP_ATOM is the C++ name of the atom + The second argument it HTTP_ATOM is the string value of the atom + ******/ + +HTTP_ATOM(Accept, "Accept") +HTTP_ATOM(Accept_Charset, "Accept-Charset") +HTTP_ATOM(Accept_Encoding, "Accept-Encoding") +HTTP_ATOM(Accept_Language, "Accept-Language") +HTTP_ATOM(Accept_Ranges, "Accept-Ranges") +HTTP_ATOM(Age, "Age") +HTTP_ATOM(Allow, "Allow") +HTTP_ATOM(Authentication, "Authentication") +HTTP_ATOM(Authorization, "Authorization") +HTTP_ATOM(Cache_Control, "Cache-Control") +HTTP_ATOM(Connection, "Connection") +HTTP_ATOM(Content_Base, "Content-Base") +HTTP_ATOM(Content_Encoding, "Content-Encoding") +HTTP_ATOM(Content_Language, "Content-Language") +HTTP_ATOM(Content_Length, "Content-Length") +HTTP_ATOM(Content_Location, "Content-Location") +HTTP_ATOM(Content_MD5, "Content-MD5") +HTTP_ATOM(Content_Range, "Content-Range") +HTTP_ATOM(Content_Transfer_Encoding, "Content-Transfer-Encoding") +HTTP_ATOM(Content_Type, "Content-Type") +HTTP_ATOM(Date, "Date") +HTTP_ATOM(Derived_From, "Derived-From") +HTTP_ATOM(ETag, "Etag") +HTTP_ATOM(Expect, "Expect") +HTTP_ATOM(Expires, "Expires") +HTTP_ATOM(From, "From") +HTTP_ATOM(Forwarded, "Forwarded") +HTTP_ATOM(Host, "Host") +HTTP_ATOM(If_Match, "If-Match") +HTTP_ATOM(If_Match_Any, "If-Match-Any") +HTTP_ATOM(If_Modified_Since, "If-Modified-Since") +HTTP_ATOM(If_None_Match, "If-None-Match") +HTTP_ATOM(If_None_Match_Any, "If-None-Match-Any") +HTTP_ATOM(If_Range, "If-Range") +HTTP_ATOM(If_Unmodified_Since, "If-Unmodified-Since") +HTTP_ATOM(Keep_Alive, "Keep-Alive") +HTTP_ATOM(Last_Modified, "Last-Modified") +HTTP_ATOM(Link, "Link") +HTTP_ATOM(Location, "Location") +HTTP_ATOM(Max_Forwards, "Max-Forwards") +HTTP_ATOM(Message_Id, "Message-Id") +HTTP_ATOM(Mime, "Mime") +HTTP_ATOM(Pragma, "Pragma") +HTTP_ATOM(Vary, "Vary") +HTTP_ATOM(Proxy_Authenticate, "Proxy-Authenticate") +HTTP_ATOM(Proxy_Authorization, "Proxy-Authorization") +HTTP_ATOM(Proxy_Connection, "Proxy-Connection") +HTTP_ATOM(Range, "Range") +HTTP_ATOM(Referer, "Referer") +HTTP_ATOM(Retry_After, "Retry-After") +HTTP_ATOM(Server, "Server") +HTTP_ATOM(TE, "TE") +HTTP_ATOM(Title, "Title") +HTTP_ATOM(Trailer, "Trailer") +HTTP_ATOM(Transfer_Encoding, "Transfer-Encoding") +HTTP_ATOM(Upgrade, "Upgrade") +HTTP_ATOM(URI, "URI") +HTTP_ATOM(User_Agent, "User-Agent") +HTTP_ATOM(Version, "Version") +HTTP_ATOM(Warning, "Warning") +HTTP_ATOM(WWW_Authenticate, "WWW-Authenticate") +HTTP_ATOM(Set_Cookie, "Set-Cookie") +HTTP_ATOM(Cookie, "Cookie") + +// methods are atoms too. +// +// note: an uppercase DELETE causes compilation problems under msvc6, so we'll +// just keep the methods mixedcase even though they're normally written all +// uppercase -- darin + +HTTP_ATOM(Options, "OPTIONS") +HTTP_ATOM(Head, "HEAD") +HTTP_ATOM(Post, "POST") +HTTP_ATOM(Put, "PUT") +HTTP_ATOM(Get, "GET") +HTTP_ATOM(Index, "INDEX") +HTTP_ATOM(Delete, "DELETE") +HTTP_ATOM(Trace, "TRACE") +HTTP_ATOM(Connect, "CONNECT") +HTTP_ATOM(M_Post, "M-POST") diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp new file mode 100644 index 00000000000..ec26345b339 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include +#include "nsHttp.h" +#include "nsHttpAuthCache.h" +#include "nsString.h" +#include "plstr.h" +#include "prprf.h" + +//----------------------------------------------------------------------------- +// nsHttpAuthCache +//----------------------------------------------------------------------------- + +nsHttpAuthCache::nsHttpAuthCache() + : mDB(nsnull) +{ +} + +nsHttpAuthCache::~nsHttpAuthCache() +{ + if (mDB) + ClearAll(); +} + +nsresult +nsHttpAuthCache::Init() +{ + NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED); + + LOG(("nsHttpAuthCache::Init\n")); + + mDB = PL_NewHashTable(128, (PLHashFunction) PL_HashString, + (PLHashComparator) PL_CompareStrings, + (PLHashComparator) 0, &gHashAllocOps, this); + if (!mDB) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +nsresult +nsHttpAuthCache::GetCredentialsForPath(const char *host, + PRInt32 port, + const char *path, + nsACString &realm, + nsACString &creds) +{ + NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED); + + LOG(("nsHttpAuthCache::GetCredentialsForPath [host=%s:%d path=%s]\n", + host, port, path)); + + nsCAutoString key; + nsEntryList *list = LookupEntryList(host, port, key); + if (!list) + return NS_ERROR_NOT_AVAILABLE; + + return list->GetCredentialsForPath(path, realm, creds); +} + +nsresult +nsHttpAuthCache::GetCredentialsForDomain(const char *host, + PRInt32 port, + const char *realm, + nsACString &creds) +{ + NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED); + + LOG(("nsHttpAuthCache::GetCredentialsForDomain [host=%s:%d realm=%s]\n", + host, port, realm)); + + nsCAutoString key; + nsEntryList *list = LookupEntryList(host, port, key); + if (!list) + return NS_ERROR_NOT_AVAILABLE; + + return list->GetCredentialsForRealm(realm, creds); +} + +nsresult +nsHttpAuthCache::SetCredentials(const char *host, + PRInt32 port, + const char *path, + const char *realm, + const char *creds) +{ + nsresult rv; + + NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED); + + LOG(("nsHttpAuthCache::SetCredentials\n")); + + if (!mDB) { + rv = Init(); + if (NS_FAILED(rv)) return rv; + } + + nsCAutoString key; + nsEntryList *list = LookupEntryList(host, port, key); + + if (!list) { + // only create a new list if we have a real entry + if (!creds) + return NS_OK; + + // create a new entry list and set the given entry + list = new nsEntryList(); + if (!list) + return NS_ERROR_OUT_OF_MEMORY; + rv = list->SetCredentials(path, realm, creds); + if (NS_FAILED(rv)) + delete list; + else + PL_HashTableAdd(mDB, PL_strdup(key.get()), list); + return rv; + } + + rv = list->SetCredentials(path, realm, creds); + if (NS_SUCCEEDED(rv) && (list->Count() == 0)) { + // the list has no longer has any entries + // XXX leak.. need to free key + PL_HashTableRemove(mDB, key.get()); + delete list; + } + + return rv; +} + +nsresult +nsHttpAuthCache::ClearAll() +{ + LOG(("nsHttpAuthCache::ClearAll\n")); + + if (mDB) { + PL_HashTableDestroy(mDB); + mDB = 0; + } + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpAuthCache +//----------------------------------------------------------------------------- + +nsHttpAuthCache::nsEntryList * +nsHttpAuthCache::LookupEntryList(const char *host, PRInt32 port, nsAFlatCString &key) +{ + char buf[32]; + + if (!mDB) + return nsnull; + + PR_snprintf(buf, sizeof(buf), "%d", port); + + key.Assign(host); + key.Append(':'); + key.Append(buf); + + return (nsEntryList *) PL_HashTableLookup(mDB, key.get()); +} + +void * +nsHttpAuthCache::AllocTable(void *self, PRSize size) +{ + return malloc(size); +} + +void +nsHttpAuthCache::FreeTable(void *self, void *item) +{ + free(item); +} + +PLHashEntry * +nsHttpAuthCache::AllocEntry(void *self, const void *key) +{ + return (PLHashEntry *) malloc(sizeof(PLHashEntry)); +} + +void +nsHttpAuthCache::FreeEntry(void *self, PLHashEntry *he, PRUintn flag) +{ + if (flag == HT_FREE_VALUE) { + // this would only happen if PL_HashTableAdd were to replace an + // existing entry in the hash table, but we _always_ do a lookup + // before adding a new entry to avoid this case. + NS_NOTREACHED("should never happen"); + } + else if (flag == HT_FREE_ENTRY) { + // three wonderful flavors of freeing memory ;-) + delete (nsEntryList *) he->value; + PL_strfree((char *) he->key); + free(he); + } +} + +PLHashAllocOps nsHttpAuthCache::gHashAllocOps = +{ + nsHttpAuthCache::AllocTable, + nsHttpAuthCache::FreeTable, + nsHttpAuthCache::AllocEntry, + nsHttpAuthCache::FreeEntry +}; + +//----------------------------------------------------------------------------- +// nsHttpAuthCache::nsEntry +//----------------------------------------------------------------------------- + +nsHttpAuthCache:: +nsEntry::nsEntry(const char *path, const char *realm, const char *creds) + : mPath(PL_strdup(path)) + , mRealm(PL_strdup(realm)) + , mCreds(PL_strdup(creds)) +{ + LOG(("Creating nsHttpAuthCache::nsEntry @%x\n", this)); +} + +nsHttpAuthCache:: +nsEntry::~nsEntry() +{ + LOG(("Destroying nsHttpAuthCache::nsEntry @%x\n", this)); + + PL_strfree(mPath); + PL_strfree(mRealm); + PL_strfree(mCreds); +} + +void nsHttpAuthCache:: +nsEntry::SetPath(const char *path) +{ + PL_strfree(mPath); + mPath = PL_strdup(path); +} + +void nsHttpAuthCache:: +nsEntry::SetCreds(const char *creds) +{ + PL_strfree(mCreds); + mCreds = PL_strdup(creds); +} + +//----------------------------------------------------------------------------- +// nsHttpAuthCache::nsEntryList +//----------------------------------------------------------------------------- + +nsHttpAuthCache:: +nsEntryList::nsEntryList() +{ + LOG(("Creating nsHttpAuthCache::nsEntryList @%x\n", this)); +} + +nsHttpAuthCache:: +nsEntryList::~nsEntryList() +{ + LOG(("Destroying nsHttpAuthCache::nsEntryList @%x\n", this)); + + PRInt32 i; + for (i=0; iPath(), PL_strlen(entry->Path()))) + break; + entry = nsnull; + } + + if (!entry) + return NS_ERROR_NOT_AVAILABLE; + + realm.Assign(entry->Realm()); + creds.Assign(entry->Creds()); + return NS_OK; +} + +nsresult nsHttpAuthCache:: +nsEntryList::GetCredentialsForRealm(const char *realm, + nsACString &creds) +{ + NS_ENSURE_ARG_POINTER(realm); + + nsEntry *entry = nsnull; + + // look for an entry that matches this realm + PRInt32 i; + for (i=0; iRealm())) + break; + entry = nsnull; + } + + if (!entry) + return NS_ERROR_NOT_AVAILABLE; + + creds.Assign(entry->Creds()); + return NS_OK; +} + +nsresult nsHttpAuthCache:: +nsEntryList::SetCredentials(const char *path, + const char *realm, + const char *creds) +{ + NS_ENSURE_ARG_POINTER(realm); + + nsEntry *entry = nsnull; + + // look for an entry with a matching realm + PRInt32 i; + for (i=0; iRealm())) + break; + entry = nsnull; + } + + if (!entry) { + if (creds) { + entry = new nsEntry(path, realm, creds); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + mList.AppendElement(entry); + } + // else, nothing to do + } + else if (!creds) { + mList.RemoveElementAt(i); + delete entry; + } + else { + // update the entry... + if (path) { + // we should hold onto the top-most of the two path + PRUint32 len1 = PL_strlen(path); + PRUint32 len2 = PL_strlen(entry->Path()); + if (len1 < len2) + entry->SetPath(path); + } + entry->SetCreds(creds); + } + + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h new file mode 100644 index 00000000000..5d38837d2d8 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +#ifndef nsHttpAuthCache_h__ +#define nsHttpAuthCache_h__ + +#include "nsError.h" +#include "nsVoidArray.h" +#include "nsAString.h" +#include "plhash.h" + +//----------------------------------------------------------------------------- +// nsHttpAuthCache +//----------------------------------------------------------------------------- + +class nsHttpAuthCache +{ +public: + nsHttpAuthCache(); + ~nsHttpAuthCache(); + + nsresult Init(); + + // host and port are required + // path can be null + nsresult GetCredentialsForPath(const char *host, + PRInt32 port, + const char *path, + nsACString &realm, + nsACString &credentials); + + // host and port are required + // realm must not be null + nsresult GetCredentialsForDomain(const char *host, + PRInt32 port, + const char *realm, + nsACString &credentials); + + // host and port are required + // path can be null + // realm must not be null + // credentials, if null, clears the entry + nsresult SetCredentials(const char *host, + PRInt32 port, + const char *directory, + const char *realm, + const char *credentials); + + // Expire all existing auth list entries including proxy auths. + nsresult ClearAll(); + +public: /* internal */ + + class nsEntry + { + public: + nsEntry(const char *path, + const char *realm, + const char *creds); + ~nsEntry(); + + const char *Path() { return mPath; } + const char *Realm() { return mRealm; } + const char *Creds() { return mCreds; } + + void SetPath(const char *); + void SetCreds(const char *); + + private: + char *mPath; + char *mRealm; + char *mCreds; + }; + + class nsEntryList + { + public: + nsEntryList(); + ~nsEntryList(); + + // path can be null, in which case we'll search for an entry + // with a null path. + nsresult GetCredentialsForPath(const char *path, + nsACString &realm, + nsACString &creds); + + // realm must not be null + nsresult GetCredentialsForRealm(const char *realm, + nsACString &creds); + + // if a matching entry is found, then credentials will be changed. + nsresult SetCredentials(const char *path, + const char *realm, + const char *credentials); + + PRUint32 Count() { return (PRUint32) mList.Count(); } + + private: + nsVoidArray mList; + }; + +private: + nsEntryList *LookupEntryList(const char *host, PRInt32 port, nsAFlatCString &key); + + // hash table allocation functions + static void *AllocTable(void *, PRSize size); + static void FreeTable(void *, void *item); + static PLHashEntry *AllocEntry(void *, const void *key); + static void FreeEntry(void *, PLHashEntry *he, PRUintn flag); + + static PLHashAllocOps gHashAllocOps; + +private: + PLHashTable *mDB; // "host:port" --> nsEntryList +}; + +#endif // nsHttpAuthCache_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp new file mode 100644 index 00000000000..c840d70691e --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Mike Shaver + * Christopher Blizzard + * Darin Fisher + */ + +#include +#include "nsHttp.h" +#include "nsHttpBasicAuth.h" +#include "plbase64.h" +#include "plstr.h" +#include "prmem.h" +#include "nsString.h" + +//----------------------------------------------------------------------------- +// nsHttpBasicAuth +//----------------------------------------------------------------------------- + +nsHttpBasicAuth::nsHttpBasicAuth() +{ + NS_INIT_ISUPPORTS(); +} + +nsHttpBasicAuth::~nsHttpBasicAuth() +{ +} + +//----------------------------------------------------------------------------- +// nsHttpBasicAuth::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS1(nsHttpBasicAuth, nsIHttpAuthenticator); + +//----------------------------------------------------------------------------- +// nsHttpBasicAuth::nsIHttpAuthenticator +//----------------------------------------------------------------------------- + +nsresult +nsHttpBasicAuth::GenerateCredentials(nsIHttpChannel *httpChannel, + const char *challenge, + const PRUnichar *username, + const PRUnichar *password, + char **creds) + +{ + LOG(("nsHttpBasicAuth::GenerateCredentials [challenge=%s]\n", challenge)); + + // we only know how to deal with Basic auth for http. + PRBool isBasicAuth = !PL_strncasecmp(challenge, "basic ", 6); + NS_ENSURE_TRUE(isBasicAuth, NS_ERROR_UNEXPECTED); + + NS_ENSURE_ARG_POINTER(creds); + + // we work with ASCII around here + nsCAutoString userpass; + userpass.AssignWithConversion(username); + if (password) { + userpass.Append(':'); + userpass.AppendWithConversion(password); + } + + char *b64userpass = PL_Base64Encode(userpass.get(), + userpass.Length(), + nsnull); + if (!b64userpass) + return NS_ERROR_OUT_OF_MEMORY; + + // allocate a buffer sizeof("Basic" + " " + b64userpass + "\0") + *creds = (char *) malloc(6 + nsCRT::strlen(b64userpass) + 1); + if (!*creds) + return NS_ERROR_OUT_OF_MEMORY; + + PL_strcpy(*creds, "Basic "); + PL_strcpy(*creds + 6, b64userpass); + + PR_Free(b64userpass); + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.h b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.h new file mode 100644 index 00000000000..e952d039118 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Gagan Saksena (original author) + * Darin Fisher + */ + +#ifndef nsBasicAuth_h__ +#define nsBasicAuth_h__ + +#include "nsIHttpAuthenticator.h" + +class nsIURI; + +//----------------------------------------------------------------------------- +// The nsBasicAuth class produces HTTP Basic-auth responses for a username/ +// (optional)password pair, BASE64("user:pass"). +//----------------------------------------------------------------------------- + +class nsHttpBasicAuth : public nsIHttpAuthenticator +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHTTPAUTHENTICATOR + + nsHttpBasicAuth(); + virtual ~nsHttpBasicAuth(); +}; + +#endif // nsHttpBasicAuth_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp new file mode 100644 index 00000000000..ab644dab82a --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -0,0 +1,2174 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpChannel.h" +#include "nsHttpTransaction.h" +#include "nsHttpConnection.h" +#include "nsHttpHandler.h" +#include "nsHttpAuthCache.h" +#include "nsHttpResponseHead.h" +#include "nsHttp.h" +#include "nsIHttpAuthenticator.h" +#include "nsIAuthPrompt.h" +#include "nsIStringBundle.h" +#include "nsIStreamConverterService.h" +#include "nsISupportsPrimitives.h" +#include "nsNetUtil.h" +#include "nsString2.h" +#include "nsReadableUtils.h" +#include "plstr.h" +#include "prprf.h" + +static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +nsHttpChannel::nsHttpChannel() + : mResponseHead(nsnull) + , mTransaction(nsnull) + , mPrevTransaction(nsnull) + , mConnectionInfo(nsnull) + , mLoadFlags(LOAD_NORMAL) + , mCapabilities(0) + , mStatus(NS_OK) + , mReferrerType(REFERRER_NONE) + , mCachedResponseHead(nsnull) + , mCacheAccess(0) + , mPostID(0) + , mRequestTime(0) + , mIsPending(PR_FALSE) + , mApplyConversion(PR_TRUE) + , mTriedCredentialsFromPrehost(PR_FALSE) + , mFromCacheOnly(PR_FALSE) + , mCachedContentIsValid(PR_FALSE) +{ + LOG(("Creating nsHttpChannel @%x\n", this)); + + NS_INIT_ISUPPORTS(); + + // grab a reference to the handler to ensure that it doesn't go away. + nsHttpHandler *handler = nsHttpHandler::get(); + NS_ADDREF(handler); +} + +nsHttpChannel::~nsHttpChannel() +{ + LOG(("Destroying nsHttpChannel @%x\n", this)); + + if (mResponseHead) { + delete mResponseHead; + mResponseHead = 0; + } + if (mCachedResponseHead) { + delete mCachedResponseHead; + mCachedResponseHead = 0; + } + + NS_IF_RELEASE(mConnectionInfo); + NS_IF_RELEASE(mTransaction); + NS_IF_RELEASE(mPrevTransaction); + + // release our reference to the handler + nsHttpHandler *handler = nsHttpHandler::get(); + NS_RELEASE(handler); +} + +nsresult +nsHttpChannel::Init(nsIURI *uri, + PRUint32 caps, + const char *proxyHost, + PRInt32 proxyPort, + const char *proxyType) +{ + nsresult rv; + + LOG(("nsHttpChannel::Init [this=%x]\n")); + + NS_PRECONDITION(uri, "null uri"); + + mURI = uri; + mOriginalURI = uri; + mCapabilities = caps; + + rv = mURI->GetSpec(getter_Copies(mSpec)); + if (NS_FAILED(rv)) return rv; + + // + // Construct connection info object + // + nsXPIDLCString host; + PRInt32 port = -1; + PRBool usingSSL = PR_FALSE; + + rv = mURI->SchemeIs("https", &usingSSL); + if (NS_FAILED(rv)) return rv; + + rv = mURI->GetHost(getter_Copies(host)); + if (NS_FAILED(rv)) return rv; + + rv = mURI->GetPort(&port); + if (NS_FAILED(rv)) return rv; + + LOG(("host=%s port=%d\n", host.get(), port)); + + mConnectionInfo = new nsHttpConnectionInfo(host, port, + proxyHost, proxyPort, + proxyType, usingSSL); + if (!mConnectionInfo) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mConnectionInfo); + + // make sure our load flags include this bit if this is a secure channel. + if (usingSSL) + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; + + // Set default request method + mRequestHead.SetMethod(nsHttp::Get); + + // + // Set request headers + // + nsCString hostLine; + if (port == -1) + hostLine.Assign(host.get()); + else if (PL_strchr(host.get(), ':')) { + hostLine.Assign('['); + hostLine.Append(host.get()); + hostLine.Append(']'); + } else { + hostLine.Assign(host.get()); + hostLine.Append(':'); + hostLine.AppendInt(port); + } + rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); + if (NS_FAILED(rv)) return rv; + + rv = nsHttpHandler::get()->AddStandardRequestHeaders(&mRequestHead.Headers(), caps); + if (NS_FAILED(rv)) return rv; + + // check to see if authorization headers should be included + rv = AddAuthorizationHeaders(); + if (NS_FAILED(rv)) return rv; + + // Notify nsIHttpNotify implementations + rv = nsHttpHandler::get()->OnModifyRequest(this); + NS_ASSERTION(NS_SUCCEEDED(rv), "OnModifyRequest failed"); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +nsresult +nsHttpChannel::Connect(PRBool firstTime) +{ + nsresult rv; + + LOG(("nsHttpChannel::Connect [this=%x]\n", this)); + + // true when called from AsyncOpen + if (firstTime) { + PRBool delayed = PR_FALSE; + + // open a cache entry for this channel... + rv = OpenCacheEntry(&delayed); + + if (NS_FAILED(rv)) { + LOG(("OpenCacheEntry failed [rv=%x]\n", rv)); + // if this channel is only allowed to pull from the cache, then + // we must fail if we were unable to open a cache entry. + if (mFromCacheOnly) + return mPostID ? NS_ERROR_DOCUMENT_NOT_CACHED : rv; + // otherwise, let's just proceed without using the cache. + } + + if (NS_SUCCEEDED(rv) && delayed) + return NS_OK; + } + + // we may or may not have a cache entry at this point + if (mCacheEntry) { + // inspect the cache entry to determine whether or not we need to go + // out to net to validate it. this call sets mCachedContentIsValid + // and may set request headers as required for cache validation. + rv = CheckCache(); + NS_ASSERTION(NS_SUCCEEDED(rv), "cache check failed"); + + // read straight from the cache if possible... + if (mCachedContentIsValid) { + return ReadFromCache(); + } + else if (mFromCacheOnly) { + // The cache no longer contains the requested resource, and we + // are not allowed to refetch it, so there's nothing more to do. + // If this was a refetch of a POST transaction's resposne, then + // this failure indicates that the response is no longer cached. + return mPostID ? NS_ERROR_DOCUMENT_NOT_CACHED : NS_BINDING_FAILED; + } + } + + // hit the net... + rv = SetupTransaction(); + if (NS_FAILED(rv)) return rv; + + return nsHttpHandler::get()->InitiateTransaction(mTransaction, mConnectionInfo); +} + +// called when Connect fails +nsresult +nsHttpChannel::AsyncAbort(nsresult status) +{ + LOG(("nsHttpChannel::AsyncAbort [this=%x status=%x]\n", this, status)); + + mStatus = status; + mIsPending = PR_FALSE; + + // create an async proxy for the listener.. + nsCOMPtr mgr; + nsHttpHandler::get()->GetProxyObjectManager(getter_AddRefs(mgr)); + if (mgr) { + nsCOMPtr observer; + mgr->GetProxyForObject(NS_CURRENT_EVENTQ, + NS_GET_IID(nsIRequestObserver), + mListener, + PROXY_ASYNC | PROXY_ALWAYS, + getter_AddRefs(observer)); + if (observer) { + observer->OnStartRequest(this, mListenerContext); + observer->OnStopRequest(this, mListenerContext, mStatus); + } + } + // XXX else, no proxy object manager... what do we do? + + // finally remove ourselves from the load group. + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nsnull, status); + + return NS_OK; +} + +nsresult +nsHttpChannel::SetupTransaction() +{ + NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED); + + nsCOMPtr listenerProxy; + nsresult rv = NS_NewStreamListenerProxy(getter_AddRefs(listenerProxy), + this, nsnull, + NS_HTTP_SEGMENT_SIZE, + NS_HTTP_BUFFER_SIZE); + if (NS_FAILED(rv)) return rv; + + // create the transaction object + mTransaction = new nsHttpTransaction(listenerProxy, this); + if (!mTransaction) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mTransaction); + + // use the URI path if not proxying (transparent proxying such as SSL proxy + // does not count here). + nsXPIDLCString requestURI; + if ((mConnectionInfo->ProxyHost() == nsnull) || mConnectionInfo->UsingSSL()) { + rv = mURI->GetPath(getter_Copies(requestURI)); + if (NS_FAILED(rv)) return rv; + } + else + requestURI = mSpec.get(); + + // trim off the #ref portion if any... + char *p = PL_strrchr(requestURI.get(), '#'); + if (p) *p = 0; + + mRequestHead.SetVersion(nsHttpHandler::get()->DefaultVersion()); + mRequestHead.SetRequestURI(requestURI); + + // set the request time for cache expiration calculations + mRequestTime = NowInSeconds(); + + return mTransaction->SetupRequest(&mRequestHead, mUploadStream); +} + +nsresult +nsHttpChannel::ApplyContentConversions() +{ + LOG(("nsHttpChannel::ApplyContentConversions [this=%x]\n", this)); + + if (!mApplyConversion) { + LOG(("not applying conversion per mApplyConversion\n")); + return NS_OK; + } + + const char *val = mResponseHead->PeekHeader(nsHttp::Content_Encoding); + if (val) { + nsCOMPtr serv; + nsresult rv = nsHttpHandler::get()-> + GetStreamConverterService(getter_AddRefs(serv)); + // we won't fail to load the page just because we couldn't load the + // stream converter service.. carry on.. + if (NS_SUCCEEDED(rv)) { + nsCOMPtr converter; + nsAutoString from = NS_ConvertASCIItoUCS2(val); + rv = serv->AsyncConvertData(from.get(), + NS_LITERAL_STRING("uncompressed").get(), + mListener, + mListenerContext, + getter_AddRefs(converter)); + if (NS_SUCCEEDED(rv)) { + LOG(("converter installed from \'%s\' to \'uncompressed\'\n", val)); + mListener = converter; + } + } + } + return NS_OK; +} + +nsresult +nsHttpChannel::ProcessResponse() +{ + nsresult rv = NS_OK; + PRUint32 httpStatus = mResponseHead->Status(); + + LOG(("nsHttpChannel::ProcessResponse [this=%x httpStatus=%u]\n", + this, httpStatus)); + + // handle different server response categories + switch (httpStatus) { + case 200: + case 203: + rv = ProcessNormal(); + break; + case 300: + case 301: + // XXX this is actually a cacheable response + CloseCacheEntry(NS_ERROR_ABORT); + rv = ProcessRedirection(httpStatus); + if (NS_FAILED(rv)) { + LOG(("ProcessRedirection failed [rv=%x]\n", rv)); + rv = ProcessNormal(); + } + break; + case 302: + case 303: + case 305: + case 307: + CloseCacheEntry(NS_ERROR_ABORT); + rv = ProcessRedirection(httpStatus); + if (NS_FAILED(rv)) { + LOG(("ProcessRedirection failed [rv=%x]\n", rv)); + rv = ProcessNormal(); + } + break; + case 304: + rv = ProcessNotModified(); + if (NS_FAILED(rv)) { + LOG(("ProcessNotModified failed [rv=%x]\n", rv)); + rv = ProcessNormal(); + } + break; + case 401: + case 407: + CloseCacheEntry(NS_ERROR_ABORT); + rv = ProcessAuthentication(httpStatus); + if (NS_FAILED(rv)) { + LOG(("ProcessAuthentication failed [rv=%x]\n", rv)); + rv = ProcessNormal(); + } + break; + default: + CloseCacheEntry(NS_ERROR_ABORT); + rv = ProcessNormal(); + break; + } + + return rv; +} + +nsresult +nsHttpChannel::ProcessNormal() +{ + nsresult rv; + + LOG(("nsHttpChannel::ProcessNormal [this=%x]\n", this)); + + // install stream converter if required + ApplyContentConversions(); + + // install cache listener if we still have a cache entry open + if (mCacheEntry) { + rv = CacheReceivedResponse(); + if (NS_FAILED(rv)) return rv; + } + + // notify nsIHttpNotify implementations + rv = nsHttpHandler::get()->OnExamineResponse(this); + NS_ASSERTION(NS_SUCCEEDED(rv), "OnExamineResponse failed"); + + return mListener->OnStartRequest(this, mListenerContext); +} + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +nsresult +nsHttpChannel::ProcessNotModified() +{ + nsresult rv; + + LOG(("nsHttpChannel::ProcessNotModified [this=%x]\n", this)); + + NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED); + + // merge any new headers with the cached response headers + rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers()); + if (NS_FAILED(rv)) return rv; + + // update the cached response headers + nsCAutoString headers; + rv = mCachedResponseHead->Flatten(headers, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + rv = mCacheEntry->SetMetaDataElement("http-headers", headers.get()); + if (NS_FAILED(rv)) return rv; + + // make the cached response be the current response + delete mResponseHead; + mResponseHead = mCachedResponseHead; + mCachedResponseHead = 0; + + rv = UpdateExpirationTime(); + if (NS_FAILED(rv)) return rv; + + // drop our reference to the current transaction... ie. let it finish + // in the background, since we can most likely reuse the connection. + mPrevTransaction = mTransaction; + mTransaction = nsnull; + + // notify nsIHttpNotify implementations as response headers may have changed + nsHttpHandler::get()->OnExamineResponse(this); + + mCachedContentIsValid = PR_TRUE; + return ReadFromCache(); +} + +nsresult +nsHttpChannel::OpenCacheEntry(PRBool *delayed) +{ + nsresult rv; + + *delayed = PR_FALSE; + + LOG(("nsHttpChannel::OpenCacheEntry [this=%x]", this)); + + // make sure we're not abusing this function + NS_PRECONDITION(!mCacheEntry, "cache entry already open"); + + nsCAutoString cacheKey; + + if (mRequestHead.Method() == nsHttp::Post) { + // If the post id is already set then this is an attempt to replay + // a post transaction via the cache. Otherwise, we need a unique + // post id for this transaction. + if (mPostID == 0) + mPostID = nsHttpHandler::get()->GenerateUniqueID(); + } + else if ((mRequestHead.Method() != nsHttp::Get) && + (mRequestHead.Method() != nsHttp::Head)) { + // don't use the cache for other types of requests + return NS_OK; + } + else if (mRequestHead.PeekHeader(nsHttp::Range)) { + // we don't support caching for byte range requests + return NS_OK; + } + + GenerateCacheKey(cacheKey); + + // Get a cache session with appropriate storage policy + nsCacheStoragePolicy storagePolicy; + if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) + storagePolicy = nsICache::STORE_IN_MEMORY; + else + storagePolicy = nsICache::STORE_ANYWHERE; // allow on disk + nsCOMPtr session; + rv = nsHttpHandler::get()->GetCacheSession(storagePolicy, + getter_AddRefs(session)); + if (NS_FAILED(rv)) return rv; + + // Are we offline? + PRBool offline = PR_FALSE; + nsCOMPtr ioService = do_GetIOService(); + if (ioService) + ioService->GetOffline(&offline); + + // Set the desired cache access mode accordingly... + nsCacheAccessMode accessRequested; + if (offline) { + // Since we are offline, we can only read from the cache. + accessRequested = nsICache::ACCESS_READ; + mFromCacheOnly = PR_TRUE; + } + else if (mLoadFlags & LOAD_BYPASS_CACHE) + accessRequested = nsICache::ACCESS_WRITE; // replace cache entry + else if (mFromCacheOnly) + accessRequested = nsICache::ACCESS_READ; // read from cache + else + accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing + + // we'll try to synchronously open the cache entry... however, it may be + // in use and not yet validated, in which case we'll try asynchronously + // opening the cache entry. + rv = session->OpenCacheEntry(cacheKey, accessRequested, PR_FALSE, + getter_AddRefs(mCacheEntry)); + if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) { + // access to the cache entry has been denied + rv = session->AsyncOpenCacheEntry(cacheKey, accessRequested, this); + if (NS_FAILED(rv)) return rv; + // we'll have to wait for the cache entry + *delayed = PR_TRUE; + } + else if (rv == NS_OK) { + mCacheEntry->GetAccessGranted(&mCacheAccess); + LOG(("got cache entry [access=%x]\n", mCacheAccess)); + } + return rv; +} + +nsresult +nsHttpChannel::GenerateCacheKey(nsACString &cacheKey) +{ + cacheKey.SetLength(0); + if (mPostID) { + char buf[32]; + PR_snprintf(buf, sizeof(buf), "%x", mPostID); + cacheKey.Append("id="); + cacheKey.Append(buf); + cacheKey.Append("&uri="); + } + // Strip any trailing #ref from the URL before using it as the key + const char *p = PL_strchr(mSpec, '#'); + if (p) + cacheKey.Append(mSpec, p - mSpec); + else + cacheKey.Append(mSpec); + return NS_OK; +} + +// UpdateExpirationTime is called when a new response comes in from the server. +// It updates the stored response-time and sets the expiration time on the +// cache entry. +// +// From section 13.2.4 of RFC2616, we compute expiration time as follows: +// +// timeRemaining = freshnessLifetime - currentAge +// expirationTime = now + timeRemaining +// +nsresult +nsHttpChannel::UpdateExpirationTime() +{ + nsresult rv; + PRUint32 now = NowInSeconds(); + PRUint32 freshnessLifetime, currentAge, timeRemaining = 0; + + NS_ENSURE_TRUE(mResponseHead, NS_ERROR_FAILURE); + + rv = mResponseHead->ComputeCurrentAge(now, mRequestTime, ¤tAge); + if (NS_FAILED(rv)) return rv; + + rv = mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime); + if (NS_FAILED(rv)) return rv; + + LOG(("freshnessLifetime = %u, currentAge = %u\n", + freshnessLifetime, currentAge)); + + if (freshnessLifetime > currentAge) + timeRemaining = freshnessLifetime - currentAge; + + return mCacheEntry->SetExpirationTime(now + timeRemaining); +} + +// CheckCache is called from Connect after a cache entry has been opened for +// this URL but before going out to net. It's purpose is to set or clear the +// mCachedContentIsValid flag, and to configure an If-Modified-Since request +// if validation is required. +nsresult +nsHttpChannel::CheckCache() +{ + nsresult rv = NS_OK; + + LOG(("nsHTTPChannel::CheckCache [this=%x entry=%x]", + this, mCacheEntry.get())); + + // Be pessimistic: assume the cache entry has no useful data. + mCachedContentIsValid = PR_FALSE; + + // Don't proceed unless we have opened a cache entry for reading. + if (!mCacheEntry || !(mCacheAccess & nsICache::ACCESS_READ)) + return NS_OK; + + // Get the cached HTTP response headers + nsXPIDLCString buf; + rv = mCacheEntry->GetMetaDataElement("http-headers", getter_Copies(buf)); + if (NS_FAILED(rv)) { + NS_WARNING("cache entry does not contain http headers!"); + return rv; + } + + // Parse the cached HTTP response headers + NS_ASSERTION(!mCachedResponseHead, "memory leak detected"); + mCachedResponseHead = new nsHttpResponseHead(); + if (!mCachedResponseHead) + return NS_ERROR_OUT_OF_MEMORY; + rv = mCachedResponseHead->Parse((char *) buf.get()); + if (NS_FAILED(rv)) return rv; + + // we're done with the header buf + buf = 0; + + // If we were only granted read access, then assume the entry is valid. + if (mCacheAccess == nsICache::ACCESS_READ) { + mCachedContentIsValid = PR_TRUE; + return NS_OK; + } + + // If the cached content-length is set and it does not match the data size + // of the cached content, then refetch. + PRInt32 contentLength = mCachedResponseHead->ContentLength(); + if (contentLength != -1) { + PRUint32 size; + rv = mCacheEntry->GetDataSize(&size); + if (NS_FAILED(rv)) return rv; + + if (size != (PRUint32) contentLength) { + LOG(("Cached data size does not match the Content-Length header " + "[content-length=%u size=%u]\n", contentLength, size)); + // looks like a partial entry. + // XXX must re-fetch until we learn how to do byte range requests. + return NS_OK; + } + } + + PRBool doValidation = PR_FALSE; + const char *val; + + // Be optimistic: assume that we won't need to do validation + mRequestHead.SetHeader(nsHttp::If_Modified_Since, nsnull); + mRequestHead.SetHeader(nsHttp::If_None_Match, nsnull); + + // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used. + if (mLoadFlags & LOAD_FROM_CACHE) { + LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n")); + doValidation = PR_FALSE; + goto end; + } + + // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until + // it's revalidated with the server. + if (mLoadFlags & VALIDATE_ALWAYS) { + LOG(("Validating based on VALIDATE_ALWAYS load flag\n")); + doValidation = PR_TRUE; + goto end; + } + + // If the must-revalidate directive is present in the cached response, data + // must always be revalidated with the server, even if the user has + // configured validation to be turned off (See RFC 2616, section 14.9.4). + val = mCachedResponseHead->PeekHeader(nsHttp::Cache_Control); + if (val && PL_strstr(val, "must-revalidate")) { + LOG(("Validating based on \"%s\" header\n", val)); + doValidation = PR_TRUE; + goto end; + } + // The no-cache directive within the 'Cache-Control:' header indicates + // that we must validate this cached response before reusing. + if (val && PL_strstr(val, "no-cache")) { + LOG(("Validating based on \"%s\" header\n", val)); + doValidation = PR_TRUE; + goto end; + } + // XXX we are not quite handling no-cache correctly in this case. We really + // should check for field-names and only force validation if they match + // existing response headers. See RFC2616 section 14.9.1 for details. + + // Although 'Pragma:no-cache' is not a standard HTTP response header (it's + // a request header), caching is inhibited when this header is present so + // as to match existing Navigator behavior. + val = mCachedResponseHead->PeekHeader(nsHttp::Pragma); + if (val && PL_strstr(val, "no-cache")) { + LOG(("Validating based on \"%s\" header\n", val)); + doValidation = PR_TRUE; + goto end; + } + + // Check the Vary header. Per comments on bug 37609, most of the request + // headers that we generate do not vary with the exception of Accept-Charset + // and Accept-Language, so we force validation only if these headers or "*" + // are listed with the Vary response header. + // + // XXX this may not be sufficient if embedders start tweaking or adding HTTP + // request headers. + // + // XXX will need to add the Accept header to this list if we start sending + // a full Accept header, since the addition of plugins could change this + // header (see bug 58040). + val = mCachedResponseHead->PeekHeader(nsHttp::Vary); + if (val && (PL_strstr(val, "*") || + PL_strstr(val, "accept-charset") || + PL_strstr(val, "accept-language"))) { + LOG(("Validating based on \"%s\" header\n", val)); + doValidation = PR_TRUE; + goto end; + } + + // delay checking this flag until we've verified that the above flags are + // not set. + if (mLoadFlags & VALIDATE_NEVER) { + LOG(("Not validating based on VALIDATE_NEVER flag\n")); + doValidation = PR_FALSE; + goto end; + } + + // Check if the cache entry has expired... + { + PRUint32 time = 0; // a temporary variable for storing time values... + + rv = mCacheEntry->GetExpirationTime(&time); + if (NS_FAILED(rv)) return rv; + + if (NowInSeconds() <= time) + doValidation = PR_FALSE; + else if (mLoadFlags & VALIDATE_ONCE_PER_SESSION) { + // If the cached response does not include expiration infor- + // mation, then we must validate the response, despite whether + // or not this is the first access this session. This behavior + // is consistent with existing browsers and is generally expected + // by web authors. + rv = mCachedResponseHead->ComputeFreshnessLifetime(&time); + if (NS_FAILED(rv)) return rv; + + if (time == 0) { + doValidation = PR_TRUE; + } + else { + rv = mCacheEntry->GetLastModified(&time); + if (NS_FAILED(rv)) return rv; + // Determine if this is the first time that this cache entry + // has been accessed in this session, and validate if so. + doValidation = (nsHttpHandler::get()->SessionStartTime() > time); + } + + } + else + doValidation = PR_TRUE; + + LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v")); + } + +end: + mCachedContentIsValid = !doValidation; + + if (doValidation) { + // Add If-Modified-Since header if a Last-Modified was given + val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified); + if (!val) { + LOG(("No Last-Lodified header sent, using the Date header instead...\n")); + val = mCachedResponseHead->PeekHeader(nsHttp::Date); + } + if (val) + mRequestHead.SetHeader(nsHttp::If_Modified_Since, val); + + // Add If-None-Match header if an ETag was given in the response + val = mCachedResponseHead->PeekHeader(nsHttp::ETag); + if (val) + mRequestHead.SetHeader(nsHttp::If_None_Match, val); + } + + LOG(("CheckCache [this=%x doValidation=%d]\n", this, doValidation)); + return NS_OK; +} + +// If the data in the cache hasn't expired, then there's no need to +// talk with the server, not even to do an if-modified-since. This +// method creates a stream from the cache, synthesizing all the various +// channel-related events. +nsresult +nsHttpChannel::ReadFromCache() +{ + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE); + + LOG(("nsHttpChannel::ReadFromCache [this=%x] " + "Using cached copy of: %s\n", this, mSpec.get())); + + if (mCachedResponseHead) { + NS_ASSERTION(!mResponseHead, "memory leak"); + mResponseHead = mCachedResponseHead; + mCachedResponseHead = 0; + } + + // install stream converter if required + ApplyContentConversions(); + + if (mCacheAccess & nsICache::ACCESS_WRITE) { + // We have write access to the cache, but we don't need to go to the + // server to validate at this time, so just mark the cache entry as + // valid in order to allow others access to this cache entry. + mCacheEntry->MarkValid(); + } + + // Get a transport to the cached data... + nsresult rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport)); + if (NS_FAILED(rv)) return rv; + + // Pump the cache data downstream + return mCacheTransport->AsyncRead(this, mListenerContext, + 0, PRUint32(-1), 0, + getter_AddRefs(mCacheReadRequest)); +} + +nsresult +nsHttpChannel::CloseCacheEntry(nsresult status) +{ + nsresult rv = NS_OK; + if (mCacheEntry) { + LOG(("nsHttpChannel::CloseCacheEntry [this=%x status=%x]", this, status)); + + if (NS_FAILED(status) && (mCacheAccess & nsICache::ACCESS_WRITE)) { + LOG(("dooming cache entry!!")); + rv = mCacheEntry->Doom(); + } + + if (mCachedResponseHead) { + delete mCachedResponseHead; + mCachedResponseHead = 0; + } + + mCacheReadRequest = 0; + mCacheTransport = 0; + mCacheEntry = 0; + mCacheAccess = 0; + } + return rv; +} + +// Cache the network response from the server, including both the content and +// the HTTP headers. +nsresult +nsHttpChannel::CacheReceivedResponse() +{ + const char *val; + nsresult rv; + + NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED); + NS_ENSURE_TRUE(mCacheAccess & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED); + + // Don't cache the response again if already cached... + if (mCachedContentIsValid) + return NS_OK; + + LOG(("nsHttpChannel::CacheReceivedResponse [this=%x entry=%x]\n", + this, mCacheEntry.get())); + + // The no-store directive within the 'Cache-Control:' header indicates + // that we should not store the response in a persistent cache + val = mResponseHead->PeekHeader(nsHttp::Cache_Control); + if (val && PL_strstr(val, "no-store")) { + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; + LOG(("Inhibiting persistent caching because of \"%s\"\n", val)); + } + + // Store secure data in memory only + nsCOMPtr securityInfo; + GetSecurityInfo(getter_AddRefs(securityInfo)); + if (securityInfo) + mCacheEntry->SetSecurityInfo(securityInfo); + + // For HTTPS transactions, the storage policy will already be IN_MEMORY. + // We are concerned instead about load attributes which may have changed. + if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { + rv = mCacheEntry->SetStoragePolicy(nsICache::STORE_IN_MEMORY); + if (NS_FAILED(rv)) return rv; + } + + // Set the expiration time for this cache entry + rv = UpdateExpirationTime(); + if (NS_FAILED(rv)) return rv; + + // Store the received HTTP headers with the cache entry as an element of + // the meta data. + + nsCAutoString headers; + rv = mResponseHead->Flatten(headers, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + rv = mCacheEntry->SetMetaDataElement("http-headers", headers.get()); + if (NS_FAILED(rv)) return rv; + + // Open an output stream to the cache entry and insert a listener tee into + // the chain of response listeners. + + LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get())); + + rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr out; + rv = mCacheTransport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(out)); + if (NS_FAILED(rv)) return rv; + + // XXX disk cache does not support overlapped i/o yet +#if 0 + // Mark entry valid inorder to allow simultaneous reading... + rv = mCacheEntry->MarkValid(); + if (NS_FAILED(rv)) return rv; +#endif + + nsCOMPtr tee = + do_CreateInstance(kStreamListenerTeeCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = tee->Init(mListener, out); + if (NS_FAILED(rv)) return rv; + + mListener = do_QueryInterface(tee, &rv); + return rv; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +nsresult +nsHttpChannel::ProcessRedirection(PRUint32 redirectType) +{ + LOG(("nsHttpChannel::ProcessRedirection [this=%x type=%u]\n", + this, redirectType)); + + const char *location = mResponseHead->PeekHeader(nsHttp::Location); + + // if a location header was not given, then we can't perform the redirect, + // so just carry on as though this were a normal response. + if (!location) + return NS_ERROR_FAILURE; + + // notify nsIHttpNotify implementations before this channel goes away + nsHttpHandler::get()->OnExamineResponse(this); + + LOG(("redirecting to: %s\n", location)); + + nsresult rv; + nsCOMPtr newChannel; + + if (redirectType == 305) { + // we must repeat the request via the proxy specified by location + + PRInt32 proxyPort; + + // location is of the form "host:port" + char *p = PL_strchr(location, ':'); + if (p) { + *p = 0; + proxyPort = atoi(p+1); + } + else + proxyPort = 80; + + // talk to the http handler directly for this case + rv = nsHttpHandler::get()-> + NewProxyChannel(mURI, location, proxyPort, "http", + getter_AddRefs(newChannel)); + if (NS_FAILED(rv)) return rv; + } + else { + // + // this redirect could be to ANY uri, so we need to talk to the + // IO service to create the new channel. + // + nsCOMPtr serv = do_GetIOService(&rv); + if (NS_FAILED(rv)) return rv; + + // create a new URI using the location header and the current URL + // as a base... + nsCOMPtr newURI; + rv = serv->NewURI(location, mURI, getter_AddRefs(newURI)); + if (NS_FAILED(rv)) return rv; + + // move the reference of the old location to the new one if the new + // one has none. + nsCOMPtr newURL = do_QueryInterface(newURI, &rv); + if (NS_SUCCEEDED(rv)) { + nsXPIDLCString ref; + rv = newURL->GetRef(getter_Copies(ref)); + if (NS_SUCCEEDED(rv) && !ref) { + nsCOMPtr baseURL = do_QueryInterface(mURI, &rv); + if (NS_SUCCEEDED(rv)) { + baseURL->GetRef(getter_Copies(ref)); + if (ref) + newURL->SetRef(ref); + } + } + } + + // build the new channel + rv = NS_OpenURI(getter_AddRefs(newChannel), newURI, serv, mLoadGroup, + mCallbacks, mLoadFlags | LOAD_REPLACE); + if (NS_FAILED(rv)) return rv; + } + + // convey the original uri + rv = newChannel->SetOriginalURI(mOriginalURI); + if (NS_FAILED(rv)) return rv; + + // convey the referrer if one was used for this channel to the next one + if (mReferrer) { + nsCOMPtr httpChannel = do_QueryInterface(newChannel); + if (httpChannel) + httpChannel->SetReferrer(mReferrer, mReferrerType); + } + + // call out to the event sink to notify it of this redirection. + if (mHttpEventSink) { + rv = mHttpEventSink->OnRedirect(this, newChannel); + if (NS_FAILED(rv)) return rv; + } + // XXX we used to talk directly with the script security manager, but that + // should really be handled by the event sink implementation. + + // begin loading the new channel + rv = newChannel->AsyncOpen(mListener, mListenerContext); + if (NS_FAILED(rv)) return rv; + + // close down this channel + mTransaction->Cancel(NS_BINDING_REDIRECTED); + + // disconnect from our listener + mListener = 0; + mListenerContext = 0; + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +nsresult +nsHttpChannel::ProcessAuthentication(PRUint32 httpStatus) +{ + LOG(("nsHttpChannel::ProcessAuthentication [this=%x code=%u]\n", + this, httpStatus)); + + const char *challenge; + PRBool proxyAuth = (httpStatus == 407); + + if (proxyAuth) + challenge = mResponseHead->PeekHeader(nsHttp::Proxy_Authenticate); + else + challenge = mResponseHead->PeekHeader(nsHttp::WWW_Authenticate); + + if (!challenge) { + LOG(("null challenge!\n")); + return NS_ERROR_UNEXPECTED; + } + + LOG(("challenge=%s\n", challenge)); + + nsCAutoString creds; + nsresult rv = GetCredentials(challenge, proxyAuth, creds); + if (NS_FAILED(rv)) return rv; + + // set the authentication credentials + if (proxyAuth) + mRequestHead.SetHeader(nsHttp::Proxy_Authorization, creds); + else + mRequestHead.SetHeader(nsHttp::Authorization, creds); + + // kill off the current transaction + mTransaction->Cancel(NS_BINDING_REDIRECTED); + mPrevTransaction = mTransaction; + mTransaction = nsnull; + + // and create a new one... + rv = SetupTransaction(); + if (NS_FAILED(rv)) return rv; + + rv = nsHttpHandler::get()->InitiateTransaction(mTransaction, mConnectionInfo); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +nsresult +nsHttpChannel::GetCredentials(const char *challenges, + PRBool proxyAuth, + nsAFlatCString &creds) +{ + nsAutoString user, pass; + nsresult rv; + + LOG(("nsHttpChannel::GetCredentials [this=%x proxyAuth=%d challenges=%s]\n", + this, proxyAuth, challenges)); + + nsHttpAuthCache *authCache = nsHttpHandler::get()->AuthCache(); + if (!authCache) + return NS_ERROR_NOT_INITIALIZED; + + // proxy auth's never in prehost + if (!mTriedCredentialsFromPrehost && !proxyAuth) { + rv = GetUserPassFromURI(user, pass); + if (NS_FAILED(rv)) return rv; + mTriedCredentialsFromPrehost = PR_TRUE; + } + + // figure out which challenge we can handle and which authenticator to use. + nsCAutoString challenge; + nsCOMPtr auth; + + rv = SelectChallenge(challenges, challenge, getter_AddRefs(auth)); + + if (!auth) { + LOG(("authentication type not supported\n")); + return NS_ERROR_FAILURE; + } + + nsCAutoString realm; + rv = ParseRealm(challenge.get(), realm); + if (NS_FAILED(rv)) return rv; + + const char *triedCreds = nsnull; + const char *host; + nsXPIDLCString path; + PRInt32 port; + + if (proxyAuth) { + host = mConnectionInfo->ProxyHost(); + port = mConnectionInfo->ProxyPort(); + triedCreds = mRequestHead.PeekHeader(nsHttp::Proxy_Authorization); + } + else { + host = mConnectionInfo->Host(); + port = mConnectionInfo->Port(); + triedCreds = mRequestHead.PeekHeader(nsHttp::Authorization); + + rv = GetCurrentPath(getter_Copies(path)); + if (NS_FAILED(rv)) return rv; + } + + // + // if we already tried some credentials for this transaction, then + // we need to possibly clear them from the cache, unless the credentials + // in the cache have changed, in which case we'd want to give them a + // try instead. + // + authCache->GetCredentialsForDomain(host, port, realm.get(), creds); + + if (triedCreds && !PL_strcmp(triedCreds, creds.get())) { + // ok.. clear the credentials from the cache + authCache->SetCredentials(host, port, nsnull, realm.get(), nsnull); + creds.Truncate(0); + } + // otherwise, let's try the credentials we got from the cache + + if (!creds.IsEmpty()) { + LOG(("using cached credentials!\n")); + return NS_OK; + } + + if (user.IsEmpty()) { + // at this point we are forced to interact with the user to get their + // username and password for this domain. + rv = PromptForUserPass(host, port, proxyAuth, realm.get(), user, pass); + if (NS_FAILED(rv)) return rv; + } + + // talk to the authenticator to get credentials for this user/pass combo. + nsXPIDLCString result; + rv = auth->GenerateCredentials(this, + challenge.get(), + user.get(), + pass.get(), + getter_Copies(result)); + if (NS_FAILED(rv)) return rv; + + creds.Assign(result); + + // store these credentials in the cache. we do this even though we don't + // yet know that these credentials are valid b/c we need to avoid prompting + // the user more than once in case the credentials are valid. + return authCache->SetCredentials(host, port, path, realm.get(), creds.get()); +} + +nsresult +nsHttpChannel::SelectChallenge(const char *challenges, + nsAFlatCString &challenge, + nsIHttpAuthenticator **auth) +{ + nsCAutoString scheme; + + LOG(("nsHttpChannel::SelectChallenge [this=%x]\n", this)); + + // loop over the various challenges (LF separated)... + for (const char *eol = challenges - 1; eol; ) { + const char *p = eol + 1; + + // get the challenge string + if ((eol = PL_strchr(p, '\n')) != nsnull) + challenge.Assign(p, eol - p); + else + challenge.Assign(p); + + // get the challenge type + if ((p = PL_strchr(challenge.get(), ' ')) != nsnull) + scheme.Assign(challenge.get(), p - challenge.get()); + else + scheme.Assign(challenge); + + // normalize to lowercase + ToLowerCase(scheme); + + if (NS_SUCCEEDED(GetAuthenticator(scheme.get(), auth))) + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +nsresult +nsHttpChannel::GetAuthenticator(const char *scheme, nsIHttpAuthenticator **auth) +{ + LOG(("nsHttpChannel::GetAuthenticator [this=%x scheme=%s]\n", this, scheme)); + + nsCAutoString contractid; + contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX); + contractid.Append(scheme); + + nsresult rv; + nsCOMPtr serv = do_GetService(contractid, &rv); + if (NS_FAILED(rv)) return rv; + + *auth = serv; + NS_ADDREF(*auth); + return NS_OK; +} + +nsresult +nsHttpChannel::GetUserPassFromURI(nsAString &user, + nsAString &pass) +{ + // XXX should be a necko utility function + nsXPIDLCString prehost; + mURI->GetPreHost(getter_Copies(prehost)); + if (prehost) { + nsresult rv; + + nsCOMPtr serv = do_GetIOService(&rv); + if (NS_FAILED(rv)) return rv; + + nsXPIDLCString buf; + rv = serv->Unescape(prehost, getter_Copies(buf)); + if (NS_FAILED(rv)) return rv; + + char *p = PL_strchr(buf, ':'); + if (p) { + // user:pass + *p = 0; + user = NS_ConvertASCIItoUCS2(buf); + pass = NS_ConvertASCIItoUCS2(p+1); + } + else { + // user + user = NS_ConvertASCIItoUCS2(buf); + } + } + return NS_OK; +} + +nsresult +nsHttpChannel::ParseRealm(const char *challenge, nsACString &realm) +{ + // + // From RFC2617 section 1.2, the realm value is defined as such: + // + // realm = "realm" "=" realm-value + // realm-value = quoted-string + // + // but, we'll accept anything after the the "=" up to the first space, or + // end-of-line, if the string is not quoted. + // + const char *p = PL_strstr(challenge, "realm="); + if (p) { + p += 6; + if (*p == '"') + p++; + const char *end = PL_strchr(p, '"'); + if (!end) + end = PL_strchr(p, ' '); + if (end) + realm.Assign(p, end - p); + else + realm.Assign(p); + } + return NS_OK; +} + +nsresult +nsHttpChannel::PromptForUserPass(const char *host, + PRInt32 port, + PRBool proxyAuth, + const char *realm, + nsAString &user, + nsAString &pass) +{ + LOG(("nsHttpChannel::PromptForUserPass [this=%x realm=%s]\n", this, realm)); + + nsresult rv; + nsCOMPtr authPrompt = do_GetInterface(mCallbacks, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("notification callbacks should provide nsIAuthPrompt"); + return rv; + } + + // construct the domain string + nsCAutoString domain; + domain.Assign(host); + domain.Append(':'); + domain.AppendInt(port); + + nsAutoString hostU = NS_ConvertASCIItoUCS2(domain); + + domain.Append(" ("); + domain.Append(realm); + domain.Append(')'); + + // construct the message string + nsCOMPtr bundleSvc = + do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr bundle; + rv = bundleSvc->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); + if (NS_FAILED(rv)) return rv; + + // figure out what message to display... + nsXPIDLString message; + if (proxyAuth) { + const PRUnichar *strings[] = { hostU.GetUnicode() }; + rv = bundle->FormatStringFromName( + NS_LITERAL_STRING("EnterUserPasswordForProxy").get(), + strings, 1, + getter_Copies(message)); + } + else { + nsAutoString realmU; + realmU.Assign(NS_LITERAL_STRING("\"")); + realmU.AppendWithConversion(realm); + realmU.Append(NS_LITERAL_STRING("\"")); + + const PRUnichar *strings[] = { realmU.GetUnicode(), hostU.GetUnicode() }; + rv = bundle->FormatStringFromName( + NS_LITERAL_STRING("EnterUserPasswordForRealm").get(), + strings, 2, + getter_Copies(message)); + } + if (NS_FAILED(rv)) return rv; + + // prompt the user... + nsXPIDLString userBuf, passBuf; + PRBool retval = PR_FALSE; + rv = authPrompt->PromptUsernameAndPassword(nsnull, + message.get(), + NS_ConvertASCIItoUCS2(domain).get(), + nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, + getter_Copies(userBuf), + getter_Copies(passBuf), + &retval); + if (NS_FAILED(rv)) + return rv; + if (!retval) + return NS_ERROR_ABORT; + + user.Assign(userBuf); + pass.Assign(passBuf); + return NS_OK; +} + +nsresult +nsHttpChannel::AddAuthorizationHeaders() +{ + LOG(("nsHttpChannel::AddAuthorizationHeaders [this=%x]\n", this)); + nsHttpAuthCache *authCache = nsHttpHandler::get()->AuthCache(); + if (authCache) { + nsCAutoString creds; + nsCAutoString realm; + nsresult rv; + + // check if proxy credentials should be sent + const char *proxyHost = mConnectionInfo->ProxyHost(); + const char *proxyType = mConnectionInfo->ProxyType(); + if (proxyHost && !PL_strcmp(proxyType, "http")) { + rv = authCache->GetCredentialsForPath(proxyHost, + mConnectionInfo->ProxyPort(), + nsnull, realm, creds); + if (NS_SUCCEEDED(rv)) { + LOG(("adding Proxy_Authorization [creds=%s]\n", creds.get())); + mRequestHead.SetHeader(nsHttp::Proxy_Authorization, creds.get()); + } + } + + // check if server credentials should be sent + nsXPIDLCString path; + rv = GetCurrentPath(getter_Copies(path)); + if (NS_FAILED(rv)) return rv; + + rv = authCache->GetCredentialsForPath(mConnectionInfo->Host(), + mConnectionInfo->Port(), + path.get(), + realm, + creds); + if (NS_SUCCEEDED(rv)) { + LOG(("adding Authorization [creds=%s]\n", creds.get())); + mRequestHead.SetHeader(nsHttp::Authorization, creds.get()); + } + } + return NS_OK; +} + +nsresult +nsHttpChannel::GetCurrentPath(char **path) +{ + nsresult rv; + nsCOMPtr url = do_QueryInterface(mURI); + if (url) + rv = url->GetDirectory(path); + else + rv = mURI->GetPath(path); + return rv; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ISUPPORTS9(nsHttpChannel, + nsIRequest, + nsIChannel, + nsIRequestObserver, + nsIStreamListener, + nsIHttpChannel, + nsIInterfaceRequestor, + nsIProgressEventSink, + nsICachingChannel, + nsICacheListener) + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIRequest +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::GetName(PRUnichar **aName) +{ + NS_ENSURE_ARG_POINTER(aName); + *aName = ToNewUnicode(NS_ConvertASCIItoUCS2(mSpec)); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::IsPending(PRBool *value) +{ + NS_ENSURE_ARG_POINTER(value); + *value = mIsPending; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetStatus(nsresult *aStatus) +{ + NS_ENSURE_ARG_POINTER(aStatus); + *aStatus = mStatus; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::Cancel(nsresult status) +{ + LOG(("nsHttpChannel::Cancel [this=%x status=%x]\n", this, status)); + if (mTransaction) + mTransaction->Cancel(status); + else if (mCacheReadRequest) + mCacheReadRequest->Cancel(status); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::Suspend() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::Resume() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) +{ + NS_ENSURE_ARG_POINTER(aLoadGroup); + *aLoadGroup = mLoadGroup; + NS_IF_ADDREF(*aLoadGroup); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) +{ + mLoadGroup = aLoadGroup; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) +{ + NS_ENSURE_ARG_POINTER(aLoadFlags); + *aLoadFlags = mLoadFlags; + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetLoadFlags(nsLoadFlags aLoadFlags) +{ + mLoadFlags = aLoadFlags; + + // don't let anyone overwrite this bit if we're using a secure channel. + if (mConnectionInfo && mConnectionInfo->UsingSSL()) + mLoadFlags |= INHIBIT_PERSISTENT_CACHING; + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::GetOriginalURI(nsIURI **originalURI) +{ + NS_ENSURE_ARG_POINTER(originalURI); + *originalURI = mOriginalURI; + NS_IF_ADDREF(*originalURI); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetOriginalURI(nsIURI *originalURI) +{ + mOriginalURI = originalURI; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetURI(nsIURI **URI) +{ + NS_ENSURE_ARG_POINTER(URI); + *URI = mURI; + NS_IF_ADDREF(*URI); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetOwner(nsISupports **owner) +{ + NS_ENSURE_ARG_POINTER(owner); + *owner = mOwner; + NS_IF_ADDREF(*owner); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetOwner(nsISupports *owner) +{ + mOwner = owner; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **callbacks) +{ + NS_ENSURE_ARG_POINTER(callbacks); + *callbacks = mCallbacks; + NS_IF_ADDREF(*callbacks); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *callbacks) +{ + mCallbacks = callbacks; + + mHttpEventSink = do_GetInterface(mCallbacks); + mProgressSink = do_GetInterface(mCallbacks); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo) +{ + NS_ENSURE_ARG_POINTER(securityInfo); + *securityInfo = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetContentType(char **value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + return DupString(mResponseHead->ContentType(), value); +} +NS_IMETHODIMP +nsHttpChannel::SetContentType(const char *value) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::GetContentLength(PRInt32 *value) +{ + NS_ENSURE_ARG_POINTER(value); + + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + + *value = mResponseHead->ContentLength(); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetContentLength(PRInt32 value) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::Open(nsIInputStream **_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) +{ + LOG(("nsHttpChannel::AsyncOpen [this=%x]\n", this)); + + NS_ENSURE_ARG_POINTER(listener); + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + mIsPending = PR_TRUE; + + mListener = listener; + mListenerContext = context; + + // add ourselves to the load group. from this point forward, we'll report + // all failures asynchronously. + if (mLoadGroup) + mLoadGroup->AddRequest(this, nsnull); + + nsresult rv = Connect(); + if (NS_FAILED(rv)) { + LOG(("Connect failed [rv=%x]\n", rv)); + + // make sure cache entry + CloseCacheEntry(rv); + + AsyncAbort(rv); + + mListener = 0; + mListenerContext = 0; + } + return NS_OK; +} +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIHttpChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::GetRequestMethod(char **method) +{ + return DupString(mRequestHead.Method().get(), method); +} +NS_IMETHODIMP +nsHttpChannel::SetRequestMethod(const char *method) +{ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + nsHttpAtom atom = nsHttp::ResolveAtom(method); + if (!atom) + return NS_ERROR_FAILURE; + + mRequestHead.SetMethod(atom); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetReferrer(nsIURI **referrer) +{ + NS_ENSURE_ARG_POINTER(referrer); + *referrer = mReferrer; + NS_IF_ADDREF(*referrer); + return NS_OK; +} +NS_IMETHODIMP +nsHttpChannel::SetReferrer(nsIURI *referrer, PRUint32 referrerType) +{ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + if (nsHttpHandler::get()->ReferrerLevel() < referrerType) + return NS_OK; + + // save a copy of the referrer so we can return it if requested + mReferrer = referrer; + + // save a copy of the referrer type for redirects + mReferrerType = referrerType; + + nsXPIDLCString spec; + referrer->GetSpec(getter_Copies(spec)); + if (spec) { + nsCAutoString ref(spec.get()); + // strip away any prehost; we don't want to be giving out passwords ;-) + nsXPIDLCString prehost; + referrer->GetPreHost(getter_Copies(prehost)); + if (prehost && *prehost) { + PRUint32 prehostLoc = PRUint32(ref.Find(prehost, PR_TRUE)); + ref.Cut(prehostLoc, nsCharTraits::length(prehost) + 1); // + 1 for @ + } + mRequestHead.SetHeader(nsHttp::Referer, ref); + } + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetRequestHeader(const char *header, char **value) +{ + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) + return NS_ERROR_NOT_AVAILABLE; + + return mRequestHead.GetHeader(atom, value); +} + +NS_IMETHODIMP +nsHttpChannel::SetRequestHeader(const char *header, const char *value) +{ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + LOG(("nsHttpChannel::SetRequestHeader [this=%x header=%s value=%s]\n", + this, header, value)); + + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) { + NS_WARNING("failed to resolve atom"); + return NS_ERROR_NOT_AVAILABLE; + } + + return mRequestHead.SetHeader(atom, value); +} + +NS_IMETHODIMP +nsHttpChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) +{ + return mRequestHead.Headers().VisitHeaders(visitor); +} + +NS_IMETHODIMP +nsHttpChannel::GetUploadStream(nsIInputStream **stream) +{ + NS_ENSURE_ARG_POINTER(stream); + *stream = mUploadStream; + NS_IF_ADDREF(*stream); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::SetUploadStream(nsIInputStream *stream) +{ + mUploadStream = stream; + if (mUploadStream) + mRequestHead.SetMethod(nsHttp::Post); + else + mRequestHead.SetMethod(nsHttp::Get); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetResponseStatus(PRUint32 *value) +{ + NS_ENSURE_ARG_POINTER(value); + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + *value = mResponseHead->Status(); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetResponseStatusText(char **value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + return DupString(mResponseHead->StatusText(), value); +} + +NS_IMETHODIMP +nsHttpChannel::GetResponseHeader(const char *header, char **value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) + return NS_ERROR_NOT_AVAILABLE; + return mResponseHead->GetHeader(atom, value); +} + +NS_IMETHODIMP +nsHttpChannel::SetResponseHeader(const char *header, const char *value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + nsHttpAtom atom = nsHttp::ResolveAtom(header); + if (!atom) + return NS_ERROR_NOT_AVAILABLE; + return mResponseHead->SetHeader(atom, value); +} + +NS_IMETHODIMP +nsHttpChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + return mResponseHead->Headers().VisitHeaders(visitor); +} + +NS_IMETHODIMP +nsHttpChannel::GetCharset(char **value) +{ + if (!mResponseHead) + return NS_ERROR_NOT_AVAILABLE; + return DupString(mResponseHead->ContentCharset(), value); +} + +NS_IMETHODIMP +nsHttpChannel::GetApplyConversion(PRBool *value) +{ + NS_ENSURE_ARG_POINTER(value); + *value = mApplyConversion; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::SetApplyConversion(PRBool value) +{ + mApplyConversion = value; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIRequestObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt) +{ + // if the request is for something we no longer reference, then simply + // drop this event. + if ((request != mTransaction) && (request != mCacheReadRequest)) + return NS_OK; + + LOG(("nsHttpChannel::OnStartRequest [this=%x request=%x]\n", this, request)); + + if (mTransaction) { + // all of the response headers have been acquired, so we can take ownership + // of them from the transaction. + mResponseHead = mTransaction->TakeResponseHead(); + // the response head may be null if the transaction was cancelled. in + // which case we just need to call OnStartRequest/OnStopRequest. + if (mResponseHead) + return ProcessResponse(); + } + + // there won't be a response head if we've been cancelled + return mListener->OnStartRequest(this, mListenerContext); +} + +NS_IMETHODIMP +nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) +{ + LOG(("nsHttpChannel::OnStopRequest [this=%x request=%x status=%x]\n", + this, request, status)); + + // if the request is a previous transaction, then simply release it. + if (request == mPrevTransaction) { + NS_RELEASE(mPrevTransaction); + mPrevTransaction = nsnull; + } + + // if the request is for something we no longer reference, then simply + // drop this event. + if ((request != mTransaction) && (request != mCacheReadRequest)) + return NS_OK; + + mIsPending = PR_FALSE; + mStatus = status; + + // at this point, we're done with the transaction + if (mTransaction) { + NS_RELEASE(mTransaction); + mTransaction = nsnull; + } + + if (mCacheEntry && NS_SUCCEEDED(status)) + mCacheEntry->MarkValid(); + + if (mListener) { + mListener->OnStopRequest(this, mListenerContext, status); + mListener = 0; + mListenerContext = 0; + } + + if (mCacheEntry) + CloseCacheEntry(status); + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nsnull, status); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, + nsIInputStream *input, + PRUint32 offset, PRUint32 count) +{ + // if the request is for something we no longer reference, then simply + // drop this event. + if ((request != mTransaction) && (request != mCacheReadRequest)) { + NS_WARNING("got stale request... why wasn't it cancelled?"); + return NS_BASE_STREAM_CLOSED; + } + + LOG(("nsHttpChannel::OnDataAvailable [this=%x offset=%u count=%u]\n", + this, offset, count)); + + if (mListener) + return mListener->OnDataAvailable(this, mListenerContext, input, offset, count); + + return NS_BASE_STREAM_CLOSED; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::GetInterface(const nsIID &iid, void **result) +{ + if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) { + // + // we return ourselves as the progress event sink so we can intercept + // notifications and set the correct request and context parameters. + // but, if we don't have a progress sink to forward those messages + // to, then there's no point in handing out a reference to ourselves. + // + if (!mProgressSink) + return NS_ERROR_NO_INTERFACE; + + return QueryInterface(iid, result); + } + + if (mCallbacks) + return mCallbacks->GetInterface(iid, result); + + return NS_ERROR_NO_INTERFACE; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsIProgressEventSink +//----------------------------------------------------------------------------- + +// called on the socket thread +NS_IMETHODIMP +nsHttpChannel::OnStatus(nsIRequest *req, nsISupports *ctx, nsresult status, + const PRUnichar *statusText) +{ + if (mProgressSink) + mProgressSink->OnStatus(this, mListenerContext, status, statusText); + + return NS_OK; +} + +// called on the socket thread +NS_IMETHODIMP +nsHttpChannel::OnProgress(nsIRequest *req, nsISupports *ctx, + PRUint32 progress, PRUint32 progressMax) +{ + if (mProgressSink) + mProgressSink->OnProgress(this, mListenerContext, progress, progressMax); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsICachingChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::GetCacheToken(nsISupports **token) +{ + NS_ENSURE_ARG_POINTER(token); + if (!mCacheEntry) + return NS_ERROR_NOT_AVAILABLE; + return CallQueryInterface(mCacheEntry, token); +} + +NS_IMETHODIMP +nsHttpChannel::SetCacheToken(nsISupports *token) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpChannel::GetCacheKey(nsISupports **key) +{ + nsresult rv; + NS_ENSURE_ARG_POINTER(key); + + LOG(("nsHttpChannel::GetCacheKey [this=%x]\n", this)); + + *key = nsnull; + + nsCOMPtr container = + do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = container->SetData(mPostID); + if (NS_FAILED(rv)) return rv; + + return CallQueryInterface(container, key); +} + +NS_IMETHODIMP +nsHttpChannel::SetCacheKey(nsISupports *key, PRBool fromCacheOnly) +{ + nsresult rv; + + LOG(("nsHttpChannel::SetCacheKey [this=%x key=%x fromCacheOnly=%d]\n", + this, key, fromCacheOnly)); + + // can only set the cache key if a load is not in progress + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + + if (!key) + mPostID = 0; + else { + // extract the post id + nsCOMPtr container = do_QueryInterface(key, &rv); + if (NS_FAILED(rv)) return rv; + + rv = container->GetData(&mPostID); + if (NS_FAILED(rv)) return rv; + } + + mFromCacheOnly = fromCacheOnly; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::GetCacheAsFile(PRBool *value) +{ + NS_ENSURE_ARG_POINTER(value); + if (!mCacheEntry) + return NS_ERROR_NOT_AVAILABLE; + nsCacheStoragePolicy storagePolicy; + mCacheEntry->GetStoragePolicy(&storagePolicy); + *value = (storagePolicy == nsICache::STORE_ON_DISK_AS_FILE); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::SetCacheAsFile(PRBool value) +{ + if (!mCacheEntry) + return NS_ERROR_NOT_AVAILABLE; + nsCacheStoragePolicy policy; + if (value) + policy = nsICache::STORE_ON_DISK_AS_FILE; + else + policy = nsICache::STORE_ANYWHERE; + return mCacheEntry->SetStoragePolicy(policy); +} + +NS_IMETHODIMP +nsHttpChannel::GetCacheFile(nsIFile **cacheFile) +{ + if (!mCacheEntry) + return NS_ERROR_NOT_AVAILABLE; + return mCacheEntry->GetFile(cacheFile); +} + +//----------------------------------------------------------------------------- +// nsHttpChannel::nsICacheListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, + nsCacheAccessMode access, + nsresult status) +{ + LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%x entry=%x " + "access=%x status=%x]\n", this, entry, access, status)); + + // if the channel's already fired onStopRequest, then we should ignore + // this event. + if (!mIsPending) + return NS_OK; + + // otherwise, we have to handle this event. + if (NS_SUCCEEDED(status)) { + mCacheEntry = entry; + mCacheAccess = access; + } + + // advance to the next state... + nsresult rv = Connect(PR_FALSE); + + // a failure from Connect means that we have to abort the channel. + if (NS_FAILED(rv)) { + CloseCacheEntry(rv); + AsyncAbort(rv); + } + + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.h b/mozilla/netwerk/protocol/http/src/nsHttpChannel.h new file mode 100644 index 00000000000..0dfa56d5a98 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.h @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpChannel_h__ +#define nsHttpChannel_h__ + +#include "nsHttpRequestHead.h" +#include "nsIHttpChannel.h" +#include "nsIHttpEventSink.h" +#include "nsIStreamListener.h" +#include "nsIURI.h" +#include "nsILoadGroup.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInputStream.h" +#include "nsIProgressEventSink.h" +#include "nsICachingChannel.h" +#include "nsICacheEntryDescriptor.h" +#include "nsICacheListener.h" +#include "nsITransport.h" +#include "nsCOMPtr.h" +#include "nsXPIDLString.h" + +class nsHttpTransaction; +class nsHttpConnectionInfo; +class nsHttpResponseHead; +class nsIHttpAuthenticator; + +//----------------------------------------------------------------------------- +// nsHttpChannel +//----------------------------------------------------------------------------- + +class nsHttpChannel : public nsIHttpChannel + , public nsIStreamListener + , public nsIInterfaceRequestor + , public nsIProgressEventSink + , public nsICachingChannel + , public nsICacheListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUEST + NS_DECL_NSICHANNEL + NS_DECL_NSIHTTPCHANNEL + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIPROGRESSEVENTSINK + NS_DECL_NSICACHINGCHANNEL + NS_DECL_NSICACHELISTENER + + nsHttpChannel(); + virtual ~nsHttpChannel(); + + nsresult Init(nsIURI *uri, + PRUint32 capabilities, + const char *proxyHost=0, + PRInt32 proxyPort=-1, + const char *proxyType=0); + +private: + nsresult Connect(PRBool firstTime = PR_TRUE); + nsresult AsyncAbort(nsresult status); + nsresult SetupTransaction(); + nsresult ApplyContentConversions(); + nsresult ProcessResponse(); + nsresult ProcessNormal(); + nsresult ProcessNotModified(); + nsresult ProcessRedirection(PRUint32 httpStatus); + nsresult ProcessAuthentication(PRUint32 httpStatus); + + // cache specific methods + nsresult OpenCacheEntry(PRBool *delayed); + nsresult GenerateCacheKey(nsACString &key); + nsresult UpdateExpirationTime(); + nsresult CheckCache(); + nsresult ReadFromCache(); + nsresult CloseCacheEntry(nsresult status); + nsresult CacheReceivedResponse(); + + // auth specific methods + nsresult GetCredentials(const char *challenges, PRBool proxyAuth, nsAFlatCString &creds); + nsresult SelectChallenge(const char *challenges, nsAFlatCString &challenge, nsIHttpAuthenticator **); + nsresult GetAuthenticator(const char *scheme, nsIHttpAuthenticator **); + nsresult GetUserPassFromURI(nsAString &user, nsAString &pass); + nsresult ParseRealm(const char *challenge, nsACString &realm); + nsresult PromptForUserPass(const char *host, PRInt32 port, PRBool proxyAuth, const char *realm, nsAString &user, nsAString &pass); + nsresult AddAuthorizationHeaders(); + nsresult GetCurrentPath(char **); + +private: + nsCOMPtr mOriginalURI; + nsCOMPtr mURI; + nsCOMPtr mListener; + nsCOMPtr mListenerContext; + nsCOMPtr mLoadGroup; + nsCOMPtr mOwner; + nsCOMPtr mCallbacks; + nsCOMPtr mProgressSink; + nsCOMPtr mHttpEventSink; + nsCOMPtr mUploadStream; + nsCOMPtr mReferrer; + + nsHttpRequestHead mRequestHead; + nsHttpResponseHead *mResponseHead; + + nsHttpTransaction *mTransaction; // hard ref + nsHttpTransaction *mPrevTransaction; // hard ref + nsHttpConnectionInfo *mConnectionInfo; // hard ref + + nsXPIDLCString mSpec; + + PRUint32 mLoadFlags; + PRUint32 mCapabilities; + PRUint32 mStatus; + PRUint32 mReferrerType; + + // cache specific data + nsCOMPtr mCacheEntry; + nsCOMPtr mCacheTransport; + nsCOMPtr mCacheReadRequest; + nsHttpResponseHead *mCachedResponseHead; + nsCacheAccessMode mCacheAccess; + PRUint32 mPostID; + PRUint32 mRequestTime; + + PRPackedBool mIsPending; + PRPackedBool mApplyConversion; + PRPackedBool mTriedCredentialsFromPrehost; + PRPackedBool mFromCacheOnly; + PRPackedBool mCachedContentIsValid; +}; + +#endif diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.cpp new file mode 100644 index 00000000000..76aca9deb7c --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.cpp @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpChunkedDecoder.h" +#include "nsHttp.h" + +//----------------------------------------------------------------------------- +// nsHttpChunkedDecoder +//----------------------------------------------------------------------------- + +nsresult +nsHttpChunkedDecoder::HandleChunkedContent(char *buf, + PRUint32 count, + PRUint32 *countRead) +{ + LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count)); + + *countRead = 0; + + while (count) { + if (mChunkRemaining) { + PRUint32 amt = PR_MIN(mChunkRemaining, count); + + count -= amt; + mChunkRemaining -= amt; + + *countRead += amt; + buf += amt; + } + + if (count) { + PRUint32 bytesConsumed = 0; + + nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed); + if (NS_FAILED(rv)) return rv; + + count -= bytesConsumed; + + if (count) { + // shift buf by bytesConsumed + memmove(buf, buf + bytesConsumed, count); + } + } + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpChunkedDecoder +//----------------------------------------------------------------------------- + +nsresult +nsHttpChunkedDecoder::ParseChunkRemaining(char *buf, + PRUint32 count, + PRUint32 *countRead) +{ + NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero"); + + char *p; + + // we may have a partial line from the last time we were called + if (mLineBuf.IsEmpty()) { + p = PL_strstr(buf, "\r\n"); + if (p) { + *p = 0; + p = buf; + } + } + else { + p = PL_strstr(buf, "\r\n"); + // it would be really sad if this ever returned null, b/c it + // would mean we were reading really small chunks from the net. + if (p) { + *p = 0; + // append to the line buf and use that as the buf to parse. + mLineBuf.Append(buf); + p = (char *) mLineBuf.get(); + } + } + + if (!p) { + mLineBuf.Append(buf, count); + *countRead = count; + return NS_OK; + } + + // indicate that the entire line has been consumed... the \r has been + // nulled out, so we have to add 2 to include the \r\n in the count read. + *countRead = PL_strlen(buf) + 2; + + buf = p; + if ((p = PL_strchr(buf, ';')) != nsnull) { + // ignore any chunk-extensions + *p = 0; + } + + if (!sscanf(buf, "%x", &mChunkRemaining)) { + LOG(("sscanf failed parsing hex on string [%s]\n", buf)); + return NS_ERROR_UNEXPECTED; + } + + if ((mChunkRemaining == 0) && (*buf != 0)) { + // XXX need to add code to consume "trailer" headers + mReachedEOF = PR_TRUE; + } + + // clear the line buffer if it is not already empty + if (!mLineBuf.IsEmpty()) + mLineBuf.SetLength(0); + + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.h b/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.h new file mode 100644 index 00000000000..c07bbfcf423 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpChunkedDecoder.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpChunkedDecoder_h__ +#define nsHttpChunkedDecoder_h__ + +#include "nsError.h" +#include "nsString.h" + +class nsHttpChunkedDecoder +{ +public: + nsHttpChunkedDecoder() : mChunkRemaining(0), mReachedEOF(0) {} + ~nsHttpChunkedDecoder() {} + + PRBool ReachedEOF() { return mReachedEOF; } + + // Called by the transaction to handle chunked content. + nsresult HandleChunkedContent(char *buf, + PRUint32 count, + PRUint32 *countRead); +private: + nsresult ParseChunkRemaining(char *buf, + PRUint32 count, + PRUint32 *countRead); + +private: + PRUint32 mChunkRemaining; + nsCString mLineBuf; // may hold a partial line + PRPackedBool mReachedEOF; +}; + +#endif diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp new file mode 100644 index 00000000000..410dc37b5ec --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp @@ -0,0 +1,642 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpConnection.h" +#include "nsHttpTransaction.h" +#include "nsHttpRequestHead.h" +#include "nsHttpResponseHead.h" +#include "nsHttpHandler.h" +#include "nsISocketTransportService.h" +#include "nsISocketTransport.h" +#include "nsIServiceManager.h" +#include "nsISSLSocketControl.h" +#include "nsIStringStream.h" +#include "netCore.h" +#include "nsNetCID.h" +#include "prmem.h" +#include "plevent.h" + +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); + +//----------------------------------------------------------------------------- +// helpers... +//----------------------------------------------------------------------------- + +static void *PR_CALLBACK +TransactionReleaseEventHandler(PLEvent *ev) +{ + nsHttpTransaction *trans = (nsHttpTransaction *) PL_GetEventOwner(ev); + + LOG(("TransactionReleaseEventHandler [trans=%x refcnt=%u] calling release...\n", + trans, trans->RefCnt())); + + NS_RELEASE(trans); + return 0; +} + +static void PR_CALLBACK +TransactionReleaseDestroyHandler(PLEvent *ev) +{ + delete ev; +} + +//----------------------------------------------------------------------------- +// nsHttpConnection +//----------------------------------------------------------------------------- + +nsHttpConnection::nsHttpConnection() + : mTransaction(0) + , mConnectionInfo(0) + , mReuseCount(0) + , mMaxReuseCount(0) + , mIdleTimeout(0) + , mLastActiveTime(0) + , mKeepAlive(0) +{ + LOG(("Creating nsHttpConnection @%x\n", this)); + + NS_INIT_ISUPPORTS(); +} + +nsHttpConnection::~nsHttpConnection() +{ + LOG(("Destroying nsHttpConnection @%x\n", this)); + + NS_IF_RELEASE(mConnectionInfo); + mConnectionInfo = 0; + + if (mTransaction) { + ProxyReleaseTransaction(mTransaction); + mTransaction = 0; + } + + // warning: this call could result in OnStopRequest being called. this + // is why we are careful to null out mTransaction and mConnectionInfo ;-) + if (mSocketTransport) + mSocketTransport->SetReuseConnection(PR_FALSE); +} + +nsresult +nsHttpConnection::Init(nsHttpConnectionInfo *info) +{ + LOG(("nsHttpConnection::Init [this=%x]\n")); + + NS_ENSURE_ARG_POINTER(info); + NS_ENSURE_TRUE(!mConnectionInfo, NS_ERROR_ALREADY_INITIALIZED); + + mConnectionInfo = info; + NS_ADDREF(mConnectionInfo); + + return NS_OK; +} + +// never called from the socket thread +nsresult +nsHttpConnection::SetTransaction(nsHttpTransaction *transaction) +{ + LOG(("nsHttpConnection::SetTransaction [this=%x]\n")); + + NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS); + NS_ENSURE_ARG_POINTER(transaction); + + // take ownership of the transaction + mTransaction = transaction; + NS_ADDREF(mTransaction); + + // grab a reference to the calling thread's event queue. + mEventQ = 0; + nsCOMPtr eqs; + nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs)); + if (eqs) + eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(mEventQ)); + + // build a proxy for the progress event sink + mProgressSink = 0; + if (mTransaction->Callbacks()) { + nsCOMPtr temp = do_GetInterface(mTransaction->Callbacks()); + if (temp) { + nsCOMPtr mgr; + nsHttpHandler::get()->GetProxyObjectManager(getter_AddRefs(mgr)); + if (mgr) + mgr->GetProxyForObject(mEventQ, + NS_GET_IID(nsIProgressEventSink), + temp, + PROXY_ASYNC | PROXY_ALWAYS, + getter_AddRefs(mProgressSink)); + } + } + + // assign ourselves to the transaction + mTransaction->SetConnection(this); + + return ActivateConnection(); +} + +// called from the socket thread +nsresult +nsHttpConnection::OnHeadersAvailable(nsHttpTransaction *trans, PRBool *reset) +{ + LOG(("nsHttpConnection::OnHeadersAvailable [this=%x trans=%x]\n", + this, trans)); + + NS_ENSURE_ARG_POINTER(trans); + + // we won't change our keep-alive policy unless the server has explicitly + // told us to do so. + + if (!trans || !trans->ResponseHead()) { + LOG(("trans->ResponseHead() = %x\n", trans)); + return NS_OK; + } + + // inspect the connection headers for keep-alive info provided the + // transaction completed successfully. + const char *val = trans->ResponseHead()->PeekHeader(nsHttp::Connection); + if (!val) + val = trans->ResponseHead()->PeekHeader(nsHttp::Proxy_Connection); + if (val) { + // be pesimistic + mKeepAlive = PR_FALSE; + + if (PL_strcasecmp(val, "keep-alive") == 0) { + mKeepAlive = PR_TRUE; + + val = trans->ResponseHead()->PeekHeader(nsHttp::Keep_Alive); + + LOG(("val = [%s]\n", val)); + + const char *cp = PL_strstr(val, "max="); + if (cp) + mMaxReuseCount = (PRUint32) atoi(cp + 4); + else + mMaxReuseCount = 100; + + cp = PL_strstr(val, "timeout="); + if (cp) + mIdleTimeout = (PRUint32) atoi(cp + 8); + else + mIdleTimeout = 10; + + LOG(("Connection can be reused [this=%x max-reuse=%u " + "keep-alive-timeout=%u\n", this, mMaxReuseCount, mIdleTimeout)); + } + } + + // if we're doing an SSL proxy connect, then we need to check whether or not + // the connect was successful. if so, then we have to reset the transaction + // and step-up the socket connection to SSL. finally, we have to wake up the + // socket write request. + if (mSSLProxyConnectStream) { + mSSLProxyConnectStream = 0; + if (trans->ResponseHead()->Status() == 200) { + LOG(("SSL proxy CONNECT succeeded!\n")); + *reset = PR_TRUE; + ProxyStepUp(); + mWriteRequest->Resume(); + } + else { + LOG(("SSL proxy CONNECT failed!\n")); + // close out the write request + mWriteRequest->Cancel(NS_OK); + } + } + + return NS_OK; +} + +// called from any thread +nsresult +nsHttpConnection::OnTransactionComplete(nsresult status) +{ + LOG(("nsHttpConnection::OnTransactionComplete [this=%x status=%x]\n", + this, status)); + + NS_ENSURE_TRUE(mSocketTransport, NS_ERROR_UNEXPECTED); + + // be warned: trans may not be mTransaction + + // cancel the requests... this will cause OnStopRequest to be fired + if (mWriteRequest) { + mWriteRequest->Cancel(status); + mWriteRequest = 0; + } + if (mReadRequest) { + mReadRequest->Cancel(status); + mReadRequest = 0; + } + + // break the cycle between the socket transport and this + if (mSocketTransport) + mSocketTransport->SetNotificationCallbacks(nsnull, 0); + + if (!mKeepAlive) { + // if we're not going to be keeping this connection alive... + mSocketTransport->SetReuseConnection(PR_FALSE); + mSocketTransport = 0; + } + + return NS_OK; +} + +// not called from the socket thread +nsresult +nsHttpConnection::Resume() +{ + // XXX may require a lock to ensure thread safety + + if (mReadRequest) + mReadRequest->Resume(); + + return NS_OK; +} + +// called from the socket thread +nsresult +nsHttpConnection::ProxyStepUp() +{ + nsCOMPtr securityInfo; + nsresult rv; + + LOG(("nsHttpConnection::ProxyStepUp [this=%x]\n", this)); + + rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr ssl = do_QueryInterface(securityInfo, &rv); + if (NS_FAILED(rv)) return rv; + + return ssl->ProxyStepUp(); +} + +PRBool +nsHttpConnection::CanReuse() +{ + return (mReuseCount < mMaxReuseCount) && + (NowInSeconds() - mLastActiveTime < mIdleTimeout) && IsAlive(); +} + +PRBool +nsHttpConnection::IsAlive() +{ + if (!mSocketTransport) + return PR_FALSE; + + PRBool isAlive = PR_FALSE; + nsresult rv = mSocketTransport->IsAlive(0, &isAlive); + NS_ASSERTION(NS_SUCCEEDED(rv), "IsAlive test failed"); + return isAlive; +} + +void +nsHttpConnection::ReportProgress(PRUint32 progress, PRInt32 progressMax) +{ + if (mProgressSink) + mProgressSink->OnProgress(nsnull, nsnull, progress, + progressMax < 0 ? 0 : PRUint32(progressMax)); +} + +//----------------------------------------------------------------------------- +// nsHttpConnection +//----------------------------------------------------------------------------- + +nsresult +nsHttpConnection::ActivateConnection() +{ + nsresult rv; + + // if we don't have a socket transport then create a new one + if (!mSocketTransport) { + rv = CreateTransport(); + if (NS_FAILED(rv)) return rv; + + // need to handle SSL proxy CONNECT if this is the first time. + if (mConnectionInfo->UsingSSL() && mConnectionInfo->ProxyHost()) { + rv = SetupSSLProxyConnect(); + if (NS_FAILED(rv)) return rv; + } + } + + // allow the socket transport to call us directly on progress + rv = mSocketTransport-> + SetNotificationCallbacks(this, nsITransport::DONT_PROXY_PROGRESS); + if (NS_FAILED(rv)) return rv; + + // note: we pass a reference to ourselves as the context so we can + // differentiate the OnStopRequest events. + rv = mSocketTransport->AsyncWrite(this, (nsIStreamListener*) this, + 0, PRUint32(-1), + nsITransport::DONT_PROXY_OBSERVER | + nsITransport::DONT_PROXY_PROVIDER, + getter_AddRefs(mWriteRequest)); + if (NS_FAILED(rv)) return rv; + + rv = mSocketTransport->AsyncRead(this, nsnull, + 0, PRUint32(-1), + nsITransport::DONT_PROXY_OBSERVER | + nsITransport::DONT_PROXY_LISTENER, + getter_AddRefs(mReadRequest)); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +nsresult +nsHttpConnection::CreateTransport() +{ + nsresult rv; + + NS_PRECONDITION(!mSocketTransport, "unexpected"); + + nsCOMPtr sts = + do_GetService(kSocketTransportServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + // configure the socket type based on the connection type requested. + const char *types[3] = {0,0,0}; + PRUint32 count = 0; + if (mConnectionInfo->UsingSSL()) { + types[0] = "ssl"; + count++; + } + if (!PL_strcasecmp(mConnectionInfo->ProxyType(), "socks")) { + types[count] = "socks"; + count++; + } + + nsCOMPtr transport; + rv = sts->CreateTransportOfTypes(count, types, + mConnectionInfo->Host(), + mConnectionInfo->Port(), + mConnectionInfo->ProxyHost(), + mConnectionInfo->ProxyPort(), + NS_HTTP_SEGMENT_SIZE, + NS_HTTP_BUFFER_SIZE, + getter_AddRefs(transport)); + if (NS_FAILED(rv)) return rv; + + // the transport has better be a socket transport !! + mSocketTransport = do_QueryInterface(transport, &rv); + if (NS_FAILED(rv)) return rv; + + rv = mSocketTransport->SetReuseConnection(PR_TRUE); + if (NS_FAILED(rv)) return rv; + + return rv; +} + +nsresult +nsHttpConnection::ProxyReleaseTransaction(nsHttpTransaction *trans) +{ + LOG(("nsHttpConnection::ProxyReleaseTransaction [this=%x trans=%x refcnt=%u]\n", + this, trans, trans->RefCnt())); + + NS_ENSURE_TRUE(mEventQ, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_ARG_POINTER(trans); + + PLEvent *event = new PLEvent; + if (!event) + return NS_ERROR_OUT_OF_MEMORY; + + PL_InitEvent(event, trans, + TransactionReleaseEventHandler, + TransactionReleaseDestroyHandler); + + return mEventQ->PostEvent(event); +} + +nsresult +nsHttpConnection::SetupSSLProxyConnect() +{ + nsresult rv; + + LOG(("nsHttpConnection::SetupSSLProxyConnect [this=%x]\n", this)); + + NS_ENSURE_TRUE(!mSSLProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED); + + nsCAutoString buf; + buf.Assign(mConnectionInfo->Host()); + buf.Append(':'); + buf.AppendInt(mConnectionInfo->Port()); + + // CONNECT host:port HTTP/1.1 + nsHttpRequestHead request; + request.SetMethod(nsHttp::Connect); + request.SetVersion(nsHttpHandler::get()->DefaultVersion()); + request.SetRequestURI(buf.get()); + request.SetHeader(nsHttp::User_Agent, nsHttpHandler::get()->UserAgent()); + + buf.Truncate(0); + request.Flatten(buf); + buf.Append("\r\n"); + + nsCOMPtr sup; + rv = NS_NewCStringInputStream(getter_AddRefs(sup), buf); + if (NS_FAILED(rv)) return rv; + + mSSLProxyConnectStream = do_QueryInterface(sup, &rv); + return rv; +} + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ADDREF(nsHttpConnection) +NS_IMPL_THREADSAFE_RELEASE(nsHttpConnection) + +NS_INTERFACE_MAP_BEGIN(nsHttpConnection) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIStreamProvider) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIRequestObserver, nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) +NS_INTERFACE_MAP_END_THREADSAFE + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsIRequestObserver +//----------------------------------------------------------------------------- + +// called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::OnStartRequest(nsIRequest *request, nsISupports *ctxt) +{ + LOG(("nsHttpConnection::OnStartRequest [this=%x]\n", this)); + return NS_OK; +} + +// called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::OnStopRequest(nsIRequest *request, nsISupports *ctxt, + nsresult status) +{ + LOG(("nsHttpConnection::OnStopRequest [this=%x ctxt=%x status=%x]\n", + this, ctxt, status)); + + if (!mTransaction) + return NS_OK; + + if (ctxt == (nsISupports *) (nsIStreamListener *) this) + mWriteRequest = 0; + else { + // Done reading, so signal transaction complete... + mReadRequest = 0; + + // break the cycle between the socket transport and this + if (mSocketTransport) + mSocketTransport->SetNotificationCallbacks(nsnull, 0); + + // don't need this anymore + mProgressSink = 0; + + // make sure mTransaction is clear before calling OnStopTransaction + nsHttpTransaction *trans = mTransaction; + mTransaction = nsnull; + + trans->OnStopTransaction(status); + + // because this could be the last reference to the transaction and + // because we are on the socket transport thread, it is essential that + // this final release be proxied to the thread which called + // SetTransaction. + if (NS_FAILED(ProxyReleaseTransaction(trans))) + NS_NOTREACHED("proxy release of transaction failed"); + } + // no point in returning anything else but NS_OK + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsIStreamProvider +//----------------------------------------------------------------------------- + +// called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::OnDataWritable(nsIRequest *request, nsISupports *context, + nsIOutputStream *outputStream, + PRUint32 offset, PRUint32 count) +{ + if (!mTransaction) { + LOG(("nsHttpConnection: no transaction! closing stream\n")); + return NS_BASE_STREAM_CLOSED; + } + + LOG(("nsHttpConnection::OnDataWritable [this=%x]\n", this)); + + // if we're doing an SSL proxy connect, then we need to bypass calling + // into the transaction. + if (mSSLProxyConnectStream) { + PRUint32 n; + + nsresult rv = mSSLProxyConnectStream->Available(&n); + if (NS_FAILED(rv)) return rv; + + // if there are bytes available in the stream, then write them out. + // otherwise, suspend the write request... it'll get restarted once + // we get a response from the proxy server. + if (n) { + LOG(("writing data from proxy connect stream [count=%u]\n", n)); + return outputStream->WriteFrom(mSSLProxyConnectStream, n, &n); + } + + LOG(("done writing proxy connect stream\n")); + return NS_BASE_STREAM_WOULD_BLOCK; + } + + LOG(("calling mTransaction->OnDataWritable\n")); + + // in the normal case, we just want to defer to the transaction to write + // out the request. + return mTransaction->OnDataWritable(outputStream); +} + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsIStreamListener +//----------------------------------------------------------------------------- + +// called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::OnDataAvailable(nsIRequest *request, nsISupports *context, + nsIInputStream *inputStream, + PRUint32 offset, PRUint32 count) +{ + if (!mTransaction) { + LOG(("nsHttpConnection: no transaction! closing stream\n")); + return NS_BASE_STREAM_CLOSED; + } + + mLastActiveTime = NowInSeconds(); + + LOG(("nsHttpConnection::OnDataAvailable [this=%x]\n", this)); + + nsresult rv = mTransaction->OnDataReadable(inputStream); + + LOG(("mTransaction->OnDataReadable() returned [rv=%x]\n", rv)); + return rv; +} + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +// not called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::GetInterface(const nsIID &iid, void **result) +{ + if (iid.Equals(NS_GET_IID(nsIProgressEventSink))) + return QueryInterface(iid, result); + + if (mTransaction && mTransaction->Callbacks()) + return mTransaction->Callbacks()->GetInterface(iid, result); + + return NS_ERROR_NO_INTERFACE; +} + +//----------------------------------------------------------------------------- +// nsHttpConnection::nsIProgressEventSink +//----------------------------------------------------------------------------- + +// called on the socket transport thread +NS_IMETHODIMP +nsHttpConnection::OnStatus(nsIRequest *req, nsISupports *ctx, nsresult status, + const PRUnichar *statusText) +{ + if (mProgressSink) + mProgressSink->OnStatus(nsnull, nsnull, status, statusText); + + return NS_OK; +} + +NS_IMETHODIMP +nsHttpConnection::OnProgress(nsIRequest *req, nsISupports *ctx, + PRUint32 progress, PRUint32 progressMax) +{ + // we ignore progress notifications from the socket transport. + // we'll generate these ourselves from OnDataAvailable + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpConnectionInfo::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionInfo) diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.h b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h new file mode 100644 index 00000000000..acde398da9b --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpConnection_h__ +#define nsHttpConnection_h__ + +#include "nsHttp.h" +#include "nsIStreamListener.h" +#include "nsIStreamProvider.h" +#include "nsISocketTransport.h" +#include "nsIProgressEventSink.h" +#include "nsIInterfaceRequestor.h" +#include "nsIEventQueue.h" +#include "nsIInputStream.h" +#include "nsCOMPtr.h" +#include "nsXPIDLString.h" +#include "plstr.h" +#include "prclist.h" + +class nsHttpHandler; +class nsHttpConnectionInfo; +class nsHttpTransaction; + +//----------------------------------------------------------------------------- +// nsHttpConnection - represents a connection to a HTTP server (or proxy) +//----------------------------------------------------------------------------- + +class nsHttpConnection : public nsIStreamListener + , public nsIStreamProvider + , public nsIProgressEventSink + , public nsIInterfaceRequestor +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLISTENER + NS_DECL_NSISTREAMPROVIDER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIPROGRESSEVENTSINK + + nsHttpConnection(); + virtual ~nsHttpConnection(); + + nsresult Init(nsHttpConnectionInfo *); + + // SetTransaction causes the given transaction to be processed on this + // connection. It fails if there is already an existing transaction. + nsresult SetTransaction(nsHttpTransaction *); + + // called by the transaction to inform the connection that all of the + // headers are available. + nsresult OnHeadersAvailable(nsHttpTransaction *, PRBool *reset); + + // called by the transaction to inform the connection that it is done. + nsresult OnTransactionComplete(nsresult status); + + // called by the transaction to resume a read-in-progress + nsresult Resume(); + + // called to cause the underlying socket to start speaking SSL + nsresult ProxyStepUp(); + + PRBool CanReuse(); // can this connection be reused? + PRBool IsAlive(); + PRUint32 ReuseCount() { return mReuseCount; } + PRUint32 MaxReuseCount() { return mMaxReuseCount; } + PRUint32 IdleTimeout() { return mIdleTimeout; } + + void ReportProgress(PRUint32 progress, PRInt32 progressMax); + + nsHttpTransaction *Transaction() { return mTransaction; } + nsHttpConnectionInfo *ConnectionInfo() { return mConnectionInfo; } + +private: + enum { + IDLE, + WAITING_FOR_WRITE, + WRITING, + WAITING_FOR_READ, + READING + }; + + nsresult ActivateConnection(); + nsresult CreateTransport(); + + // proxy the release of the transaction + nsresult ProxyReleaseTransaction(nsHttpTransaction *); + + nsresult SetupSSLProxyConnect(); + +private: + nsCOMPtr mSocketTransport; + nsCOMPtr mWriteRequest; + nsCOMPtr mReadRequest; + + nsCOMPtr mProgressSink; + nsCOMPtr mEventQ; + + nsCOMPtr mSSLProxyConnectStream; + + nsHttpTransaction *mTransaction; // hard ref + nsHttpConnectionInfo *mConnectionInfo; // hard ref + + PRUint32 mReuseCount; + PRUint32 mMaxReuseCount; // value of keep-alive: max= + PRUint32 mIdleTimeout; // value of keep-alive: timeout= + PRUint32 mLastActiveTime; + + PRPackedBool mKeepAlive; +}; + +//----------------------------------------------------------------------------- +// nsHttpConnectionInfo - holds the properties of a connection +//----------------------------------------------------------------------------- + +class nsHttpConnectionInfo : public nsISupports +{ +public: + NS_DECL_ISUPPORTS + + nsHttpConnectionInfo(const char *host, PRInt32 port=-1, + const char *proxyHost=0, PRInt32 proxyPort=-1, + const char *proxyType=0, PRBool usingSSL=0) + : mProxyPort(proxyPort) + , mUsingSSL(usingSSL) + { + LOG(("Creating nsHttpConnectionInfo @%x\n", this)); + + NS_INIT_ISUPPORTS(); + + mHost = host; + mPort = port == -1 ? DefaultPort() : port; + mProxyHost = proxyHost; + mProxyType = proxyType; + } + + virtual ~nsHttpConnectionInfo() + { + LOG(("Destroying nsHttpConnectionInfo @%x\n", this)); + } + + // Compare this connection info to another... + PRBool Equals(const nsHttpConnectionInfo *info) + { + return !PL_strcasecmp(info->mHost, mHost) && + !PL_strcasecmp(info->mProxyHost, mProxyHost) && + !PL_strcasecmp(info->mProxyType, mProxyType) && + info->mPort == mPort && + info->mProxyPort == mProxyPort && + info->mUsingSSL == mUsingSSL; + } + + const char *Host() { return mHost; } + PRInt32 Port() { return mPort; } + const char *ProxyHost() { return mProxyHost; } + PRInt32 ProxyPort() { return mProxyPort; } + const char *ProxyType() { return mProxyType; } + PRBool UsingSSL() { return mUsingSSL; } + + PRInt32 DefaultPort() { return mUsingSSL ? 443 : 80; } + +private: + nsXPIDLCString mHost; + PRInt32 mPort; + nsXPIDLCString mProxyHost; + PRInt32 mProxyPort; + nsXPIDLCString mProxyType; + PRPackedBool mUsingSSL; +}; + +#endif // nsHttpConnection_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp new file mode 100644 index 00000000000..b19d667c8ec --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -0,0 +1,1633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + * Gagan Saksena + * Pierre Phaneuf + * Christopher Blizzard + * Adrian Havill + */ + +#include "nsHttp.h" +#include "nsHttpHandler.h" +#include "nsHttpChannel.h" +#include "nsHttpConnection.h" +#include "nsHttpResponseHead.h" +#include "nsHttpTransaction.h" +#include "nsHttpAuthCache.h" +#include "nsIHttpChannel.h" +#include "nsIHttpNotify.h" +#include "nsIURL.h" +#include "nsICacheService.h" +#include "nsICategoryManager.h" +#include "nsIObserverService.h" +#include "nsISupportsPrimitives.h" +#include "nsINetModRegEntry.h" +#include "nsICacheService.h" +#include "nsPrintfCString.h" +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "prprf.h" + +#if defined(XP_UNIX) || defined(XP_BEOS) +#include +#endif + +#if defined(XP_PC) && !defined(XP_OS2) +#include +#endif + +static const char NETWORK_PREFS[] = "network."; +static const char INTL_ACCEPT_LANGUAGES[] = "intl.accept_languages"; +static const char INTL_ACCEPT_CHARSET[] = "intl.charset.default"; + +static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID); +static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); +static NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID); +static NS_DEFINE_CID(kNetModuleMgrCID, NS_NETMODULEMGR_CID); +static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID); +static NS_DEFINE_CID(kCacheServiceCID, NS_CACHESERVICE_CID); +static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); + +#define UA_PREF_PREFIX "general.useragent." +#define UA_APPNAME "Mozilla" +#define UA_APPVERSION "5.0" +#define UA_APPSECURITY_FALLBACK "N" + +//----------------------------------------------------------------------------- +// nsHttpHandler +//----------------------------------------------------------------------------- + +nsHttpHandler *nsHttpHandler::mGlobalInstance = 0; + +nsHttpHandler::nsHttpHandler() + : mAuthCache(nsnull) + , mHttpVersion(NS_HTTP_VERSION_1_1) + , mReferrerLevel(PRUint32(-1)) // by default we always send a referrer + , mCapabilities(ALLOW_KEEPALIVE) + , mProxyCapabilities(ALLOW_KEEPALIVE) + , mProxySSLConnectAllowed(PR_TRUE) + , mConnectTimeout(30) + , mRequestTimeout(30) + , mIdleTimeout(10) + , mMaxConnections(16) + , mMaxConnectionsPerServer(8) + , mMaxIdleConnections(16) + , mMaxIdleConnectionsPerServer(4) + , mActiveConnections(0) + , mIdleConnections(0) + , mTransactionQ(0) + , mUserAgentIsDirty(PR_TRUE) +{ + NS_INIT_ISUPPORTS(); + +#if defined(PR_LOGGING) + gHttpLog = PR_NewLogModule("nsHttp"); +#endif + + LOG(("Creating nsHttpHandler [this=%x].\n", this)); + + NS_ASSERTION(!mGlobalInstance, "HTTP handler already created!"); + mGlobalInstance = this; +} + +nsHttpHandler::~nsHttpHandler() +{ + LOG(("Deleting nsHttpHandler [this=%x]\n", this)); + + nsHttp::DestroyAtomTable(); + + if (mPrefs) { + mPrefs->UnregisterCallback(NETWORK_PREFS, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->UnregisterCallback(INTL_ACCEPT_LANGUAGES, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->UnregisterCallback(UA_PREF_PREFIX "override", + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->UnregisterCallback(INTL_ACCEPT_CHARSET, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->UnregisterCallback(UA_PREF_PREFIX "locale", + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->UnregisterCallback(UA_PREF_PREFIX "misc", + nsHttpHandler::PrefsCallback, (void *)this); + } + + LOG(("dropping active connections...\n")); + DropConnections(mActiveConnections); + + LOG(("dropping idle connections...\n")); + DropConnections(mIdleConnections); + + if (mAuthCache) { + delete mAuthCache; + mAuthCache = nsnull; + } + + mGlobalInstance = nsnull; +} + +NS_METHOD +nsHttpHandler::Create(nsISupports *outer, REFNSIID iid, void **result) +{ + nsresult rv; + if (outer) + return NS_ERROR_NO_AGGREGATION; + nsHttpHandler *handler = get(); + if (!handler) { + // create the one any only instance of nsHttpHandler + NS_NEWXPCOM(handler, nsHttpHandler); + if (!handler) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(handler); + rv = handler->Init(); + if (NS_FAILED(rv)) { + LOG(("nsHttpHandler::Init failed [rv=%x]\n", rv)); + NS_RELEASE(handler); + return rv; + } + } + else + NS_ADDREF(handler); + rv = handler->QueryInterface(iid, result); + NS_RELEASE(handler); + return rv; +} + +nsresult +nsHttpHandler::Init() +{ + nsresult rv = NS_OK; + + LOG(("nsHttpHandler::Init\n")); + + mPrefs = do_GetService(kPrefServiceCID, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("unable to continue without prefs service"); + return rv; + } + + InitUserAgentComponents(); + + mPrefs->RegisterCallback(NETWORK_PREFS, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->RegisterCallback(INTL_ACCEPT_LANGUAGES, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->RegisterCallback(UA_PREF_PREFIX "override", + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->RegisterCallback(INTL_ACCEPT_CHARSET, + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->RegisterCallback(UA_PREF_PREFIX "locale", + nsHttpHandler::PrefsCallback, (void*)this); + mPrefs->RegisterCallback(UA_PREF_PREFIX "misc", + nsHttpHandler::PrefsCallback, (void *)this); + + PrefsChanged(); + + mSessionStartTime = NowInSeconds(); + + mAuthCache = new nsHttpAuthCache(); + if (!mAuthCache) + return NS_ERROR_OUT_OF_MEMORY; + rv = mAuthCache->Init(); + if (NS_FAILED(rv)) return rv; + + // Startup the http category + // Bring alive the objects in the http-protocol-startup category + CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY); + + nsCOMPtr observerSvc = + do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv); + if (observerSvc) + observerSvc->AddObserver(this, NS_LITERAL_STRING("profile-before-change").get()); + + return NS_OK; +} + +nsresult +nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request, + PRUint32 caps) +{ + nsresult rv; + + LOG(("nsHttpHandler::AddStandardRequestHeaders\n")); + + // Add the User-Agent header: + rv = request->SetHeader(nsHttp::User_Agent, UserAgent()); + if (NS_FAILED(rv)) return rv; + + // Add the Accept header: + // + // Send */*. We're no longer chopping MIME-types for acceptance. + // MIME based content negotiation has died. + rv = request->SetHeader(nsHttp::Accept, "*/*"); + if (NS_FAILED(rv)) return rv; + + // Add the Accept-Language header: + rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages.get()); + if (NS_FAILED(rv)) return rv; + + // Add the Accept-Encoding header: + rv = request->SetHeader(nsHttp::Accept_Encoding, mAcceptEncodings.get()); + if (NS_FAILED(rv)) return rv; + + // Add the Accept-Charset header: + rv = request->SetHeader(nsHttp::Accept_Charset, mAcceptCharsets.get()); + if (NS_FAILED(rv)) return rv; + + // Add the Connection header: + const char *connectionType = "close"; + if (caps && ALLOW_KEEPALIVE) { + char buf[32]; + + PR_snprintf(buf, sizeof(buf), "%d", mIdleTimeout); + + rv = request->SetHeader(nsHttp::Keep_Alive, buf); + if (NS_FAILED(rv)) return rv; + + connectionType = "keep-alive"; + } + return request->SetHeader(nsHttp::Connection, connectionType); +} + +nsresult +nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy, + nsICacheSession **result) +{ + static PRBool checkedPref = PR_FALSE; + static PRBool useCache = PR_TRUE; + nsresult rv; + + if (!checkedPref) { + // XXX should register a prefs changed callback for this + nsCOMPtr prefs = do_GetService(kPrefServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + prefs->GetBoolPref("browser.cache.enable", &useCache); + + checkedPref = PR_TRUE; + } + + // Skip cache if disabled in preferences + if (!useCache) + return NS_ERROR_NOT_AVAILABLE; + + if (!mCacheSession_ANY) { + nsCOMPtr serv = do_GetService(kCacheServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = serv->CreateSession("HTTP", + nsICache::STORE_ANYWHERE, + nsICache::STREAM_BASED, + getter_AddRefs(mCacheSession_ANY)); + if (NS_FAILED(rv)) return rv; + + rv = mCacheSession_ANY->SetDoomEntriesIfExpired(PR_FALSE); + if (NS_FAILED(rv)) return rv; + + rv = serv->CreateSession("HTTP-memory-only", + nsICache::STORE_IN_MEMORY, + nsICache::STREAM_BASED, + getter_AddRefs(mCacheSession_MEM)); + if (NS_FAILED(rv)) return rv; + + rv = mCacheSession_MEM->SetDoomEntriesIfExpired(PR_FALSE); + if (NS_FAILED(rv)) return rv; + } + + if (storagePolicy == nsICache::STORE_IN_MEMORY) + NS_ADDREF(*result = mCacheSession_MEM); + else + NS_ADDREF(*result = mCacheSession_ANY); + + return NS_OK; +} + +nsresult +nsHttpHandler::InitiateTransaction(nsHttpTransaction *trans, + nsHttpConnectionInfo *ci, + PRBool failIfBusy) +{ + nsresult rv; + + LOG(("nsHttpHandler::InitiateTransaction\n")); + + NS_ENSURE_ARG_POINTER(trans); + NS_ENSURE_ARG_POINTER(ci); + + if ((mActiveConnections.Count() == mMaxConnections) || + (CountActiveConnections(ci) == PRUint32(mMaxConnectionsPerServer))) { + LOG(("unable to perform the transaction at this time [trans=%x]\n", trans)); + if (failIfBusy) return NS_ERROR_FAILURE; + return EnqueueTransaction(trans, ci); + } + + nsHttpConnection *conn = nsnull; + + // search the idle connection list + PRInt32 i; + for (i=0; iConnectionInfo()->Host(), conn->ConnectionInfo()->Port())); + + if (conn->ConnectionInfo()->Equals(ci)) { + // found a matching connection; remove from list + mIdleConnections.RemoveElementAt(i); + + i--; + + if (conn->CanReuse()) { + LOG(("reusing connection [conn=%x]\n", conn)); + break; + } + else { + LOG(("dropping stale connection: [conn=%x]\n", conn)); + NS_RELEASE(conn); + } + } + conn = nsnull; + } + + if (!conn) { + LOG(("creating new connection...\n")); + NS_NEWXPCOM(conn, nsHttpConnection); + if (!conn) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(conn); + + rv = conn->Init(ci); + if (NS_FAILED(rv)) goto failed; + } + + rv = conn->SetTransaction(trans); + if (NS_FAILED(rv)) goto failed; + + mActiveConnections.AppendElement(conn); + return NS_OK; + +failed: + NS_RELEASE(conn); + return rv; +} + +nsresult +nsHttpHandler::ReclaimConnection(nsHttpConnection *conn) +{ + NS_ENSURE_ARG_POINTER(conn); + + LOG(("nsHttpHandler::ReclaimConnection [conn=%x keep-alive=%d]\n", + conn, conn->CanReuse())); + + // remove connection from the active connection list + mActiveConnections.RemoveElement(conn); + + LOG(("active connection count = %u\n", mActiveConnections.Count())); + + if (conn->CanReuse()) { + // verify that we aren't already maxed out on the number of + // keep-alives we can have for this server. + PRUint32 count = CountIdleConnections(conn->ConnectionInfo()); + if (count == PRUint32(mMaxIdleConnectionsPerServer)) { + LOG(("not caching keep-alive connection: " + "would exceed max allowed per server\n")); + NS_RELEASE(conn); + } + else { + LOG(("adding connection to idle list [conn=%x]\n", conn)); + // hold onto this connection in the idle list. we push it + // to the end of the list so as to ensure that we'll visit + // older connections first before getting to this one. + mIdleConnections.AppendElement(conn); + } + } + else { + LOG(("closing connection: connection can't be reused\n")); + NS_RELEASE(conn); + } + + // process the pending transaction queue... + if (mTransactionQ.Count() > 0) + ProcessTransactionQ(); + + return NS_OK; +} + +nsresult +nsHttpHandler::CancelPendingTransaction(nsHttpTransaction *trans, + nsresult status) +{ + LOG(("nsHttpHandler::CancelPendingTransaction [trans=%x status=%x]\n", + trans, status)); + + NS_ENSURE_ARG_POINTER(trans); + + nsPendingTransaction *pt = nsnull; + + PRInt32 i; + for (i=0; iTransaction() == trans) { + trans->OnStopTransaction(status); + + mTransactionQ.RemoveElementAt(i); + delete pt; + + return NS_OK; + } + } + + LOG(("CancelPendingTransaction failed: transaction not in pending queue\n")); + return NS_ERROR_NOT_AVAILABLE; +} + +nsresult +nsHttpHandler::GetProxyObjectManager(nsIProxyObjectManager **result) +{ + if (!mProxyMgr) { + nsresult rv; + mProxyMgr = do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + } + *result = mProxyMgr; + NS_ADDREF(*result); + return NS_OK; +} + +nsresult +nsHttpHandler::GetEventQueueService(nsIEventQueueService **result) +{ + if (!mEventQueueService) { + nsresult rv; + mEventQueueService = do_GetService(kEventQueueServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + } + *result = mEventQueueService; + NS_ADDREF(*result); + return NS_OK; +} + +nsresult +nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result) +{ + if (!mStreamConvSvc) { + nsresult rv; + mStreamConvSvc = do_GetService(kStreamConverterServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + } + *result = mStreamConvSvc; + NS_ADDREF(*result); + return NS_OK; +} + +nsresult +nsHttpHandler::OnModifyRequest(nsIHttpChannel *chan) +{ + nsresult rv; + + LOG(("nsHttpHandler::OnModifyRequest [chan=%x]\n", chan)); + + if (!mNetModuleMgr) { + mNetModuleMgr = do_GetService(kNetModuleMgrCID, &rv); + if (NS_FAILED(rv)) return rv; + } + + nsCOMPtr modules; + rv = mNetModuleMgr->EnumerateModules( + NS_NETWORK_MODULE_MANAGER_HTTP_REQUEST_CONTRACTID, + getter_AddRefs(modules)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr sup; + + // notify each module... + while (NS_SUCCEEDED(modules->GetNext(getter_AddRefs(sup)))) { + nsCOMPtr entry = do_QueryInterface(sup, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr netNotify; + rv = entry->GetSyncProxy(getter_AddRefs(netNotify)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr httpNotify = do_QueryInterface(netNotify, &rv); + if (NS_FAILED(rv)) return rv; + + // fire off the notification, ignore the return code. + httpNotify->OnModifyRequest(chan); + } + + return NS_OK; +} + +nsresult +nsHttpHandler::OnExamineResponse(nsIHttpChannel *chan) +{ + nsresult rv; + + LOG(("nsHttpHandler::OnExamineResponse [chan=%x]\n", chan)); + + if (!mNetModuleMgr) { + mNetModuleMgr = do_GetService(kNetModuleMgrCID, &rv); + if (NS_FAILED(rv)) return rv; + } + + nsCOMPtr modules; + rv = mNetModuleMgr->EnumerateModules( + NS_NETWORK_MODULE_MANAGER_HTTP_RESPONSE_CONTRACTID, + getter_AddRefs(modules)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr sup; + nsCOMPtr entry; + nsCOMPtr netNotify; + nsCOMPtr httpNotify; + + // notify each module... + while (NS_SUCCEEDED(modules->GetNext(getter_AddRefs(sup)))) { + entry = do_QueryInterface(sup, &rv); + if (NS_FAILED(rv)) return rv; + + rv = entry->GetSyncProxy(getter_AddRefs(netNotify)); + if (NS_FAILED(rv)) return rv; + + httpNotify = do_QueryInterface(netNotify, &rv); + if (NS_FAILED(rv)) return rv; + + // fire off the notification, ignore the return code. + httpNotify->OnExamineResponse(chan); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpHandler +//----------------------------------------------------------------------------- + +const char * +nsHttpHandler::UserAgent() +{ + if (mUserAgentIsDirty) { + BuildUserAgent(); + mUserAgentIsDirty = PR_FALSE; + } + return mUserAgent.get(); +} + +void +nsHttpHandler::ProcessTransactionQ() +{ + LOG(("nsHttpHandler::ProcessTransactionQ\n")); + + nsPendingTransaction *pt = nsnull; + + PRInt32 i; + for (i=0; (i < mTransactionQ.Count()) && + (mActiveConnections.Count() < mMaxConnections); ++i) { + + pt = (nsPendingTransaction *) mTransactionQ[i]; + + // try to initiate this transaction... if it fails + // then we'll just skip over this pending transaction + // and try the next. + nsresult rv = InitiateTransaction(pt->Transaction(), + pt->ConnectionInfo(), + PR_TRUE); + if (NS_SUCCEEDED(rv)) { + mTransactionQ.RemoveElementAt(i); + delete pt; + i--; + } + } +} + +nsresult +nsHttpHandler::EnqueueTransaction(nsHttpTransaction *trans, + nsHttpConnectionInfo *ci) +{ + LOG(("nsHttpHandler::EnqueueTransaction [trans=%x]\n", trans)); + + nsPendingTransaction *pt = new nsPendingTransaction(trans, ci); + if (!pt) + return NS_ERROR_OUT_OF_MEMORY; + + mTransactionQ.AppendElement(pt); + + LOG(("transaction queue contains %u elements\n", mTransactionQ.Count())); + return NS_OK; +} + +PRUint32 +nsHttpHandler::CountActiveConnections(nsHttpConnectionInfo *ci) +{ + PRUint32 count = mActiveConnections.Count(); + + LOG(("nsHttpHandler::CountActiveConnections [host=%s:%d] found %u\n", + ci->Host(), ci->Port(), count)); + + return count; +} + +PRUint32 +nsHttpHandler::CountIdleConnections(nsHttpConnectionInfo *ci) +{ + PRUint32 count = 0; + nsHttpConnection *conn = 0; + + if (!ci) + return 0; + + LOG(("nsHttpHandler::CountIdleConnections [host=%s:%d]\n", + ci->Host(), ci->Port())); + + PRInt32 i; + for (i=0; iConnectionInfo()->Equals(ci)) { + if (conn->CanReuse()) + count++; + else { + mIdleConnections.RemoveElementAt(i); + NS_RELEASE(conn); + i--; + } + } + } + + LOG(("found count=%u\n", count)); + return count; +} + +void +nsHttpHandler::DropConnections(nsVoidArray &connections) +{ + nsHttpConnection *conn; + PRInt32 i; + for (i=0; iCopyCharPref(UA_PREF_PREFIX "override", + getter_Copies(mUserAgentOverride)); + + // Gather vendor values. + mPrefs->CopyCharPref(UA_PREF_PREFIX "vendor", + getter_Copies(mVendor)); + + mPrefs->CopyCharPref(UA_PREF_PREFIX "vendorSub", + getter_Copies(mVendorSub)); + + mPrefs->CopyCharPref(UA_PREF_PREFIX "vendorComment", + getter_Copies(mVendorComment)); + + // Gather product values. + mPrefs->CopyCharPref(UA_PREF_PREFIX "product", + getter_Copies(mProduct)); + + mPrefs->CopyCharPref(UA_PREF_PREFIX "productSub", + getter_Copies(mProductSub)); + + mPrefs->CopyCharPref(UA_PREF_PREFIX "productComment", + getter_Copies(mProductComment)); + + // Gather misc value. + mPrefs->CopyCharPref(UA_PREF_PREFIX "misc", + getter_Copies(mMisc)); + + // Gather Application name and Version. + mAppName = UA_APPNAME; + mAppVersion = UA_APPVERSION; + + // Get Security level supported + mPrefs->CopyCharPref(UA_PREF_PREFIX "security", + getter_Copies(mSecurity)); + if (!mSecurity) + mSecurity = UA_APPSECURITY_FALLBACK; + + // Gather locale. + nsXPIDLString uval; + mPrefs->GetLocalizedUnicharPref(UA_PREF_PREFIX "locale", + getter_Copies(uval)); + if (uval) + mLanguage = NS_ConvertUCS2toUTF8(uval).get(); + + // Gather platform. +#if defined(XP_OS2) + mPlatform = "OS/2"; +#elif defined(XP_PC) + mPlatform = "Windows"; +#elif defined(RHAPSODY) + mPlatform = "Macintosh"; +#elif defined (XP_UNIX) + mPlatform = "X11"; +#elif defined(XP_BEOS) + mPlatform = "BeOS"; +#elif defined(XP_MAC) + mPlatform = "Macintosh"; +#endif + + // Gather OS/CPU. +#if defined(XP_OS2) + ULONG os2ver = 0; + DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_MINOR, + &os2ver, sizeof(os2ver)); + if (os2ver == 11) + mOscpu = "2.11"; + else if (os2ver == 30) + mOscpu = "Warp 3"; + else if (os2ver == 40) + mOscpu = "Warp 4"; + else if (os2ver == 45) + mOscpu = "Warp 4.5"; + +#elif defined(XP_PC) + OSVERSIONINFO info = { sizeof OSVERSIONINFO }; + if (GetVersionEx(&info)) { + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) { + if (info.dwMajorVersion == 3) + mOscpu = "WinNT3.51"; + else if (info.dwMajorVersion == 4) + mOscpu = "WinNT4.0"; + else if (info.dwMajorVersion >= 5) { + char *buf = PR_smprintf("Windows NT %ld.%ld", + info.dwMajorVersion, + info.dwMinorVersion); + if (buf) { + mOscpu = buf; + PR_smprintf_free(buf); + } + } + else + mOscpu = "WinNT"; + } else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + if (info.dwMinorVersion == 90) + mOscpu = "Win 9x 4.90"; + else if (info.dwMinorVersion > 0) + mOscpu = "Win98"; + else + mOscpu = "Win95"; + } + } +#elif defined (XP_UNIX) || defined (XP_BEOS) + struct utsname name; + + int ret = uname(&name); + if (ret >= 0) { + nsCString buf; + buf = (char*)name.sysname; + buf += ' '; + buf += (char*)name.release; + buf += ' '; + buf += (char*)name.machine; + mOscpu = buf; + } +#elif defined (XP_MAC) + mOscpu = "PPC"; +#endif + + mUserAgentIsDirty = PR_TRUE; +} + +void +nsHttpHandler::PrefsChanged(const char *pref) +{ + PRBool bChangedAll = pref ? PR_FALSE : PR_TRUE; + + if (!mPrefs) { + NS_NOTREACHED("No preference service available!"); + return; + } + + nsresult rv = NS_OK; + + if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive.timeout") == 0) + mPrefs->GetIntPref("network.http.keep-alive.timeout", &mIdleTimeout); + + if (bChangedAll || PL_strcmp(pref, "network.http.max-connections") == 0) + mPrefs->GetIntPref("network.http.max-connections", &mMaxConnections); + + if (bChangedAll || PL_strcmp(pref, "network.http.max-connections-per-server") == 0) + mPrefs->GetIntPref("network.http.max-connections-per-server", &mMaxConnectionsPerServer); + + if (bChangedAll || PL_strcmp(pref, "network.http.sendRefererHeader") == 0) + mPrefs->GetIntPref("network.http.sendRefererHeader", (PRInt32 *) &mReferrerLevel); + + if (bChangedAll || PL_strcmp(pref, "network.http.version") == 0) { + nsXPIDLCString httpVersion; + mPrefs->CopyCharPref("network.http.version", getter_Copies(httpVersion)); + + if (httpVersion) { + if (PL_strcmp(httpVersion, "1.1") == 0) + mHttpVersion = NS_HTTP_VERSION_1_1; + else if (PL_strcmp(httpVersion, "0.9") == 0) + mHttpVersion = NS_HTTP_VERSION_0_9; + else + mHttpVersion = NS_HTTP_VERSION_1_0; + } + + if (mHttpVersion == NS_HTTP_VERSION_1_1) { + mCapabilities = ALLOW_KEEPALIVE; + mProxyCapabilities = ALLOW_KEEPALIVE; + } + else { + mCapabilities = 0; + mProxyCapabilities = 0; + } + } + + PRBool cVar = PR_FALSE; + + if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive") == 0) { + rv = mPrefs->GetBoolPref("network.http.keep-alive", &cVar); + if (NS_SUCCEEDED(rv)) { + if (cVar) + mCapabilities |= ALLOW_KEEPALIVE; + else + mCapabilities &= ~ALLOW_KEEPALIVE; + } + } + + if (bChangedAll || PL_strcmp(pref, "network.http.proxy.keep-alive") == 0) { + rv = mPrefs->GetBoolPref("network.http.proxy.keep-alive", &cVar); + if (NS_SUCCEEDED(rv)) { + if (cVar) + mProxyCapabilities |= ALLOW_KEEPALIVE; + else + mProxyCapabilities &= ~ALLOW_KEEPALIVE; + } + } + + if (bChangedAll || PL_strcmp(pref, "network.http.pipelining") == 0) { + rv = mPrefs->GetBoolPref("network.http.pipelining", &cVar); + if (NS_SUCCEEDED(rv)) { + if (cVar) + mCapabilities |= ALLOW_PIPELINING; + else + mCapabilities &= ~ALLOW_PIPELINING; + } + } + + /* + mPipelineFirstRequest = PR_FALSE; + rv = mPrefs->GetBoolPref("network.http.pipelining.firstrequest", &mPipelineFirstRequest); + + mPipelineMaxRequests = DEFAULT_PIPELINE_MAX_REQUESTS; + rv = mPrefs->GetIntPref("network.http.pipelining.maxrequests", &mPipelineMaxRequests ); + */ + + if (bChangedAll || PL_strcmp(pref, "network.http.proxy.pipelining") == 0) { + rv = mPrefs->GetBoolPref("network.http.proxy.pipelining", &cVar); + if (NS_SUCCEEDED(rv)) { + if (cVar) + mProxyCapabilities |= ALLOW_PIPELINING; + else + mProxyCapabilities &= ~ALLOW_PIPELINING; + } + } + + if (bChangedAll || PL_strcmp(pref, "network.http.proxy.ssl.connect") == 0) + mPrefs->GetBoolPref("network.http.proxy.ssl.connect", &mProxySSLConnectAllowed); + + if (bChangedAll || PL_strcmp(pref, "network.http.connect.timeout") == 0) + mPrefs->GetIntPref("network.http.connect.timeout", &mConnectTimeout); + + if (bChangedAll || PL_strcmp(pref, "network.http.request.timeout") == 0) + mPrefs->GetIntPref("network.http.request.timeout", &mRequestTimeout); + + if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive.max-connections") == 0) + mPrefs->GetIntPref("network.http.keep-alive.max-connections", &mMaxIdleConnections); + + if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive.max-connections-per-server") == 0) + mPrefs->GetIntPref("network.http.keep-alive.max-connections-per-server", &mMaxIdleConnectionsPerServer); + + if (bChangedAll || PL_strcmp(pref, INTL_ACCEPT_LANGUAGES) == 0) { + nsXPIDLString acceptLanguages; + rv = mPrefs->GetLocalizedUnicharPref(INTL_ACCEPT_LANGUAGES, + getter_Copies(acceptLanguages)); + if (NS_SUCCEEDED(rv)) + SetAcceptLanguages(NS_ConvertUCS2toUTF8(acceptLanguages).get()); + } + + if (bChangedAll || PL_strcmp(pref, INTL_ACCEPT_CHARSET) == 0) { + nsXPIDLString acceptCharset; + rv = mPrefs->GetLocalizedUnicharPref(INTL_ACCEPT_CHARSET, + getter_Copies(acceptCharset)); + if (NS_SUCCEEDED(rv)) + SetAcceptCharsets(NS_ConvertUCS2toUTF8(acceptCharset).get()); + } + + // general.useragent.override + if (bChangedAll || PL_strcmp(pref, UA_PREF_PREFIX "override") == 0) { + nsXPIDLCString uval; + rv = mPrefs->CopyCharPref(UA_PREF_PREFIX "override", + getter_Copies(uval)); + if (NS_SUCCEEDED(rv)) { + mUserAgentOverride = uval.get(); + mUserAgentIsDirty = PR_TRUE; + } + } + + if (bChangedAll || PL_strcmp(pref, UA_PREF_PREFIX "locale") == 0) { + // 55156: re-Gather locale. + nsXPIDLString uval; + rv = mPrefs->GetLocalizedUnicharPref(UA_PREF_PREFIX "locale", + getter_Copies(uval)); + if (NS_SUCCEEDED(rv)) { + mLanguage = NS_ConvertUCS2toUTF8(uval).get(); + mUserAgentIsDirty = PR_TRUE; + } + } + + // general.useragent.misc + if (bChangedAll || PL_strcmp(pref, UA_PREF_PREFIX "misc") == 0) { + nsXPIDLCString uval; + rv = mPrefs->CopyCharPref(UA_PREF_PREFIX "misc", + getter_Copies(uval)); + if (NS_SUCCEEDED(rv)) { + mMisc = uval.get(); + mUserAgentIsDirty = PR_TRUE; + } + } + + if (bChangedAll || PL_strcmp(pref, "network.http.accept-encoding") == 0) { + nsXPIDLCString acceptEncodings; + rv = mPrefs->CopyCharPref("network.http.accept-encoding", + getter_Copies(acceptEncodings)); + if (NS_SUCCEEDED(rv)) + SetAcceptEncodings(acceptEncodings); + } +} + +PRInt32 PR_CALLBACK +nsHttpHandler::PrefsCallback(const char *pref, void *self) +{ + nsHttpHandler *handler = (nsHttpHandler *) self; + NS_ASSERTION(handler, "bad instance data"); + if (handler) + handler->PrefsChanged(pref); + return 0; +} + +/* + * CreateServicesFromCategory() + * + * Given a category, this convenience functions enumerates the category and + * creates a service of every CID or ContractID registered under the category + * + * @category: Input category + * @return: returns error if any CID or ContractID registered failed to create. + */ +nsresult +nsHttpHandler::CreateServicesFromCategory(const char *category) +{ + nsresult rv = NS_OK; + + int nFailed = 0; + nsCOMPtr categoryManager = + do_GetService("@mozilla.org/categorymanager;1", &rv); + if (!categoryManager) return rv; + + nsCOMPtr enumerator; + rv = categoryManager->EnumerateCategory(category, + getter_AddRefs(enumerator)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr entry; + while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) { + // From here on just skip any error we get. + nsCOMPtr catEntry = do_QueryInterface(entry, &rv); + if (NS_FAILED(rv)) { + nFailed++; + continue; + } + nsXPIDLCString entryString; + rv = catEntry->GetData(getter_Copies(entryString)); + if (NS_FAILED(rv)) { + nFailed++; + continue; + } + nsXPIDLCString contractID; + rv = categoryManager->GetCategoryEntry(category,(const char *)entryString, getter_Copies(contractID)); + if (NS_FAILED(rv)) { + nFailed++; + continue; + } + + LOG(("nsHttpHandler: instantiating [%s]\n", (const char *)contractID)); + + nsCOMPtr instance = do_GetService(contractID, &rv); + if (NS_FAILED(rv)) + nFailed++; + + // try an observer, if it implements it. + nsCOMPtr observer = do_QueryInterface(instance, &rv); + if (NS_SUCCEEDED(rv) && observer) + observer->Observe(NS_STATIC_CAST(nsISupports*,NS_STATIC_CAST(void*,this)), + NS_HTTP_STARTUP_TOPIC, + NS_LITERAL_STRING("").get()); + } + return (nFailed ? NS_ERROR_FAILURE : NS_OK); +} + +/** + * Allocates a C string into that contains a ISO 639 language list + * notated with HTTP "q" values for output with a HTTP Accept-Language + * header. Previous q values will be stripped because the order of + * the langs imply the q value. The q values are calculated by dividing + * 1.0 amongst the number of languages present. + * + * Ex: passing: "en, ja" + * returns: "en, ja; q=0.500" + * + * passing: "en, ja, fr_CA" + * returns: "en, ja; q=0.667, fr_CA; q=0.333" + */ +static nsresult +PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLanguages) +{ + if (!i_AcceptLanguages) + return NS_OK; + + PRUint32 n, size, wrote; + double q, dec; + char *p, *p2, *token, *q_Accept, *o_Accept; + const char *comma; + PRInt32 available; + + + o_Accept = nsCRT::strdup(i_AcceptLanguages); + if (nsnull == o_Accept) + return NS_ERROR_OUT_OF_MEMORY; + for (p = o_Accept, n = size = 0; '\0' != *p; p++) { + if (*p == ',') n++; + size++; + } + + available = size + ++n * 11 + 1; + q_Accept = new char[available]; + if ((char *) 0 == q_Accept) + return nsnull; + *q_Accept = '\0'; + q = 1.0; + dec = q / (double) n; + n = 0; + p2 = q_Accept; + for (token = nsCRT::strtok(o_Accept, ",", &p); + token != (char *) 0; + token = nsCRT::strtok(p, ",", &p)) + { + while (*token == ' ' || *token == '\x9') token++; + char* trim; + trim = PL_strpbrk(token, "; \x9"); + if (trim != (char*)0) // remove "; q=..." if present + *trim = '\0'; + + if (*token != '\0') { + comma = n++ != 0 ? ", " : ""; // delimiter if not first item + if (q < 0.9995) + wrote = PR_snprintf(p2, available, "%s%s; q=%1.3f", comma, token, q); + else + wrote = PR_snprintf(p2, available, "%s%s", comma, token); + q -= dec; + p2 += wrote; + available -= wrote; + NS_ASSERTION(available > 0, "allocated string not long enough"); + } + } + nsCRT::free(o_Accept); + + o_AcceptLanguages.Assign((const char *) q_Accept); + delete [] q_Accept; + + return NS_OK; +} + +nsresult +nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages) +{ + nsCString buf; + nsresult rv = PrepareAcceptLanguages(aAcceptLanguages, buf); + if (NS_SUCCEEDED(rv)) + mAcceptLanguages.Assign(buf.get()); + return rv; +} + +/** + * Allocates a C string into that contains a character set/encoding list + * notated with HTTP "q" values for output with a HTTP Accept-Charset + * header. If the UTF-8 character set is not present, it will be added. + * If a wildcard catch-all is not present, it will be added. If more than + * one charset is set (as of 2001-02-07, only one is used), they will be + * comma delimited and with q values set for each charset in decending order. + * + * Ex: passing: "euc-jp" + * returns: "euc-jp, utf-8; q=0.667, *; q=0.667" + * + * passing: "UTF-8" + * returns: "UTF-8, *" + */ +static nsresult +PrepareAcceptCharsets(const char *i_AcceptCharset, nsACString &o_AcceptCharset) +{ + PRUint32 n, size, wrote; + PRInt32 available; + double q, dec; + char *p, *p2, *token, *q_Accept, *o_Accept; + const char *acceptable, *comma; + PRBool add_utf = PR_FALSE; + PRBool add_asterick = PR_FALSE; + + if (!i_AcceptCharset) + acceptable = ""; + else + acceptable = i_AcceptCharset; + o_Accept = nsCRT::strdup(acceptable); + if (nsnull == o_Accept) + return NS_ERROR_OUT_OF_MEMORY; + for (p = o_Accept, n = size = 0; '\0' != *p; p++) { + if (*p == ',') n++; + size++; + } + + // only add "utf-8" and "*" to the list if they aren't + // already specified. + + if (PL_strcasestr(acceptable, "utf-8") == NULL) { + n++; + add_utf = PR_TRUE; + } + if (PL_strstr(acceptable, "*") == NULL) { + n++; + add_asterick = PR_TRUE; + } + + available = size + ++n * 11 + 1; + q_Accept = new char[available]; + if ((char *) 0 == q_Accept) + return NS_ERROR_OUT_OF_MEMORY; + *q_Accept = '\0'; + q = 1.0; + dec = q / (double) n; + n = 0; + p2 = q_Accept; + for (token = nsCRT::strtok(o_Accept, ",", &p); + token != (char *) 0; + token = nsCRT::strtok(p, ",", &p)) { + while (*token == ' ' || *token == '\x9') token++; + char* trim; + trim = PL_strpbrk(token, "; \x9"); + if (trim != (char*)0) // remove "; q=..." if present + *trim = '\0'; + + if (*token != '\0') { + comma = n++ != 0 ? ", " : ""; // delimiter if not first item + if (q < 0.9995) + wrote = PR_snprintf(p2, available, "%s%s; q=%1.3f", comma, token, q); + else + wrote = PR_snprintf(p2, available, "%s%s", comma, token); + q -= dec; + p2 += wrote; + available -= wrote; + NS_ASSERTION(available > 0, "allocated string not long enough"); + } + } + if (add_utf) { + comma = n++ != 0 ? ", " : ""; // delimiter if not first item + if (q < 0.9995) + wrote = PR_snprintf(p2, available, "%sutf-8; q=%1.3f", comma, q); + else + wrote = PR_snprintf(p2, available, "%sutf-8", comma); + q -= dec; + p2 += wrote; + available -= wrote; + NS_ASSERTION(available > 0, "allocated string not long enough"); + } + if (add_asterick) { + comma = n++ != 0 ? ", " : ""; // delimiter if not first item + + // keep q of "*" equal to the lowest q value + // in the event of a tie between the q of "*" and a non-wildcard + // the non-wildcard always receives preference. + + q += dec; + if (q < 0.9995) { + wrote = PR_snprintf(p2, available, "%s*; q=%1.3f", comma, q); + } + else + wrote = PR_snprintf(p2, available, "%s*", comma); + available -= wrote; + p2 += wrote; + NS_ASSERTION(available > 0, "allocated string not long enough"); + } + nsCRT::free(o_Accept); + + // change alloc from C++ new/delete to nsCRT::strdup's way + o_AcceptCharset.Assign(q_Accept); +#if defined DEBUG_havill + printf("Accept-Charset: %s\n", q_Accept); +#endif + delete [] q_Accept; + return NS_OK; +} + +nsresult +nsHttpHandler::SetAcceptCharsets(const char *aAcceptCharsets) +{ + nsCString buf; + nsresult rv = PrepareAcceptCharsets(aAcceptCharsets, buf); + if (NS_SUCCEEDED(rv)) + mAcceptCharsets.Assign(buf.get()); + return rv; +} + +nsresult +nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings) +{ + mAcceptEncodings = aAcceptEncodings; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpHandler::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpHandler, + nsIHttpProtocolHandler, + nsIProtocolHandler, + nsIObserver, + nsISupportsWeakReference) + +//----------------------------------------------------------------------------- +// nsHttpHandler::nsIProtocolHandler +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpHandler::GetScheme(char **aScheme) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpHandler::GetDefaultPort(PRInt32 *aDefaultPort) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpHandler::NewURI(const char *aSpec, nsIURI *aBaseURI, nsIURI **aURI) +{ + nsresult rv = NS_OK; + + LOG(("nsHttpHandler::NewURI\n")); + + nsCOMPtr url = do_CreateInstance(kStandardURLCID, &rv); + if (NS_FAILED(rv)) return rv; + + // XXX need to choose the default port based on the scheme + rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 80, aSpec, aBaseURI); + if (NS_FAILED(rv)) return rv; + + return CallQueryInterface(url, aURI); +} + +NS_IMETHODIMP +nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result) +{ + LOG(("nsHttpHandler::NewChannel\n")); + + NS_ENSURE_ARG_POINTER(uri); + NS_ENSURE_ARG_POINTER(result); + + PRBool isHttp = PR_FALSE, isHttps = PR_FALSE; + + // Verify that we have been given a valid scheme + nsresult rv = uri->SchemeIs("http", &isHttp); + if (NS_FAILED(rv)) return rv; + if (!isHttp) { + rv = uri->SchemeIs("https", &isHttps); + if (NS_FAILED(rv)) return rv; + if (!isHttps) { + NS_WARNING("Invalid URI scheme"); + return NS_ERROR_UNEXPECTED; + } + } + + rv = NewProxyChannel(uri, + + nsnull, + -1, + nsnull, + result); + return rv; +} + +//----------------------------------------------------------------------------- +// nsHttpHandler::nsIHttpProtocolHandler +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpHandler::NewProxyChannel(nsIURI *uri, + const char *proxyHost, + PRInt32 proxyPort, + const char *proxyType, + nsIChannel **result) +{ + nsHttpChannel *httpChannel = nsnull; + + LOG(("nsHttpHandler::NewProxyChannel [proxy=%s:%d type=%s]\n", + proxyHost, proxyPort, proxyType)); + + NS_NEWXPCOM(httpChannel, nsHttpChannel); + if (!httpChannel) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(httpChannel); + + nsresult rv = httpChannel->Init(uri, + mCapabilities, + proxyHost, + proxyPort, + proxyType); + + if (NS_SUCCEEDED(rv)) + rv = httpChannel-> + QueryInterface(NS_GET_IID(nsIChannel), (void **) result); + + NS_RELEASE(httpChannel); + return rv; +} + +NS_IMETHODIMP +nsHttpHandler::GetUserAgent(char **aUserAgent) +{ + return DupString(UserAgent(), aUserAgent); +} + +NS_IMETHODIMP +nsHttpHandler::GetAppName(char **aAppName) +{ + return DupString(mAppName, aAppName); +} + +NS_IMETHODIMP +nsHttpHandler::GetAppVersion(char **aAppVersion) +{ + return DupString(mAppVersion, aAppVersion); +} + +NS_IMETHODIMP +nsHttpHandler::GetVendor(char **aVendor) +{ + return DupString(mVendor, aVendor); +} +NS_IMETHODIMP +nsHttpHandler::SetVendor(const char *aVendor) +{ + mVendor = aVendor; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetVendorSub(char **aVendorSub) +{ + return DupString(mVendorSub, aVendorSub); +} +NS_IMETHODIMP +nsHttpHandler::SetVendorSub(const char *aVendorSub) +{ + mVendorSub = aVendorSub; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetVendorComment(char **aVendorComment) +{ + return DupString(mVendorComment, aVendorComment); +} +NS_IMETHODIMP +nsHttpHandler::SetVendorComment(const char *aVendorComment) +{ + mVendorComment = aVendorComment; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetProduct(char **aProduct) +{ + return DupString(mProduct, aProduct); +} +NS_IMETHODIMP +nsHttpHandler::SetProduct(const char *aProduct) +{ + mProduct = aProduct; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetProductSub(char **aProductSub) +{ + return DupString(mProductSub, aProductSub); +} +NS_IMETHODIMP +nsHttpHandler::SetProductSub(const char *aProductSub) +{ + mProductSub = aProductSub; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetProductComment(char **aProductComment) +{ + return DupString(mProductComment, aProductComment); +} +NS_IMETHODIMP +nsHttpHandler::SetProductComment(const char *aProductComment) +{ + mProductComment = aProductComment; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetPlatform(char **aPlatform) +{ + return DupString(mPlatform, aPlatform); +} + +NS_IMETHODIMP +nsHttpHandler::GetOscpu(char **aOscpu) +{ + return DupString(mOscpu, aOscpu); +} + +NS_IMETHODIMP +nsHttpHandler::GetLanguage(char **aLanguage) +{ + return DupString(mLanguage, aLanguage); +} +NS_IMETHODIMP +nsHttpHandler::SetLanguage(const char *aLanguage) +{ + mLanguage = aLanguage; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsHttpHandler::GetMisc(char **aMisc) +{ + return DupString(mMisc, aMisc); +} +NS_IMETHODIMP +nsHttpHandler::SetMisc(const char *aMisc) +{ + mMisc = aMisc; + mUserAgentIsDirty = PR_TRUE; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpHandler::nsIObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpHandler::Observe(nsISupports *subject, + const PRUnichar *topic, + const PRUnichar *data) +{ + if (!nsCRT::strcmp(topic, NS_LITERAL_STRING("profile-before-change").get())) { + // clear cache of all authentication credentials. + if (mAuthCache) + mAuthCache->ClearAll(); + + // need to reset the session start time since cache validation may + // depend on this value. + mSessionStartTime = NowInSeconds(); + } + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpHandler::nsPendingTransaction +//----------------------------------------------------------------------------- + +nsHttpHandler:: +nsPendingTransaction::nsPendingTransaction(nsHttpTransaction *trans, + nsHttpConnectionInfo *ci) + : mTransaction(trans) + , mConnectionInfo(ci) +{ + LOG(("Creating nsPendingTransaction @%x\n", this)); + + NS_PRECONDITION(mTransaction, "null transaction"); + NS_PRECONDITION(mConnectionInfo, "null connection info"); + + NS_ADDREF(mTransaction); + NS_ADDREF(mConnectionInfo); +} + +nsHttpHandler:: +nsPendingTransaction::~nsPendingTransaction() +{ + LOG(("Destroying nsPendingTransaction @%x\n", this)); + + NS_RELEASE(mTransaction); + NS_RELEASE(mConnectionInfo); +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHandler.h b/mozilla/netwerk/protocol/http/src/nsHttpHandler.h new file mode 100644 index 00000000000..01214649737 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpHandler.h @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpHandler_h__ +#define nsHttpHandler_h__ + +#include "nsHttp.h" +#include "nsIHttpProtocolHandler.h" +#include "nsIProtocolProxyService.h" +#include "nsIPref.h" +#include "nsIObserver.h" +#include "nsIProxyObjectManager.h" +#include "nsINetModuleMgr.h" +#include "nsIProxy.h" +#include "nsIStreamConverterService.h" +#include "nsICacheSession.h" +#include "nsIEventQueueService.h" +#include "nsXPIDLString.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsWeakReference.h" +#include "nsVoidArray.h" + +class nsHttpConnection; +class nsHttpConnectionInfo; +class nsHttpHeaderArray; +class nsHttpTransaction; +class nsHttpAuthCache; +class nsIHttpChannel; + +//----------------------------------------------------------------------------- +// nsHttpHandler - protocol handler for HTTP and HTTPS +//----------------------------------------------------------------------------- + +class nsHttpHandler : public nsIHttpProtocolHandler + , public nsIObserver + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPROTOCOLHANDLER + NS_DECL_NSIHTTPPROTOCOLHANDLER + NS_DECL_NSIOBSERVER + + enum { + ALLOW_KEEPALIVE = 1 << 0, + ALLOW_PIPELINING = 1 << 1 + }; + + nsHttpHandler(); + virtual ~nsHttpHandler(); + + // Implement our own create function so we can register ourselves as both + // the HTTP and HTTPS handler. + static NS_METHOD Create(nsISupports *outer, REFNSIID iid, void **result); + + // Returns a pointer to the one and only HTTP handler + static nsHttpHandler *get() { return mGlobalInstance; } + + nsresult Init(); + nsresult AddStandardRequestHeaders(nsHttpHeaderArray *, + PRUint32 capabilities); + + const char *UserAgent(); + nsHttpVersion DefaultVersion() { return (nsHttpVersion) mHttpVersion; } + PRUint32 ReferrerLevel() { return mReferrerLevel; } + + nsHttpAuthCache *AuthCache() { return mAuthCache; } + + // cache support + nsresult GetCacheSession(nsCacheStoragePolicy, nsICacheSession **); + PRUint32 GenerateUniqueID() { return ++mLastUniqueID; } + PRUint32 SessionStartTime() { return mSessionStartTime; } + + // + // Connection management methods: + // + // - the handler only owns idle connections; it does not own active + // connections. + // + // - the handler keeps a count of active connections to enforce the + // steady-state max-connections pref. + // + + // Called to kick-off a new transaction, by default the transaction + // will be put on the pending transaction queue if it cannot be + // initiated at this time. + nsresult InitiateTransaction(nsHttpTransaction *, + nsHttpConnectionInfo *, + PRBool failIfBusy = PR_FALSE); + + // Called when a connection is done processing a transaction + nsresult ReclaimConnection(nsHttpConnection *); + + // Called when a transaction, which is not assigned to a connection, + // is canceled. + nsresult CancelPendingTransaction(nsHttpTransaction *, nsresult status); + + // + // The HTTP handler caches pointers to specific XPCOM services, and + // provides the following helper routines for accessing those services: + // + nsresult GetProxyObjectManager(nsIProxyObjectManager **); + nsresult GetEventQueueService(nsIEventQueueService **); + nsresult GetStreamConverterService(nsIStreamConverterService **); + + // Called by the channel before writing a request + nsresult OnModifyRequest(nsIHttpChannel *); + + // Called by the channel once headers are available + nsresult OnExamineResponse(nsIHttpChannel *); + +private: + // + // Transactions that have not yet been assigned to a connection are kept + // in a queue of nsPendingTransaction objects. nsPendingTransaction + // implements nsAHttpTransactionSink to handle transaction Cancellation. + // + class nsPendingTransaction + { + public: + nsPendingTransaction(nsHttpTransaction *, nsHttpConnectionInfo *); + ~nsPendingTransaction(); + + nsHttpTransaction *Transaction() { return mTransaction; } + nsHttpConnectionInfo *ConnectionInfo() { return mConnectionInfo; } + + private: + nsHttpTransaction *mTransaction; + nsHttpConnectionInfo *mConnectionInfo; + }; + + // + // Transaction queue helper methods + // + void ProcessTransactionQ(); + nsresult EnqueueTransaction(nsHttpTransaction *, nsHttpConnectionInfo *); + + PRUint32 CountActiveConnections(nsHttpConnectionInfo *); + PRUint32 CountIdleConnections(nsHttpConnectionInfo *); + + void DropConnections(nsVoidArray &); + + // + // Useragent/prefs helper methods + // + void BuildUserAgent(); + void InitUserAgentComponents(); + void PrefsChanged(const char *pref = nsnull); + + nsresult SetAcceptLanguages(const char *); + nsresult SetAcceptEncodings(const char *); + nsresult SetAcceptCharsets(const char *); + + static PRInt32 PR_CALLBACK PrefsCallback(const char *, void *); + + nsresult CreateServicesFromCategory(const char *category); + +private: + static nsHttpHandler *mGlobalInstance; + + // cached services + nsCOMPtr mPrefs; + nsCOMPtr mProxyMgr; + nsCOMPtr mEventQueueService; + nsCOMPtr mNetModuleMgr; + nsCOMPtr mStreamConvSvc; + + // the authentication credentials cache + nsHttpAuthCache *mAuthCache; + + // + // prefs + // + + PRUint32 mHttpVersion; + PRUint32 mReferrerLevel; + PRUint32 mCapabilities; + PRUint32 mProxyCapabilities; + PRBool mProxySSLConnectAllowed; + + PRInt32 mConnectTimeout; + PRInt32 mRequestTimeout; + PRInt32 mIdleTimeout; + + PRInt32 mMaxConnections; + PRInt32 mMaxConnectionsPerServer; + PRInt32 mMaxIdleConnections; + PRInt32 mMaxIdleConnectionsPerServer; + + nsCString mAcceptLanguages; + nsCString mAcceptEncodings; + nsCString mAcceptCharsets; + + // cache support + nsCOMPtr mCacheSession_ANY; + nsCOMPtr mCacheSession_MEM; + PRUint32 mLastUniqueID; + PRUint32 mSessionStartTime; + + // connection management + nsVoidArray mActiveConnections; // list of nsHttpConnection objects + nsVoidArray mIdleConnections; // list of nsHttpConnection objects + nsVoidArray mTransactionQ; // list of nsPendingTransaction objects + + // useragent components + nsXPIDLCString mAppName; + nsXPIDLCString mAppVersion; + nsXPIDLCString mPlatform; + nsXPIDLCString mOscpu; + nsXPIDLCString mSecurity; + nsXPIDLCString mLanguage; + nsXPIDLCString mMisc; + nsXPIDLCString mVendor; + nsXPIDLCString mVendorSub; + nsXPIDLCString mVendorComment; + nsXPIDLCString mProduct; + nsXPIDLCString mProductSub; + nsXPIDLCString mProductComment; + + nsCString mUserAgent; + nsXPIDLCString mUserAgentOverride; + PRPackedBool mUserAgentIsDirty; // true if mUserAgent should be rebuilt +}; + +#endif // nsHttpHandler_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp new file mode 100644 index 00000000000..89eb064b526 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.cpp @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpHeaderArray.h" +#include "nsHttp.h" + +//----------------------------------------------------------------------------- +// nsHttpHeaderArray +//----------------------------------------------------------------------------- + +nsresult +nsHttpHeaderArray::SetHeader(nsHttpAtom header, const char *value) +{ + nsEntry *entry = nsnull; + PRInt32 index; + + // If a NULL value is passed in, then delete the header entry... + index = LookupEntry(header, &entry); + if (!value) { + if (entry) { + mHeaders.RemoveElementAt(index); + delete entry; + } + return NS_OK; + } + + // Create a new entry or... + if (!entry) { + entry = new nsEntry(header, value); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + if (!mHeaders.AppendElement(entry)) { + NS_WARNING("AppendElement failed"); + delete entry; + } + } + // Append the new value to the existing value iff... + else if (CanAppendToHeader(header)) { + if (header == nsHttp::Set_Cookie || + header == nsHttp::WWW_Authenticate || + header == nsHttp::Proxy_Authenticate) + // Special case these headers and use a newline delimiter to + // delimit the values from one another as commas may appear + // in the values of these headers contrary to what the spec says. + entry->value.Append('\n'); + else + // Delimit each value from the others using a comma (per HTTP spec) + entry->value.Append(", "); + entry->value.Append(value); + } + // Replace the existing string with the new value + else + entry->value = value; + return NS_OK; +} + +const char * +nsHttpHeaderArray::PeekHeader(nsHttpAtom header) +{ + nsEntry *entry = nsnull; + LookupEntry(header, &entry); + return entry ? entry->value.get() : nsnull; +} + +nsresult +nsHttpHeaderArray::GetHeader(nsHttpAtom header, char **result) +{ + const char *val = PeekHeader(header); + return val ? DupString(val, result) : NS_ERROR_NOT_AVAILABLE; +} + +nsresult +nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor) +{ + NS_ENSURE_ARG_POINTER(visitor); + PRInt32 i, count = mHeaders.Count(); + for (i=0; iVisitHeader(entry->header, entry->value.get()))) + break; + } + return NS_OK; +} + +nsresult +nsHttpHeaderArray::Flatten(nsACString &buf) +{ + PRInt32 i, count = mHeaders.Count(); + for (i=0; iheader); + buf.Append(": "); + buf.Append(entry->value); + buf.Append("\r\n"); + } + return NS_OK; +} + +const char * +nsHttpHeaderArray::PeekHeaderAt(PRUint32 index, nsHttpAtom &header) +{ + nsEntry *entry = (nsEntry *) mHeaders[index]; + if (!entry) + return nsnull; + + header = entry->header; + return entry->value.get(); +} + +void +nsHttpHeaderArray::Clear() +{ + PRInt32 i, count = mHeaders.Count(); + for (i=0; i +//----------------------------------------------------------------------------- + +PRInt32 +nsHttpHeaderArray::LookupEntry(nsHttpAtom header, nsEntry **entry) +{ + PRInt32 i, count = mHeaders.Count(); + for (i=0; iheader == header) + return i; + } + *entry = nsnull; + return -1; +} + +PRBool +nsHttpHeaderArray::CanAppendToHeader(nsHttpAtom header) +{ + return header == nsHttp::Accept_Charset || + header == nsHttp::User_Agent || + header == nsHttp::Referer || + header == nsHttp::Host || + header == nsHttp::Authorization || + header == nsHttp::If_Modified_Since || + header == nsHttp::If_Unmodified_Since || + header == nsHttp::From || + header == nsHttp::Location || + header == nsHttp::Max_Forwards + ? + PR_FALSE : PR_TRUE; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h new file mode 100644 index 00000000000..03df11c1b5a --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpHeaderArray.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpHeaderArray_h__ +#define nsHttpHeaderArray_h__ + +#include "nsVoidArray.h" +#include "nsIHttpChannel.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsHttp.h" + +class nsHttpHeaderArray +{ +public: + nsHttpHeaderArray() {} + ~nsHttpHeaderArray() { Clear(); } + + const char *PeekHeader(nsHttpAtom header); + + nsresult SetHeader(nsHttpAtom header, const char *value); + nsresult GetHeader(nsHttpAtom header, char **value); + + nsresult VisitHeaders(nsIHttpHeaderVisitor *visitor); + + nsresult Flatten(nsACString &); + + PRUint32 Count() { return (PRUint32) mHeaders.Count(); } + + const char *PeekHeaderAt(PRUint32 i, nsHttpAtom &header); + + void Clear(); + +private: + struct nsEntry + { + nsEntry(nsHttpAtom h, const char *v) + : header(h) { value = v; } + + nsHttpAtom header; + nsCString value; + }; + + PRInt32 LookupEntry(nsHttpAtom header, nsEntry **); + PRBool CanAppendToHeader(nsHttpAtom header); + +private: + nsVoidArray mHeaders; +}; + +#endif diff --git a/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.cpp b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.cpp new file mode 100644 index 00000000000..0fbbf1d373e --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.cpp @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpRequestHead.h" + +//----------------------------------------------------------------------------- +// nsHttpRequestHead +//----------------------------------------------------------------------------- + +nsresult +nsHttpRequestHead::Flatten(nsACString &buf) +{ + // note: the first append is intentional. + + buf.Append(mMethod.get()); + buf.Append(' '); + buf.Append(mRequestURI); + buf.Append(" HTTP/"); + + switch (mVersion) { + case NS_HTTP_VERSION_1_1: + buf.Append("1.1"); + break; + case NS_HTTP_VERSION_0_9: + buf.Append("0.9"); + break; + default: + buf.Append("1.0"); + } + + buf.Append("\r\n"); + + mHeaders.Flatten(buf); + + // do not append the final \r\n + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h new file mode 100644 index 00000000000..c7f62f48a3d --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpRequestHead.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpRequestHead_h__ +#define nsHttpRequestHead_h__ + +#include "nsHttpHeaderArray.h" +#include "nsHttp.h" +#include "nsXPIDLString.h" + +//----------------------------------------------------------------------------- +// nsHttpRequestHead represents the request line and headers from an HTTP +// request. +//----------------------------------------------------------------------------- + +class nsHttpRequestHead +{ +public: + nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {} + ~nsHttpRequestHead() {} + + void SetMethod(nsHttpAtom method) { mMethod = method; } + void SetVersion(nsHttpVersion version) { mVersion = version; } + void SetRequestURI(const char *s) { mRequestURI = s; } + + nsHttpHeaderArray &Headers() { return mHeaders; } + nsHttpAtom Method() { return mMethod; } + nsHttpVersion Version() { return mVersion; } + const char *RequestURI() { return mRequestURI; } + + const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); } + nsresult SetHeader(nsHttpAtom h, const char *v) { return mHeaders.SetHeader(h, v); } + nsresult GetHeader(nsHttpAtom h, char **v) { return mHeaders.GetHeader(h, v); } + void ClearHeaders() { mHeaders.Clear(); } + + nsresult Flatten(nsACString &); + +private: + nsHttpHeaderArray mHeaders; + nsHttpAtom mMethod; + nsHttpVersion mVersion; + nsXPIDLCString mRequestURI; +}; + +#endif // nsHttpRequestHead_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp new file mode 100644 index 00000000000..d95d2ce7d70 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.cpp @@ -0,0 +1,472 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include +#include "nsHttpResponseHead.h" +#include "prprf.h" +#include "prtime.h" + +//----------------------------------------------------------------------------- +// nsHttpResponseHead +//----------------------------------------------------------------------------- + +nsresult +nsHttpResponseHead::Flatten(nsACString &buf, PRBool pruneTransients) +{ + if (mVersion == NS_HTTP_VERSION_0_9) + return NS_OK; + + buf.Append("HTTP/"); + if (mVersion == NS_HTTP_VERSION_1_1) + buf.Append("1.1 "); + else + buf.Append("1.0 "); + + char b[32]; + PR_snprintf(b, sizeof(b), "%d", mStatus); + + buf.Append(b); + buf.Append(' '); + buf.Append(mStatusText); + buf.Append("\r\n"); + + if (!pruneTransients) + return mHeaders.Flatten(buf); + + // otherwise, we need to iterate over the headers and only flatten + // those that are appropriate. + PRUint32 i, count = mHeaders.Count(); + for (i=0; i dateValue) + *result = now - dateValue; + + // Compute corrected received age + if (NS_SUCCEEDED(GetAgeValue(&ageValue))) + *result = PR_MAX(*result, ageValue); + + NS_ASSERTION(now >= requestTime, "bogus request time"); + + // Compute current age + *result += (now - requestTime); + return NS_OK; +} + +// From section 13.2.4 of RFC2616, we compute the freshness lifetime of a cached +// response as follows: +// +// freshnessLifetime = max_age_value +// +// freshnessLifetime = expires_value - date_value +// +// freshnessLifetime = (date_value - last_modified_value) * 0.10 +// +// freshnessLifetime = 0 +// +nsresult +nsHttpResponseHead::ComputeFreshnessLifetime(PRUint32 *result) +{ + *result = 0; + + // Try HTTP/1.1 style max-age directive... + if (NS_SUCCEEDED(GetMaxAgeValue(result))) + return NS_OK; + + PRUint32 date, date2; + + if (NS_SUCCEEDED(GetDateValue(&date))) { + // Try HTTP/1.0 style expires header... + if (NS_SUCCEEDED(GetExpiresValue(&date2))) { + *result = date2 - date; + return NS_OK; + } + + // Fallback on heuristic using last modified header... + if (NS_SUCCEEDED(GetLastModifiedValue(&date2))) { + LOG(("using last-modified to determine freshness-lifetime\n")); + LOG(("last-modified = %u, date = %u\n", date2, date)); + *result = (date - date2) / 10; + return NS_OK; + } + } + + LOG(("nsHttpResponseHead::ComputeFreshnessLifetime [this = %x] " + "Insufficient information to compute a non-zero freshness " + "lifetime!\n", this)); + + return NS_OK; +} + +nsresult +nsHttpResponseHead::UpdateHeaders(nsHttpHeaderArray &headers) +{ + LOG(("nsHttpResponseHead::UpdateHeaders [this=%x]\n", this)); + + PRUint32 i, count = headers.Count(); + for (i=0; i +//----------------------------------------------------------------------------- + +void +nsHttpResponseHead::ParseVersion(const char *str) +{ + // Parse HTTP-Version:: "HTTP" "/" 1*DIGIT "." 1*DIGIT + + // make sure we have HTTP at the beginning + if (PL_strncmp(str, "HTTP", 4) != 0) { + LOG(("looks like a HTTP/0.9 response\n")); + mVersion = NS_HTTP_VERSION_0_9; + return; + } + str += 4; + + if (*str != '/') { + LOG(("server did not send a version number; assuming HTTP/1.0\n")); + // NCSA/1.5.2 has a bug in which it fails to send a version number + // if the request version is HTTP/1.1, so we fall back on HTTP/1.0 + mVersion = NS_HTTP_VERSION_1_0; + return; + } + + char *p = PL_strchr(str, '.'); + if (p == nsnull) { + LOG(("mal-formed server version; assuming HTTP/1.0\n")); + mVersion = NS_HTTP_VERSION_1_0; + return; + } + + ++p; // let b point to the minor version + + int major = atoi(str + 1); + int minor = atoi(p); + + if ((major > 1) || ((major == 1) && (minor >= 1))) + // at least HTTP/1.1 + mVersion = NS_HTTP_VERSION_1_1; + else + // treat anything else as version 1.0 + mVersion = NS_HTTP_VERSION_1_0; +} + +nsresult +nsHttpResponseHead::ParseContentType(char *type) +{ + char *p = PL_strchr(type, '('); + + if (p) { + // we don't care about comments + *p = 0; + // trim any trailing whitespace + do { + --p; + } while ((*p == ' ') && (p > type)); + } + + if (p != type) { + // check if the content-type has additional fields... + if ((p = PL_strchr(type, ';')) != nsnull) { + // null out the ';' char + *p = 0; + // is there a charset field? + if ((p = PL_strstr(p, "charset=")) != nsnull) + mContentCharset = p + 8; + } + mContentType = type; + } + + return NS_OK; +} + +nsresult +nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, PRUint32 *result) +{ + const char *val = PeekHeader(header); + if (!val) + return NS_ERROR_NOT_AVAILABLE; + + PRTime time; + PRStatus st = PR_ParseTimeString(val, PR_TRUE, &time); + if (st != PR_SUCCESS) + return NS_ERROR_NOT_AVAILABLE; + + *result = PRTimeToSeconds(time); + return NS_OK; +} + +nsresult +nsHttpResponseHead::GetAgeValue(PRUint32 *result) +{ + const char *val = PeekHeader(nsHttp::Age); + if (!val) + return NS_ERROR_NOT_AVAILABLE; + + *result = (PRUint32) atoi(val); + return NS_OK; +} + +// Return the value of the (HTTP 1.1) max-age directive, which itself is a +// component of the Cache-Control response header +nsresult nsHttpResponseHead::GetMaxAgeValue(PRUint32 *result) +{ + const char *val = PeekHeader(nsHttp::Cache_Control); + if (!val) + return NS_ERROR_NOT_AVAILABLE; + + const char *p = PL_strstr(val, "max-age="); + if (!p) + return NS_ERROR_NOT_AVAILABLE; + + *result = (PRUint32) atoi(p + 8); + return NS_OK; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h new file mode 100644 index 00000000000..92cc0b5fb42 --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpResponseHead.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpResponseHead_h__ +#define nsHttpResponseHead_h__ + +#include "nsHttpHeaderArray.h" +#include "nsHttp.h" +#include "nsXPIDLString.h" + +//----------------------------------------------------------------------------- +// nsHttpResponseHead represents the status line and headers from an HTTP +// response. +//----------------------------------------------------------------------------- + +class nsHttpResponseHead +{ +public: + nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1) + , mStatus(200) + , mContentLength(-1) {} + ~nsHttpResponseHead() {} + + nsHttpHeaderArray &Headers() { return mHeaders; } + nsHttpVersion Version() { return mVersion; } + PRUint32 Status() { return mStatus; } + const char *StatusText() { return mStatusText; } + PRInt32 ContentLength() { return mContentLength; } + const char *ContentType() { return mContentType; } + const char *ContentCharset() { return mContentCharset; } + + const char *PeekHeader(nsHttpAtom h) { return mHeaders.PeekHeader(h); } + nsresult SetHeader(nsHttpAtom h, const char *v) { return mHeaders.SetHeader(h, v); } + nsresult GetHeader(nsHttpAtom h, char **v) { return mHeaders.GetHeader(h, v); } + void ClearHeaders() { mHeaders.Clear(); } + + // write out the response status line and headers as a single text block, + // optionally pruning out transient headers (ie. headers that only make + // sense the first time the response is handled). + nsresult Flatten(nsACString &, PRBool pruneTransients); + + // parse flattened response head. block must be null terminated. parsing is + // destructive. + nsresult Parse(char *block); + + // parse the status line. line must be null terminated. + void ParseStatusLine(char *line); + + // parse a header line. line must be null terminated. parsing is destructive. + void ParseHeaderLine(char *line); + + // cache validation support methods + nsresult ComputeFreshnessLifetime(PRUint32 *); + nsresult ComputeCurrentAge(PRUint32 now, PRUint32 requestTime, PRUint32 *result); + + // update headers... + nsresult UpdateHeaders(nsHttpHeaderArray &headers); + + // reset the response head to it's initial state + void Reset(); + +private: + void ParseVersion(const char *); + nsresult ParseContentType(char *); + + // these return failure if the header does not exist. + nsresult ParseDateHeader(nsHttpAtom header, PRUint32 *result); + nsresult GetAgeValue(PRUint32 *result); + nsresult GetMaxAgeValue(PRUint32 *result); + nsresult GetDateValue(PRUint32 *result) { return ParseDateHeader(nsHttp::Date, result); } + nsresult GetExpiresValue(PRUint32 *result) { return ParseDateHeader(nsHttp::Expires, result); } + nsresult GetLastModifiedValue(PRUint32 *result) { return ParseDateHeader(nsHttp::Last_Modified, result); } + +private: + nsHttpHeaderArray mHeaders; + nsHttpVersion mVersion; + PRUint32 mStatus; + nsXPIDLCString mStatusText; + PRInt32 mContentLength; + nsXPIDLCString mContentType; + nsXPIDLCString mContentCharset; +}; + +#endif // nsHttpResponseHead_h__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp new file mode 100644 index 00000000000..94bbc3920ad --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp @@ -0,0 +1,606 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#include "nsHttpHandler.h" +#include "nsHttpTransaction.h" +#include "nsHttpConnection.h" +#include "nsHttpRequestHead.h" +#include "nsHttpResponseHead.h" +#include "nsHttpChunkedDecoder.h" +#include "nsIStringStream.h" +#include "pratom.h" + +//----------------------------------------------------------------------------- +// nsHttpTransaction +//----------------------------------------------------------------------------- + +nsHttpTransaction::nsHttpTransaction(nsIStreamListener *listener, + nsIInterfaceRequestor *callbacks) + : mListener(listener) + , mCallbacks(callbacks) + , mConnection(nsnull) + , mResponseHead(nsnull) + , mContentLength(-1) + , mContentRead(0) + , mChunkedDecoder(nsnull) + , mTransactionDone(0) + , mStatus(NS_OK) + , mHaveStatusLine(PR_FALSE) + , mHaveAllHeaders(PR_FALSE) + , mFiredOnStart(PR_FALSE) + , mNoContent(PR_FALSE) +{ + LOG(("Creating nsHttpTransaction @%x\n", this)); + + NS_INIT_ISUPPORTS(); + + NS_PRECONDITION(listener, "null listener"); +} + +nsHttpTransaction::~nsHttpTransaction() +{ + LOG(("Destroying nsHttpTransaction @%x\n", this)); + + if (mConnection) { + nsHttpHandler::get()->ReclaimConnection(mConnection); + NS_RELEASE(mConnection); + } + + if (mChunkedDecoder) + delete mChunkedDecoder; +} + +nsresult +nsHttpTransaction::SetupRequest(nsHttpRequestHead *requestHead, + nsIInputStream *requestStream) +{ + nsresult rv; + + LOG(("nsHttpTransaction::SetupRequest [this=%x]\n", this)); + + NS_ENSURE_ARG_POINTER(requestHead); + + if (requestHead->Method() == nsHttp::Head) + mNoContent = PR_TRUE; + + mReqHeaderBuf.SetLength(0); + + rv = requestHead->Flatten(mReqHeaderBuf); + if (NS_FAILED(rv)) return rv; + + LOG2(("http request [\n%s]\n", mReqHeaderBuf.get())); + + mReqUploadStream = requestStream; + if (!mReqUploadStream) + // Write out end-of-headers sequence if NOT uploading data: + mReqHeaderBuf.Append("\r\n"); + + // Create a string stream for the request header buf + nsCOMPtr sup; + rv = NS_NewCStringInputStream(getter_AddRefs(sup), mReqHeaderBuf); + if (NS_FAILED(rv)) return rv; + mReqHeaderStream = do_QueryInterface(sup, &rv); + + return rv; +} + +nsresult +nsHttpTransaction::SetConnection(nsHttpConnection *conn) +{ + NS_ENSURE_ARG_POINTER(conn); + NS_ENSURE_TRUE(!mConnection, NS_ERROR_ALREADY_INITIALIZED); + + mConnection = conn; + NS_ADDREF(mConnection); + + return NS_OK; +} + +nsHttpResponseHead * +nsHttpTransaction::TakeResponseHead() +{ + nsHttpResponseHead *head = mResponseHead; + mResponseHead = nsnull; + return head; +} + +// called on the socket transport thread +nsresult +nsHttpTransaction::OnDataWritable(nsIOutputStream *os) +{ + PRUint32 n = 0; + + LOG(("nsHttpTransaction::OnDataWritable [this=%x]\n", this)); + + // check if we're done writing the headers + nsresult rv = mReqHeaderStream->Available(&n); + if (NS_FAILED(rv)) return rv; + + // let at most NS_HTTP_BUFFER_SIZE bytes be written at a time. + + if (n != 0) + return os->WriteFrom(mReqHeaderStream, NS_HTTP_BUFFER_SIZE, &n); + + if (mReqUploadStream) + return os->WriteFrom(mReqUploadStream, NS_HTTP_BUFFER_SIZE, &n); + + return NS_BASE_STREAM_CLOSED; +} + +// called on the socket transport thread +nsresult +nsHttpTransaction::OnDataReadable(nsIInputStream *is) +{ + nsresult rv; + + LOG(("nsHttpTransaction::OnDataReadable [this=%x]\n", this)); + + if (!mListener) { + LOG(("nsHttpTransaction: no listener! closing stream\n")); + return NS_BASE_STREAM_CLOSED; + } + + mSource = is; + + // let our listener try to read up to NS_HTTP_BUFFER_SIZE from us. + rv = mListener->OnDataAvailable(this, nsnull, this, + mContentRead, NS_HTTP_BUFFER_SIZE); + + LOG(("nsHttpTransaction: listener returned [rv=%x]\n", rv)); + + mSource = 0; + return rv; +} + +// called on the socket transport thread +nsresult +nsHttpTransaction::OnStopTransaction(nsresult status) +{ + LOG(("nsHttpTransaction::OnStopTransaction [this=%x status=%x]\n", + this, status)); + + // make sure that this flag is set. + mTransactionDone = PR_TRUE; + mStatus = status; + + if (mListener) { + if (!mFiredOnStart) { + mFiredOnStart = PR_TRUE; + mListener->OnStartRequest(this, nsnull); + } + mListener->OnStopRequest(this, nsnull, status); + mListener = 0; + } + return NS_OK; +} + +nsresult +nsHttpTransaction::ParseLine(char *line) +{ + NS_PRECONDITION(!mHaveAllHeaders, "already have all headers"); + + LOG(("nsHttpTransaction::ParseLine [%s]\n", line)); + + // allocate the response head object if necessary + if (!mResponseHead) { + mResponseHead = new nsHttpResponseHead(); + if (!mResponseHead) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!mHaveStatusLine) { + mResponseHead->ParseStatusLine(line); + mHaveStatusLine = PR_TRUE; + } + else if (*line == '\0') + mHaveAllHeaders = PR_TRUE; + else + mResponseHead->ParseHeaderLine(line); + return NS_OK; +} + +nsresult +nsHttpTransaction::ParseHead(char *buf, + PRUint32 count, + PRUint32 *countRead) +{ + char *eol; + + LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count)); + + *countRead = 0; + + NS_PRECONDITION(!mHaveAllHeaders, "oops"); + + while ((eol = PL_strnchr(buf, '\n', count - *countRead)) != nsnull) { + // found line in range [buf:eol] + *eol = 0; + + // actually, in range [buf:eol-1] + if ((eol > buf) && (*(eol-1) == '\r')) + *(eol-1) = 0; + + // we may have a partial line to complete... + if (!mLineBuf.IsEmpty()) { + mLineBuf.Append(buf); + ParseLine((char *) mLineBuf.get()); + mLineBuf.SetLength(0); + } + else + ParseLine(buf); + + *countRead += (eol + 1 - buf); + NS_ASSERTION(*countRead <= count, "oops"); + + // skip over line + buf = eol + 1; + + if (mHaveAllHeaders) + break; + } + + if (!mHaveAllHeaders && (count > *countRead)) { + // remember this partial line + mLineBuf.Assign(buf, count - *countRead); + *countRead = count; + + LOG(("partial line [%s]\n", mLineBuf.get())); + } + + // read something + return NS_OK; +} + +nsresult +nsHttpTransaction::HandleContent(char *buf, + PRUint32 count, + PRUint32 *countRead) +{ + nsresult rv; + + LOG(("nsHttpTransaction::HandleContent [this=%x count=%u]\n", + this, count)); + + *countRead = 0; + + if (mTransactionDone) + return NS_OK; + + NS_PRECONDITION(mConnection, "no connection"); + + if (!mFiredOnStart) { + if (mResponseHead) { +#if defined(PR_LOGGING) + nsCAutoString headers; + mResponseHead->Flatten(headers, PR_FALSE); + LOG2(("http response [\n%s]\n", headers.get())); +#endif + PRBool reset = PR_FALSE; + + // we ignore a 100 response, which means that we must reset + // our state so that we'll parse from the beginning again. + if (mResponseHead->Status() == 100) { + LOG(("ignoring 100 response\n")); + reset = PR_TRUE; + } + // notify the connection, give it a chance to cause a reset. + else if (mConnection) + mConnection->OnHeadersAvailable(this, &reset); + + // looks like we should ignore this response, resetting... + if (reset) { + LOG(("resetting transaction's response head\n")); + mHaveAllHeaders = PR_FALSE; + mHaveStatusLine = PR_FALSE; + mResponseHead->Reset(); + // wait to be called again... + return NS_BASE_STREAM_WOULD_BLOCK; + } + + // grab the content-length from the response headers + mContentLength = mResponseHead->ContentLength(); + + // handle chunked encoding here, so we'll know immediately when + // we're done with the socket. please note that _all_ other + // decoding is done when the channel receives the content data + // so as not to block the socket transport thread too much. + const char *val = + mResponseHead->PeekHeader(nsHttp::Transfer_Encoding); + if (val) { + // we only support the "chunked" transfer encoding right now. + mChunkedDecoder = new nsHttpChunkedDecoder(); + if (!mChunkedDecoder) + return NS_ERROR_OUT_OF_MEMORY; + LOG(("chunked decoder created\n")); + + val = mResponseHead->PeekHeader(nsHttp::Trailer); + NS_ASSERTION(!val, "FIXME: unhandled trailer header present!"); + } + else if (mContentLength == -1) { + // check if this is a no-content response + switch (mResponseHead->Status()) { + case 204: + case 205: + case 304: + mNoContent = PR_TRUE; + LOG(("looks like this response should not contain a body.\n")); + break; + } + if (mNoContent) + mContentLength = 0; + // otherwise, we'll wait for the server to close the connection. + } + } + + LOG(("nsHttpTransaction [this=%x] sending OnStartRequest\n", this)); + mFiredOnStart = PR_TRUE; + + rv = mListener->OnStartRequest(this, nsnull); + if (NS_FAILED(rv)) { + LOG(("OnStartRequest failed with [rv=%x]\n", rv)); + return rv; + } + } + + if (mChunkedDecoder) { + // give the buf over to the chunked decoder so it can reformat the + // data and tell us how much is really there. + rv = mChunkedDecoder->HandleChunkedContent(buf, count, countRead); + if (NS_FAILED(rv)) return rv; + } + else if (mContentLength >= 0) + // when a content length has been specified... + *countRead = PR_MIN(count, mContentLength - mContentRead); + else + // when we are just waiting for the server to close the connection... + *countRead = count; + + if (*countRead) { + // update count of content bytes read.. + mContentRead += *countRead; + // and report progress + mConnection->ReportProgress(mContentRead, mContentLength); + } + + LOG(("nsHttpTransaction [this=%x count=%u read=%u mContentRead=%u mContentLength=%d]\n", + this, count, *countRead, mContentRead, mContentLength)); + + // check for end-of-file + if ((mContentRead == PRUint32(mContentLength)) || + (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) { + // atomically mark the transaction as complete to ensure that + // OnTransactionComplete is fired only once! + PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1); + if (priorVal == 0 && mConnection) { + // let the connection know that we are done with it; this should + // result in OnStopTransaction being fired. + return mConnection->OnTransactionComplete(NS_OK); + } + return NS_OK; + } + + // if we didn't "read" anything and this is not a no-content response, + // then we must return NS_BASE_STREAM_WOULD_BLOCK so we'll be called again. + return (!mNoContent && !*countRead) ? NS_BASE_STREAM_WOULD_BLOCK : NS_OK; +} + +//----------------------------------------------------------------------------- +// nsHttpTransaction::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsHttpTransaction, + nsIRequest, + nsIInputStream) + +//----------------------------------------------------------------------------- +// nsHttpTransaction::nsIRequest +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpTransaction::GetName(PRUnichar **aName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::IsPending(PRBool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::GetStatus(nsresult *aStatus) +{ + *aStatus = mStatus; + return NS_OK; +} + +// called from any thread +NS_IMETHODIMP +nsHttpTransaction::Cancel(nsresult status) +{ + LOG(("nsHttpTransaction::Cancel [this=%x status=%x]\n", this, status)); + + // if the transaction is already "done" then there is nothing more to do. + // ie., our consumer _will_ eventually receive their OnStopRequest. + if (mTransactionDone) { + LOG(("ignoring cancel since transaction is already done [this=%x]\n", this)); + return NS_OK; + } + + // the status must be set immediately as the following routines may + // take action asynchronously. + mStatus = status; + + if (!mConnection) { + // the connection is not assigned to a connection yet, so we must + // notify the HTTP handler, so it can process the cancelation. + return nsHttpHandler::get()->CancelPendingTransaction(this, status); + } + + // atomically cancel the connection. it's important to consider that + // the socket thread could already be in the middle of processing + // completion, in which case we should ignore this cancelation request. + + PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1); + if (priorVal == 0) + mConnection->OnTransactionComplete(status); + + return NS_OK; +} + +NS_IMETHODIMP +nsHttpTransaction::Suspend() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::Resume() +{ + LOG(("nsHttpTransaction::Resume [this=%x]\n", this)); + if (mConnection) + mConnection->Resume(); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpTransaction::GetLoadGroup(nsILoadGroup **aLoadGroup) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +nsHttpTransaction::SetLoadGroup(nsILoadGroup *aLoadGroup) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::GetLoadFlags(nsLoadFlags *aLoadFlags) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +nsHttpTransaction::SetLoadFlags(nsLoadFlags aLoadFlags) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +//----------------------------------------------------------------------------- +// nsHttpTransaction::nsIInputStream +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsHttpTransaction::Close() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::Available(PRUint32 *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::Read(char *buf, PRUint32 count, PRUint32 *bytesWritten) +{ + nsresult rv; + + LOG(("nsHttpTransaction::Read [this=%x count=%u]\n", this, count)); + + NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED); + + if (mTransactionDone) + return NS_BASE_STREAM_CLOSED; + + // read some data from our source and put it in the given buf + rv = mSource->Read(buf, count, bytesWritten); + if (NS_FAILED(rv) || (*bytesWritten == 0)) { + LOG(("nsHttpTransaction: mSource->Read() returned [rv=%x]\n", rv)); + return rv; + } + + // pretend that no bytes were written (since we're just borrowing the + // given buf anyways). + count = *bytesWritten; + *bytesWritten = 0; + + // we may not have read all of the headers yet... + if (!mHaveAllHeaders) { + PRUint32 offset = 0, bytesConsumed; + + while (count) { + bytesConsumed = 0; + + rv = ParseHead(buf + offset, count, &bytesConsumed); + if (NS_FAILED(rv)) return rv; + + count -= bytesConsumed; + offset += bytesConsumed; + + // see if we're done reading headers + if (mHaveAllHeaders) { + LOG(("have all response headers\n")); + break; + } + } + + if (count) { + // buf has some content in it; shift bytes to top of buf. + memmove(buf, buf + offset, count); + } + } + + // even though count may be 0, we still want to call HandleContent + // so it can complete the transaction if this is a "no-content" response. + if (mHaveAllHeaders) + return HandleContent(buf, count, bytesWritten); + + // wait for more data + return NS_BASE_STREAM_WOULD_BLOCK; +} + +NS_IMETHODIMP +nsHttpTransaction::ReadSegments(nsWriteSegmentFun writer, void *closure, + PRUint32 count, PRUint32 *countRead) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::GetNonBlocking(PRBool *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsHttpTransaction::GetObserver(nsIInputStreamObserver **obs) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +nsHttpTransaction::SetObserver(nsIInputStreamObserver *obs) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h new file mode 100644 index 00000000000..1c1af8a3c8c --- /dev/null +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is Netscape + * Communications. Portions created by Netscape Communications are + * Copyright (C) 2001 by Netscape Communications. All + * Rights Reserved. + * + * Contributor(s): + * Darin Fisher (original author) + */ + +#ifndef nsHttpTransaction_h__ +#define nsHttpTransaction_h__ + +#include "nsHttp.h" +#include "nsHttpHeaderArray.h" +#include "nsIStreamListener.h" +#include "nsIInputStream.h" +#include "nsIInterfaceRequestor.h" +#include "nsXPIDLString.h" +#include "nsCOMPtr.h" + +class nsHttpRequestHead; +class nsHttpResponseHead; +class nsHttpConnection; +class nsHttpChunkedDecoder; + +//----------------------------------------------------------------------------- +// nsHttpTransaction represents a single HTTP transaction. It is thread-safe, +// intended to run on the socket thread. +//----------------------------------------------------------------------------- + +class nsHttpTransaction : public nsIRequest + , public nsIInputStream +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUEST + NS_DECL_NSIINPUTSTREAM + + // A transaction is constructed from request headers. + nsHttpTransaction(nsIStreamListener *, nsIInterfaceRequestor *); + virtual ~nsHttpTransaction(); + + nsrefcnt RefCnt() { return mRefCnt; } + + // Called when assigned to a connection + nsresult SetConnection(nsHttpConnection *); + + // Called to initialize the transaction + nsresult SetupRequest(nsHttpRequestHead *, nsIInputStream *); + + nsIStreamListener *Listener() { return mListener; } + nsHttpConnection *Connection() { return mConnection; } + nsHttpResponseHead *ResponseHead() { return mResponseHead; } + nsIInterfaceRequestor *Callbacks() { return mCallbacks; } + + // Called to take ownership of the response headers; the transaction + // will drop any reference to the response headers after this call. + nsHttpResponseHead *TakeResponseHead(); + + // Called to write data to the socket until return NS_BASE_STREAM_CLOSED + nsresult OnDataWritable(nsIOutputStream *); + + // Called to read data from the socket buffer + nsresult OnDataReadable(nsIInputStream *); + + // Called when the transaction should stop, possibly prematurely with an error. + nsresult OnStopTransaction(nsresult); + +private: + nsresult ParseLine(char *line); + nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead); + nsresult HandleContent(char *, PRUint32 count, PRUint32 *countRead); + +private: + nsCOMPtr mListener; + nsCOMPtr mCallbacks; + + nsHttpConnection *mConnection; // hard ref + + nsCString mReqHeaderBuf; // flattened request headers + nsCOMPtr mReqHeaderStream; // header data stream + nsCOMPtr mReqUploadStream; // upload data stream + + nsCOMPtr mSource; + nsHttpResponseHead *mResponseHead; + + nsCString mLineBuf; // may contain a partial line + + PRInt32 mContentLength; // equals -1 if unknown + PRUint32 mContentRead; // count of consumed content bytes + + nsHttpChunkedDecoder *mChunkedDecoder; + + PRInt32 mTransactionDone; // set atomically + nsresult mStatus; + + PRPackedBool mHaveStatusLine; + PRPackedBool mHaveAllHeaders; + PRPackedBool mFiredOnStart; + PRPackedBool mNoContent; // true if we're expecting an empty entity body +}; + +#endif // nsHttpTransaction_h__ diff --git a/mozilla/netwerk/socket/base/nsSOCKSIOLayer.cpp b/mozilla/netwerk/socket/base/nsSOCKSIOLayer.cpp index 78338ccbbcb..8dcabbe9ee6 100644 --- a/mozilla/netwerk/socket/base/nsSOCKSIOLayer.cpp +++ b/mozilla/netwerk/socket/base/nsSOCKSIOLayer.cpp @@ -38,12 +38,17 @@ public: virtual ~nsSOCKSSocketInfo(); NS_DECL_ISUPPORTS - NS_DECL_NSIPROXY NS_DECL_NSISOCKSSOCKETINFO + NS_IMETHOD GetProxyHost(char * *aProxyHost); + NS_IMETHOD SetProxyHost(const char * aProxyHost); + NS_IMETHOD GetProxyPort(PRInt32 *aProxyPort); + NS_IMETHOD SetProxyPort(PRInt32 aProxyPort); + NS_IMETHOD GetProxyType(char * *aProxyType); + NS_IMETHOD SetProxyType(const char * aProxyType); + protected: - // nsIProxy char* mProxyHost; PRInt32 mProxyPort; char* mProxyType; @@ -72,7 +77,7 @@ nsSOCKSSocketInfo::~nsSOCKSSocketInfo() PR_FREEIF(mProxyHost); } -NS_IMPL_THREADSAFE_ISUPPORTS2(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIProxy) +NS_IMPL_THREADSAFE_ISUPPORTS1(nsSOCKSSocketInfo, nsISOCKSSocketInfo); NS_IMETHODIMP nsSOCKSSocketInfo::GetProxyHost(char * *aProxyHost) diff --git a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp index 9aacdd25277..3a83a542707 100644 --- a/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsFTPDirListingConv.cpp @@ -24,7 +24,6 @@ #include "nsMemory.h" #include "plstr.h" #include "prlog.h" -#include "nsIHTTPChannel.h" #include "nsIAtom.h" #include "nsIServiceManager.h" #include "nsIGenericFactory.h" diff --git a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp index 1d1f623badd..343de53f495 100644 --- a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -23,7 +23,7 @@ #include "nsMultiMixedConv.h" #include "nsMemory.h" #include "plstr.h" -#include "nsIHTTPChannel.h" +#include "nsIHttpChannel.h" #include "nsIAtom.h" #include "nsIServiceManager.h" #include "nsNetUtil.h" @@ -255,11 +255,9 @@ nsMultiMixedConv::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { if (NS_FAILED(rv)) return rv; // ask the HTTP channel for the content-type and extract the boundary from it. - nsCOMPtr httpChannel = do_QueryInterface(channel, &rv); + nsCOMPtr httpChannel = do_QueryInterface(channel, &rv); if (NS_SUCCEEDED(rv)) { - nsCOMPtr header(dont_AddRef(NS_NewAtom("content-type"))); - if (!header) return NS_ERROR_OUT_OF_MEMORY; - rv = httpChannel->GetResponseHeader(header, getter_Copies(delimiter)); + rv = httpChannel->GetResponseHeader("content-type", getter_Copies(delimiter)); if (NS_FAILED(rv)) return rv; } else { // try asking the channel directly @@ -490,9 +488,6 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, *colon = '\0'; nsCAutoString headerStr(cursor); headerStr.CompressWhitespace(); - headerStr.ToLowerCase(); - nsCOMPtr header(dont_AddRef(NS_NewAtom(headerStr.get()))); - if (!header) return NS_ERROR_OUT_OF_MEMORY; *colon = ':'; nsCAutoString headerVal(colon + 1); @@ -507,9 +502,9 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, // setting headers on the HTTP channel // causes HTTP to notify, again if necessary, // it's header observers. - nsCOMPtr httpChannel = do_QueryInterface(aChannel); + nsCOMPtr httpChannel = do_QueryInterface(aChannel); if (httpChannel) { - rv = httpChannel->SetResponseHeader(header, headerVal); + rv = httpChannel->SetResponseHeader(headerStr, headerVal); if (NS_FAILED(rv)) return rv; } } diff --git a/mozilla/netwerk/test/Makefile.in b/mozilla/netwerk/test/Makefile.in index fb6327d4f89..628c23c18a3 100644 --- a/mozilla/netwerk/test/Makefile.in +++ b/mozilla/netwerk/test/Makefile.in @@ -39,11 +39,11 @@ CPPSRCS = \ TestFileInput.cpp \ TestFileTransport.cpp \ TestProtocols.cpp \ + TestHttp.cpp \ urltest.cpp \ TestFileInput2.cpp \ TestRes.cpp \ TestCallbacks.cpp \ - TestSyncHTTP.cpp \ TestDBMAccess.cpp \ $(NULL) diff --git a/mozilla/netwerk/test/TestProtocols.cpp b/mozilla/netwerk/test/TestProtocols.cpp index 0c47105c07f..cb38e405b8c 100644 --- a/mozilla/netwerk/test/TestProtocols.cpp +++ b/mozilla/netwerk/test/TestProtocols.cpp @@ -44,13 +44,12 @@ #include "nsCRT.h" #include "nsIChannel.h" #include "nsIURL.h" -#include "nsIHTTPChannel.h" -#include "nsIHTTPEventSink.h" +#include "nsIHttpChannel.h" +#include "nsIHttpEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIDNSService.h" #include "nsISimpleEnumerator.h" -#include "nsIHTTPHeader.h" #include "nsXPIDLString.h" #include "nsNetUtil.h" @@ -67,6 +66,31 @@ static PRBool gVerbose = PR_FALSE; static nsIEventQueue* gEventQ = nsnull; static PRBool gAskUserForInput = PR_FALSE; +//----------------------------------------------------------------------------- +// HeaderVisitor +//----------------------------------------------------------------------------- + +class HeaderVisitor : public nsIHttpHeaderVisitor +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIHTTPHEADERVISITOR + + HeaderVisitor() { NS_INIT_ISUPPORTS(); } + virtual ~HeaderVisitor() {} +}; +NS_IMPL_ISUPPORTS1(HeaderVisitor, nsIHttpHeaderVisitor) + +NS_IMETHODIMP +HeaderVisitor::VisitHeader(const char *header, const char *value) +{ + printf("%s: %s\n", header, value); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// URLLoadInfo +//----------------------------------------------------------------------------- class URLLoadInfo : public nsISupports { @@ -100,136 +124,50 @@ URLLoadInfo::~URLLoadInfo() NS_IMPL_THREADSAFE_ISUPPORTS(URLLoadInfo,NS_GET_IID(nsISupports)); +//----------------------------------------------------------------------------- +// TestHttpEventSink +//----------------------------------------------------------------------------- -class TestHTTPEventSink : public nsIHTTPEventSink +class TestHttpEventSink : public nsIHttpEventSink { public: - - TestHTTPEventSink(); - virtual ~TestHTTPEventSink(); - - // ISupports interface... NS_DECL_ISUPPORTS + NS_DECL_NSIHTTPEVENTSINK - // nsIHTTPEventSink interface... - - NS_IMETHOD OnHeadersAvailable(nsISupports* i_Context); - - // OnRedirect gets fired only if you have set FollowRedirects on the handler! - NS_IMETHOD OnRedirect(nsIChannel *aOldChannel, - nsIChannel *aNewChannel); + TestHttpEventSink(); + virtual ~TestHttpEventSink(); }; -TestHTTPEventSink::TestHTTPEventSink() +TestHttpEventSink::TestHttpEventSink() { NS_INIT_REFCNT(); } -TestHTTPEventSink::~TestHTTPEventSink() +TestHttpEventSink::~TestHttpEventSink() { } -NS_IMPL_ISUPPORTS(TestHTTPEventSink,NS_GET_IID(nsIHTTPEventSink)); +NS_IMPL_ISUPPORTS1(TestHttpEventSink, nsIHttpEventSink); +#if 0 NS_IMETHODIMP TestHTTPEventSink::OnHeadersAvailable(nsISupports* context) { - nsCOMPtr enumerator; - nsCOMPtr pHTTPCon(do_QueryInterface(context)); - PRBool bMoreHeaders; - - if (pHTTPCon) { - nsXPIDLCString value; - - printf("Channel Info:\n"); - pHTTPCon->GetContentType(getter_Copies(value)); - printf("\tContent-Type: %s\n", (const char*)value); - - pHTTPCon->GetCharset(getter_Copies(value)); - printf("\tCharset: %s\n", (const char*)value); - - pHTTPCon->GetRequestHeaderEnumerator(getter_AddRefs(enumerator)); - - printf("Request headers:\n"); - enumerator->HasMoreElements(&bMoreHeaders); - while (bMoreHeaders) { - nsCOMPtr item; - nsCOMPtr header; - - enumerator->GetNext(getter_AddRefs(item)); - header = do_QueryInterface(item); - - if (header) { - nsCOMPtr key; - nsAutoString field; - header->GetField(getter_AddRefs(key)); - key->ToString(field); - nsCAutoString theField; - theField.AssignWithConversion(field); - printf("\t%s: ", theField.get()); - - header->GetValue(getter_Copies(value)); - printf("%s\n", (const char*)value); - } - - enumerator->HasMoreElements(&bMoreHeaders); - } - - pHTTPCon->GetResponseHeaderEnumerator(getter_AddRefs(enumerator)); - - printf("Response headers:\n"); - enumerator->HasMoreElements(&bMoreHeaders); - while (bMoreHeaders) { - nsCOMPtr item; - nsCOMPtr header; - - enumerator->GetNext(getter_AddRefs(item)); - header = do_QueryInterface(item); - - if (header) { - nsCOMPtr key; - nsAutoString field; - - header->GetField(getter_AddRefs(key)); - key->ToString(field); - nsCAutoString theField; - theField.AssignWithConversion(field); - printf("\t%s: ", theField.get()); - - header->GetValue(getter_Copies(value)); - printf("%s\n", (const char*)value); - } - - enumerator->HasMoreElements(&bMoreHeaders); - } - - } - - - if (gVerbose) { - printf("\n+++ TestHTTPEventSink::OnHeadersAvailable +++\n"); - if (pHTTPCon) { - char* type; - //optimize later TODO allow atoms here...! intead of just the header strings - pHTTPCon->GetContentType(&type); - if (type) { - printf("\nReceiving ... %s\n", type); - nsCRT::free(type); - } - } - } return NS_OK; } +#endif NS_IMETHODIMP -TestHTTPEventSink::OnRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel) +TestHttpEventSink::OnRedirect(nsIHttpChannel *channel, nsIChannel *newChannel) { printf("\n+++ TestHTTPEventSink::OnRedirect +++\n"); return NS_OK; } -//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// InputTestConsumer +//----------------------------------------------------------------------------- class InputTestConsumer : public nsIStreamListener { @@ -243,57 +181,6 @@ public: NS_DECL_NSISTREAMLISTENER }; -//////////////////////////////////////////////////////////////////////////////// - -class OpenObserver : public nsIRequestObserver -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER - - OpenObserver(InputTestConsumer* cons) - : mInputConsumer(cons) { - NS_INIT_REFCNT(); - } - virtual ~OpenObserver() {} - -protected: - InputTestConsumer* mInputConsumer; -}; - -NS_IMPL_ISUPPORTS1(OpenObserver, nsIRequestObserver); - -NS_IMETHODIMP -OpenObserver::OnStartRequest(nsIRequest *request, nsISupports* context) -{ - printf("\n+++ OpenObserver::OnStartRequest +++. Context = %p\n", (void*)context); - - char* type; - PRInt32 length = -1; - nsresult rv; - nsCOMPtr pChan(do_QueryInterface(context)); - rv = pChan->GetContentType(&type); - NS_ASSERTION(NS_SUCCEEDED(rv), "GetContentType failed"); - rv = pChan->GetContentLength(&length); - NS_ASSERTION(NS_SUCCEEDED(rv), "GetContentLength failed"); - printf(" contentType = %s length = %d\n", type, length); - nsCRT::free(type); - - return NS_OK; -} - -NS_IMETHODIMP -OpenObserver::OnStopRequest(nsIRequest *request, nsISupports* context, - nsresult aStatus) -{ - printf("\n+++ OpenObserver::OnStopRequest (status = %x) +++." - "\tContext = %p\n", - aStatus, (void*)context); - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// - InputTestConsumer::InputTestConsumer() { NS_INIT_REFCNT(); @@ -303,38 +190,53 @@ InputTestConsumer::~InputTestConsumer() { } - NS_IMPL_ISUPPORTS(InputTestConsumer,NS_GET_IID(nsIStreamListener)); - NS_IMETHODIMP InputTestConsumer::OnStartRequest(nsIRequest *request, nsISupports* context) { URLLoadInfo* info = (URLLoadInfo*)context; - if (info) { + if (info) info->mConnectTime = PR_Now() - info->mConnectTime; - } - if (gVerbose) { + if (gVerbose) printf("\nStarted loading: %s\n", info ? info->Name() : "UNKNOWN URL"); - } -/* - nsCOMPtr pURI(do_QueryInterface(context)); - char* location = nsnull; - if (pURI) { - pURI->GetSpec(&location); + nsXPIDLCString value; + + nsCOMPtr channel = do_QueryInterface(request); + if (channel) { + + printf("Channel Info:\n"); + channel->GetContentType(getter_Copies(value)); + printf("\tContent-Type: %s\n", (const char*)value); + + PRInt32 length = -1; + channel->GetContentLength(&length); + printf("\tContent-Length: %d\n", length); } - printf("\nStarted loading: %s\n", location ? location : "UNKNOWN URL"); - if (location) { - nsCRT::free(location); + nsCOMPtr httpChannel(do_QueryInterface(request)); + if (httpChannel) { + HeaderVisitor *visitor = new HeaderVisitor(); + if (!visitor) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(visitor); + + httpChannel->GetCharset(getter_Copies(value)); + printf("\tCharset: %s\n", (const char*)value); + + printf("\nHTTP request headers:\n"); + httpChannel->VisitRequestHeaders(visitor); + + printf("\nHTTP response headers:\n"); + httpChannel->VisitResponseHeaders(visitor); + + NS_RELEASE(visitor); } -*/ return NS_OK; } - NS_IMETHODIMP InputTestConsumer::OnDataAvailable(nsIRequest *request, nsISupports* context, @@ -410,7 +312,7 @@ InputTestConsumer::OnStopRequest(nsIRequest *request, nsISupports* context, connectTime = (info->mConnectTime/1000.0)/1000.0; readTime = ((info->mTotalTime-info->mConnectTime)/1000.0)/1000.0; - nsCOMPtr pHTTPCon(do_QueryInterface(request)); + nsCOMPtr pHTTPCon(do_QueryInterface(request)); if (pHTTPCon) { pHTTPCon->GetResponseStatus(&httpStatus); bHTTPURL = PR_TRUE; @@ -433,42 +335,30 @@ InputTestConsumer::OnStopRequest(nsIRequest *request, nsISupports* context, } else { printf("\nFinished loading: UNKNOWN URL. Status Code: %x\n", aStatus); } -/* - nsCOMPtr pURI(do_QueryInterface(context)); - char* location = nsnull; - if (pURI) { - pURI->GetSpec(&location); - } - - printf("\nFinished loading: %s Status Code: %x\n", location ? location : "UNKNOWN URL", status); - - if (location) { - nsCRT::free(location); - } -*/ FireDecrement(); - return NS_OK; } -//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// NotificationCallbacks +//----------------------------------------------------------------------------- -class nsNotificationCallbacks : public nsIInterfaceRequestor { +class NotificationCallbacks : public nsIInterfaceRequestor { public: NS_DECL_ISUPPORTS - nsNotificationCallbacks() { + NotificationCallbacks() { NS_INIT_REFCNT(); } NS_IMETHOD GetInterface(const nsIID& eventSinkIID, void* *result) { nsresult rv = NS_ERROR_FAILURE; - if (eventSinkIID.Equals(NS_GET_IID(nsIHTTPEventSink))) { - TestHTTPEventSink *sink; + if (eventSinkIID.Equals(NS_GET_IID(nsIHttpEventSink))) { + TestHttpEventSink *sink; - sink = new TestHTTPEventSink(); + sink = new TestHttpEventSink(); if (sink == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(sink); @@ -479,9 +369,11 @@ public: } }; -NS_IMPL_ISUPPORTS1(nsNotificationCallbacks, nsIInterfaceRequestor) +NS_IMPL_ISUPPORTS1(NotificationCallbacks, nsIInterfaceRequestor) -//////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// helpers... +//----------------------------------------------------------------------------- nsresult StartLoadingURL(const char* aUrlString) { @@ -498,7 +390,7 @@ nsresult StartLoadingURL(const char* aUrlString) } nsCOMPtr pChannel; - nsNotificationCallbacks* callbacks = new nsNotificationCallbacks(); + NotificationCallbacks* callbacks = new NotificationCallbacks(); if (!callbacks) { NS_ERROR("Failed to create a new consumer!"); return NS_ERROR_OUT_OF_MEMORY;; @@ -520,14 +412,11 @@ nsresult StartLoadingURL(const char* aUrlString) request object. This is done by QI for the specific protocolConnection. */ - nsCOMPtr pHTTPCon(do_QueryInterface(pChannel)); + nsCOMPtr pHTTPCon(do_QueryInterface(pChannel)); if (pHTTPCon) { // Setting a sample header. - nsCOMPtr sampleHeader; - - sampleHeader = NS_NewAtom("sample-header-minus-the-colon"); - rv = pHTTPCon->SetRequestHeader(sampleHeader, "Sample-Value"); + rv = pHTTPCon->SetRequestHeader("sample-header", "Sample-Value"); if (NS_FAILED(rv)) return rv; } InputTestConsumer* listener; diff --git a/mozilla/netwerk/test/makefile.win b/mozilla/netwerk/test/makefile.win index ad1cd8e3d57..eb3fba70dd8 100644 --- a/mozilla/netwerk/test/makefile.win +++ b/mozilla/netwerk/test/makefile.win @@ -34,9 +34,10 @@ PROG9 = .\$(OBJDIR)\TestRes.exe PROGC = .\$(OBJDIR)\TestWriteStream.exe PROGD = .\$(OBJDIR)\TestWriteSpeed.exe PROGE = .\$(OBJDIR)\TestCallbacks.exe +PROGF = .\$(OBJDIR)\TestHttp.exe PROGRAMS = \ $(PROG1) $(PROG2) $(PROG3) $(PROG4) $(PROG5) $(PROG6) $(PROG7) $(PROG8) $(PROG9)\ - $(PROGC) $(PROGD) $(PROGE) + $(PROGC) $(PROGD) $(PROGE) $(PROGF) LCFLAGS=-DUSE_NSREG -GX @@ -82,3 +83,5 @@ $(PROGD): $(OBJDIR) TestWriteSpeed.cpp $(PROGE): $(OBJDIR) TestCallbacks.cpp +$(PROGF): $(OBJDIR) TestHttp.cpp +