Bug 477578 - uppercase selected HTTP methods (CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT, TRACE and TRACK) and make other methods case sensitive
authorMichal Novotny <michal.novotny@gmail.com>
Mon, 12 Sep 2011 18:00:15 +0200
changeset 78201 90e4d207f5dd1b8abad50ec47468a702ecb33431
parent 78200 b3eb728a2d54120c77bec8b3bdba9579aaa916f1
child 78202 38ff711d43699ddf0f16f287e08254e2da22b392
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs477578
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 477578 - uppercase selected HTTP methods (CONNECT, DELETE, GET, HEAD, OPTIONS, POST, PUT, TRACE and TRACK) and make other methods case sensitive
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsHttp.cpp
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpAtomList.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpRequestHead.cpp
netwerk/protocol/http/nsHttpRequestHead.h
netwerk/test/unit/test_bug477578.js
netwerk/test/unit/xpcshell.ini
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -771,35 +771,46 @@ HttpBaseChannel::nsContentEncodings::Pre
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIHttpChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestMethod(nsACString& aMethod)
 {
-  aMethod = mRequestHead.Method();
+  mRequestHead.Method()->ToUTF8String(aMethod);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod)
 {
   ENSURE_CALLED_BEFORE_ASYNC_OPEN();
 
-  const nsCString& flatMethod = PromiseFlatCString(aMethod);
+  nsCAutoString upperCaseMethod;
+  ToUpperCase(aMethod, upperCaseMethod);
 
   // Method names are restricted to valid HTTP tokens.
-  if (!nsHttp::IsValidToken(flatMethod))
+  if (!nsHttp::IsValidToken(upperCaseMethod))
     return NS_ERROR_INVALID_ARG;
 
-  nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get());
-  if (!atom)
-    return NS_ERROR_FAILURE;
+  nsCOMPtr<nsIAtom> atom = do_GetAtom(upperCaseMethod);
 
+  // We've changed method names to case sensitive in bug 477578. Some
+  // methods are kept case insensitive to keep backward compatibility and
+  // to satisfy XMLHttpRequest specification which demands it.
+#define HTTP_METHOD_ATOM(name_, value_)
+#define HTTP_CASE_INSENSITIVE_METHOD_ATOM(name_, value_) \
+  if (nsHttp::name_ == atom) {} else
+#include "nsHttpAtomList.h"
+#undef HTTP_CASE_INSENSITIVE_METHOD_ATOM
+#undef HTTP_METHOD_ATOM
+  { // upper case atom doesn't match any case insensitive atom
+    atom = do_GetAtom(aMethod);
+  }
   mRequestHead.SetMethod(atom);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetReferrer(nsIURI **referrer)
 {
   NS_ENSURE_ARG_POINTER(referrer);
@@ -1538,17 +1549,17 @@ HttpBaseChannel::SetupReplacementChannel
       if (uploadChannel2) {
         const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type);
         if (!ctype)
           ctype = "";
         const char *clen  = mRequestHead.PeekHeader(nsHttp::Content_Length);
         PRInt64 len = clen ? nsCRT::atoll(clen) : -1;
         uploadChannel2->ExplicitSetUploadStream(
                                   mUploadStream, nsDependentCString(ctype), len,
-                                  nsDependentCString(mRequestHead.Method()),
+                                  nsAtomCString(mRequestHead.Method()),
                                   mUploadStreamHasHeaders);
       } else {
         if (mUploadStreamHasHeaders) {
           uploadChannel->SetUploadStream(mUploadStream, EmptyCString(),
                            -1);
         } else {
           const char *ctype =
             mRequestHead.PeekHeader(nsHttp::Content_Type);
@@ -1565,17 +1576,17 @@ HttpBaseChannel::SetupReplacementChannel
         }
       }
     }
     // since preserveMethod is true, we need to ensure that the appropriate 
     // request method gets set on the channel, regardless of whether or not 
     // we set the upload stream above. This means SetRequestMethod() will
     // be called twice if ExplicitSetUploadStream() gets called above.
 
-    httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method()));
+    httpChannel->SetRequestMethod(nsAtomCString(mRequestHead.Method()));
   }
   // convey the referrer if one was used for this channel to the next one
   if (mReferrer)
     httpChannel->SetReferrer(mReferrer);
   // convey the mAllowPipelining flag
   httpChannel->SetAllowPipelining(mAllowPipelining);
   // convey the new redirection limit
   httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1067,17 +1067,17 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   // The socket transport in the chrome process now holds a logical ref to us
   // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
   AddIPDLReference();
 
   gNeckoChild->SendPHttpChannelConstructor(this, tabChild);
 
   SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI),
                 IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags,
-                mRequestHeaders, mRequestHead.Method(),
+                mRequestHeaders, nsAtomCString(mRequestHead.Method()),
                 IPC::InputStream(mUploadStream), mUploadStreamHasHeaders,
                 mPriority, mRedirectionLimit, mAllowPipelining,
                 mForceAllowThirdPartyCookie, mSendResumeAt,
                 mStartPos, mEntityID, mChooseApplicationCache, 
                 appCacheClientId);
 
   return NS_OK;
 }
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -127,17 +127,17 @@ HttpChannelParent::GetInterface(const ns
 
 bool 
 HttpChannelParent::RecvAsyncOpen(const IPC::URI&            aURI,
                                  const IPC::URI&            aOriginalURI,
                                  const IPC::URI&            aDocURI,
                                  const IPC::URI&            aReferrerURI,
                                  const PRUint32&            loadFlags,
                                  const RequestHeaderTuples& requestHeaders,
-                                 const nsHttpAtom&          requestMethod,
+                                 const nsCString&           requestMethod,
                                  const IPC::InputStream&    uploadStream,
                                  const PRBool&              uploadStreamHasHeaders,
                                  const PRUint16&            priority,
                                  const PRUint8&             redirectionLimit,
                                  const PRBool&              allowPipelining,
                                  const PRBool&              forceAllowThirdPartyCookie,
                                  const bool&                doResumeAt,
                                  const PRUint64&            startPos,
@@ -185,17 +185,17 @@ HttpChannelParent::RecvAsyncOpen(const I
                                requestHeaders[i].mMerge);
   }
 
   nsRefPtr<HttpChannelParentListener> channelListener =
       new HttpChannelParentListener(this);
 
   httpChan->SetNotificationCallbacks(channelListener);
 
-  httpChan->SetRequestMethod(nsDependentCString(requestMethod.get()));
+  httpChan->SetRequestMethod(requestMethod);
 
   nsCOMPtr<nsIInputStream> stream(uploadStream);
   if (stream) {
     httpChan->InternalSetUploadStream(stream);
     httpChan->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (priority != nsISupportsPriority::PRIORITY_NORMAL)
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -81,17 +81,17 @@ public:
 
 protected:
   virtual bool RecvAsyncOpen(const IPC::URI&            uri,
                              const IPC::URI&            originalUri,
                              const IPC::URI&            docUri,
                              const IPC::URI&            referrerUri,
                              const PRUint32&            loadFlags,
                              const RequestHeaderTuples& requestHeaders,
-                             const nsHttpAtom&          requestMethod,
+                             const nsCString&           requestMethod,
                              const IPC::InputStream&    uploadStream,
                              const PRBool&              uploadStreamHasHeaders,
                              const PRUint16&            priority,
                              const PRUint8&             redirectionLimit,
                              const PRBool&              allowPipelining,
                              const PRBool&              forceAllowThirdPartyCookie,
                              const bool&                doResumeAt,
                              const PRUint64&            startPos,
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -42,17 +42,16 @@
 include protocol PNecko;
 
 include "mozilla/net/PHttpChannelParams.h";
 include "mozilla/net/NeckoMessageUtils.h";
 include "prio.h";
 
 using RequestHeaderTuples;
 using nsHttpResponseHead;
-using nsHttpAtom;
 using IPC::URI;
 using IPC::InputStream;
 using PRNetAddr;
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
@@ -65,17 +64,17 @@ parent:
             // - TODO: bug 571161: unclear if any HTTP channel clients ever
             // set originalURI != uri (about:credits?); also not clear if
             // chrome channel would ever need to know.  Get rid of next arg?
             URI                 original,
             URI                 doc,
             URI                 referrer,
             PRUint32            loadFlags,
             RequestHeaderTuples requestHeaders,
-            nsHttpAtom          requestMethod,
+            nsCString           requestMethod,
             InputStream         uploadStream,
             PRBool              uploadStreamHasHeaders,
             PRUint16            priority,
             PRUint8             redirectionLimit,
             PRBool              allowPipelining,
             PRBool              forceAllowThirdPartyCookie,
             bool                resumeAt,
             PRUint64            startPos,
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -37,16 +37,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHttp.h"
 #include "pldhash.h"
 #include "mozilla/Mutex.h"
 #include "nsCRT.h"
 #include "prbit.h"
+#include "nsStaticAtom.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"
@@ -55,16 +56,33 @@ PRLogModuleInfo *gHttpLog = nsnull;
 // find out how many atoms we have
 #define HTTP_ATOM(_name, _value) Unused_ ## _name,
 enum {
 #include "nsHttpAtomList.h"
     NUM_HTTP_ATOMS
 };
 #undef HTTP_ATOM
 
+// define all method atoms
+#define HTTP_METHOD_ATOM(name_, value_) nsIAtom* nsHttp::name_;
+#include "nsHttpAtomList.h"
+#undef HTTP_METHOD_ATOM
+
+#define HTTP_METHOD_ATOM(name_, value_) \
+    NS_STATIC_ATOM_BUFFER(name_##_buffer, value_)
+#include "nsHttpAtomList.h"
+#undef HTTP_METHOD_ATOM
+
+static const nsStaticAtom methodAtomsInfo[] = {
+#define HTTP_METHOD_ATOM(name_, value_) \
+    NS_STATIC_ATOM(name_##_buffer, &nsHttp::name_),
+#include "nsHttpAtomList.h"
+#undef HTTP_METHOD_ATOM
+};
+
 using namespace mozilla;
 
 // we keep a linked list of atoms allocated on the heap for easy clean up when
 // the atom table is destroyed.  The structure and value string are allocated
 // as one contiguous block.
 
 struct HttpHeapAtom {
     struct HttpHeapAtom *next;
@@ -179,16 +197,22 @@ nsHttp::DestroyAtomTable()
     }
 
     if (sLock) {
         delete sLock;
         sLock = nsnull;
     }
 }
 
+void
+nsHttp::CreateMethodAtoms()
+{
+    NS_RegisterStaticAtoms(methodAtomsInfo, NS_ARRAY_LENGTH(methodAtomsInfo));
+}
+
 // this function may be called from multiple threads
 nsHttpAtom
 nsHttp::ResolveAtom(const char *str)
 {
     nsHttpAtom atom = { nsnull };
 
     if (!str || !sAtomTable.ops)
         return atom;
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -62,16 +62,17 @@
 
 #include "plstr.h"
 #include "prlog.h"
 #include "prtime.h"
 #include "nsISupportsUtils.h"
 #include "nsPromiseFlatString.h"
 #include "nsURLHelper.h"
 #include "netCore.h"
+#include "nsIAtom.h"
 
 #if defined(PR_LOGGING)
 //
 // Log module for HTTP Protocol logging...
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsHttp:5
@@ -159,16 +160,18 @@ struct nsHttpAtom
     const char *_val;
 };
 
 struct nsHttp
 {
     static nsresult CreateAtomTable();
     static void DestroyAtomTable();
 
+    static void CreateMethodAtoms();
+
     // will dynamically add atoms to the table if they don't already exist
     static nsHttpAtom ResolveAtom(const char *);
     static nsHttpAtom ResolveAtom(const nsACString &s)
     {
         return ResolveAtom(PromiseFlatCString(s).get());
     }
 
     // returns true if the specified token [start,end) is valid per RFC 2616
@@ -210,16 +213,20 @@ struct nsHttp
     // 
     // 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
+
+#define HTTP_METHOD_ATOM(_name, _value) static nsIAtom* _name;
+#include "nsHttpAtomList.h"
+#undef HTTP_METHOD_ATOM
 };
 
 //-----------------------------------------------------------------------------
 // utilities...
 //-----------------------------------------------------------------------------
 
 static inline PRUint32
 PRTimeToSeconds(PRTime t_usec)
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -45,16 +45,17 @@
 
   All entries 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 to HTTP_ATOM is the string value of the atom.
  ******/
 
+#if defined(HTTP_ATOM)
 HTTP_ATOM(Accept,                    "Accept")
 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")
@@ -118,31 +119,45 @@ HTTP_ATOM(Trailer,                   "Tr
 HTTP_ATOM(Transfer_Encoding,         "Transfer-Encoding")
 HTTP_ATOM(URI,                       "URI")
 HTTP_ATOM(Upgrade,                   "Upgrade")
 HTTP_ATOM(User_Agent,                "User-Agent")
 HTTP_ATOM(Vary,                      "Vary")
 HTTP_ATOM(Version,                   "Version")
 HTTP_ATOM(WWW_Authenticate,          "WWW-Authenticate")
 HTTP_ATOM(Warning,                   "Warning")
+#endif
 
 // methods are atoms too.
 //
 // Note: winnt.h defines DELETE macro, so we'll just keep the methods mixedcase
 // even though they're normally written all uppercase. -- darin
 
-HTTP_ATOM(Connect,                   "CONNECT")
-HTTP_ATOM(Copy,                      "COPY")
-HTTP_ATOM(Delete,                    "DELETE")
-HTTP_ATOM(Get,                       "GET")
-HTTP_ATOM(Head,                      "HEAD")
-HTTP_ATOM(Index,                     "INDEX")
-HTTP_ATOM(Lock,                      "LOCK")
-HTTP_ATOM(M_Post,                    "M-POST")
-HTTP_ATOM(Mkcol,                     "MKCOL")
-HTTP_ATOM(Move,                      "MOVE")
-HTTP_ATOM(Options,                   "OPTIONS")
-HTTP_ATOM(Post,                      "POST")
-HTTP_ATOM(Propfind,                  "PROPFIND")
-HTTP_ATOM(Proppatch,                 "PROPPATCH")
-HTTP_ATOM(Put,                       "PUT")
-HTTP_ATOM(Trace,                     "TRACE")
-HTTP_ATOM(Unlock,                    "UNLOCK")
+#if defined(HTTP_METHOD_ATOM)
+
+#if !defined(HTTP_CASE_INSENSITIVE_METHOD_ATOM)
+#define HTTP_CASE_INSENSITIVE_METHOD_ATOM HTTP_METHOD_ATOM
+#define UNDEF_HTTP_CASE_INSENSITIVE_METHOD_ATOM
+#endif
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Connect,   "CONNECT")
+HTTP_METHOD_ATOM                 (Copy,      "COPY")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Delete,    "DELETE")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Get,       "GET")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Head,      "HEAD")
+HTTP_METHOD_ATOM                 (Index,     "INDEX")
+HTTP_METHOD_ATOM                 (Lock,      "LOCK")
+HTTP_METHOD_ATOM                 (M_Post,    "M-POST")
+HTTP_METHOD_ATOM                 (Mkcol,     "MKCOL")
+HTTP_METHOD_ATOM                 (Move,      "MOVE")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Options,   "OPTIONS")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Post,      "POST")
+HTTP_METHOD_ATOM                 (Propfind,  "PROPFIND")
+HTTP_METHOD_ATOM                 (Proppatch, "PROPPATCH")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Put,       "PUT")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Trace,     "TRACE")
+HTTP_CASE_INSENSITIVE_METHOD_ATOM(Track,     "TRACK")
+HTTP_METHOD_ATOM                 (Unlock,    "UNLOCK")
+#if defined(UNDEF_HTTP_CASE_INSENSITIVE_METHOD_ATOM)
+#undef UNDEF_HTTP_CASE_INSENSITIVE_METHOD_ATOM
+#undef HTTP_CASE_INSENSITIVE_METHOD_ATOM
+#endif
+
+#endif
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2407,17 +2407,19 @@ nsHttpChannel::CheckCache()
         return NS_OK;
 
     nsXPIDLCString buf;
 
     // Get the method that was used to generate the cached response
     rv = mCacheEntry->GetMetaDataElement("request-method", getter_Copies(buf));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsHttpAtom method = nsHttp::ResolveAtom(buf);
+    nsCOMPtr<nsIAtom> method = do_GetAtom(buf);
+    NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY);
+
     if (method == nsHttp::Head) {
         // The cached response does not contain an entity.  We can only reuse
         // the response if the current request is also HEAD.
         if (mRequestHead.Method() != nsHttp::Head)
             return NS_OK;
     }
     buf.Adopt(0);
 
@@ -2976,17 +2978,17 @@ nsHttpChannel::AddCacheEntryHeaders(nsIC
     LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%x] begin", this));
     // Store secure data in memory only
     if (mSecurityInfo)
         entry->SetSecurityInfo(mSecurityInfo);
 
     // Store the HTTP request method with the cache entry so we can distinguish
     // for example GET and HEAD responses.
     rv = entry->SetMetaDataElement("request-method",
-                                   mRequestHead.Method().get());
+                                   nsAtomCString(mRequestHead.Method()).get());
     if (NS_FAILED(rv)) return rv;
 
     // Store the HTTP authorization scheme used if any...
     rv = StoreAuthorizationMetaData(entry);
     if (NS_FAILED(rv)) return rv;
 
     // Iterate over the headers listed in the Vary response header, and
     // store the value of the corresponding request header so we can verify
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -233,16 +233,18 @@ nsHttpHandler::Init()
     nsresult rv;
 
     LOG(("nsHttpHandler::Init\n"));
 
     rv = nsHttp::CreateAtomTable();
     if (NS_FAILED(rv))
         return rv;
 
+    nsHttp::CreateMethodAtoms();
+
     mIOService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
         NS_WARNING("unable to continue without io service");
         return rv;
     }
 
     if (IsNeckoChild())
         NeckoChild::InitNeckoChild();
--- a/netwerk/protocol/http/nsHttpRequestHead.cpp
+++ b/netwerk/protocol/http/nsHttpRequestHead.cpp
@@ -42,17 +42,17 @@
 // nsHttpRequestHead
 //-----------------------------------------------------------------------------
 
 void
 nsHttpRequestHead::Flatten(nsACString &buf, PRBool pruneProxyHeaders)
 {
     // note: the first append is intentional.
  
-    buf.Append(mMethod.get());
+    buf.Append(nsAtomCString(mMethod));
     buf.Append(' ');
     buf.Append(mRequestURI);
     buf.AppendLiteral(" HTTP/");
 
     switch (mVersion) {
     case NS_HTTP_VERSION_1_1:
         buf.AppendLiteral("1.1");
         break;
--- a/netwerk/protocol/http/nsHttpRequestHead.h
+++ b/netwerk/protocol/http/nsHttpRequestHead.h
@@ -50,36 +50,36 @@
 //-----------------------------------------------------------------------------
 
 class nsHttpRequestHead
 {
 public:
     nsHttpRequestHead() : mMethod(nsHttp::Get), mVersion(NS_HTTP_VERSION_1_1) {}
     ~nsHttpRequestHead() {}
 
-    void SetMethod(nsHttpAtom method) { mMethod = method; }
+    void SetMethod(nsIAtom *method) { mMethod = method; }
     void SetVersion(nsHttpVersion version) { mVersion = version; }
     void SetRequestURI(const nsCSubstring &s) { mRequestURI = s; }
 
     nsHttpHeaderArray  &Headers()    { return mHeaders; }
-    nsHttpAtom          Method()     { return mMethod; }
+    nsIAtom            *Method()     { return mMethod; }
     nsHttpVersion       Version()    { return mVersion; }
     const nsCSubstring &RequestURI() { return mRequestURI; }
 
     const char *PeekHeader(nsHttpAtom h)                                     { return mHeaders.PeekHeader(h); }
     nsresult SetHeader(nsHttpAtom h, const nsACString &v, PRBool m=PR_FALSE) { return mHeaders.SetHeader(h, v, m); }
     nsresult GetHeader(nsHttpAtom h, nsACString &v)                          { return mHeaders.GetHeader(h, v); }
     void ClearHeader(nsHttpAtom h)                                           { mHeaders.ClearHeader(h); }
     void ClearHeaders()                                                      { mHeaders.Clear(); }
 
     const char *FindHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.FindHeaderValue(h, v); }
     PRBool      HasHeaderValue(nsHttpAtom h, const char *v) { return mHeaders.HasHeaderValue(h, v); }
 
     void Flatten(nsACString &, PRBool pruneProxyHeaders = PR_FALSE);
 
 private:
     nsHttpHeaderArray mHeaders;
-    nsHttpAtom        mMethod;
+    nsCOMPtr<nsIAtom> mMethod;
     nsHttpVersion     mVersion;
     nsCString         mRequestURI;
 };
 
 #endif // nsHttpRequestHead_h__
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug477578.js
@@ -0,0 +1,42 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const testMethods = [
+  ["get", "GET"],
+  ["post", "POST"],
+  ["head", "HEAD"],
+  ["put", "PUT"],
+  ["delete", "DELETE"],
+  ["connect", "CONNECT"],
+  ["options", "OPTIONS"],
+  ["trace", "TRACE"],
+  ["track", "TRACK"],
+  ["copy", "copy"],
+  ["index", "index"],
+  ["lock", "lock"],
+  ["m-post", "m-post"],
+  ["mkcol", "mkcol"],
+  ["move", "move"],
+  ["propfind", "propfind"],
+  ["proppatch", "proppatch"],
+  ["unlock", "unlock"],
+  ["link", "link"],
+  ["foo", "foo"],
+  ["foO", "foO"],
+  ["fOo", "fOo"],
+  ["Foo", "Foo"]
+]
+
+function run_test() {
+  var ios =
+    Cc["@mozilla.org/network/io-service;1"].
+    getService(Ci.nsIIOService);
+
+  var chan = ios.newChannel("http://localhost/", null, null)
+                  .QueryInterface(Components.interfaces.nsIHttpChannel);
+
+  for (var i = 0; i < testMethods.length; i++) {
+    chan.requestMethod = testMethods[i][0];
+    do_check_eq(chan.requestMethod, testMethods[i][1]);
+  }
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -39,16 +39,17 @@ skip-if = os == "android"
 [test_bug429347.js]
 [test_bug455311.js]
 [test_bug455598.js]
 [test_bug468426.js]
 # Bug 675039: test hangs consistently on Android 
 skip-if = os == "android"
 [test_bug468594.js]
 [test_bug470716.js]
+[test_bug477578.js]
 [test_bug479413.js]
 [test_bug479485.js]
 [test_bug482601.js]
 [test_bug484684.js]
 [test_bug490095.js]
 [test_bug504014.js]
 [test_bug510359.js]
 # Bug 675039: test hangs consistently on Android