| File: | var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp |
| Warning: | line 391, column 17 Value stored to 'preamble' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| 2 | /* vim: set sw=2 ts=8 et tw=80 : */ |
| 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 6 | |
| 7 | // HttpLog.h should generally be included first |
| 8 | #include "HttpLog.h" |
| 9 | |
| 10 | // Log on level :5, instead of default :4. |
| 11 | #undef LOG |
| 12 | #define LOG(args)do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , MOZ_LOG_EXPAND_ARGS args); } } while (0) LOG5(args)do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , MOZ_LOG_EXPAND_ARGS args); } } while (0) |
| 13 | #undef LOG_ENABLED |
| 14 | #define LOG_ENABLED()(__builtin_expect(!!(mozilla::detail::log_test(mozilla::net:: gHttpLog, mozilla::LogLevel::Verbose)), 0)) LOG5_ENABLED()(__builtin_expect(!!(mozilla::detail::log_test(mozilla::net:: gHttpLog, mozilla::LogLevel::Verbose)), 0)) |
| 15 | |
| 16 | #include "Http2Compression.h" |
| 17 | #include "Http2HuffmanIncoming.h" |
| 18 | #include "Http2HuffmanOutgoing.h" |
| 19 | #include "mozilla/StaticPtr.h" |
| 20 | #include "nsCharSeparatedTokenizer.h" |
| 21 | #include "nsIMemoryReporter.h" |
| 22 | #include "nsHttpHandler.h" |
| 23 | |
| 24 | namespace mozilla { |
| 25 | namespace net { |
| 26 | |
| 27 | static nsDeque<nvPair>* gStaticHeaders = nullptr; |
| 28 | |
| 29 | class HpackStaticTableReporter final : public nsIMemoryReporter { |
| 30 | public: |
| 31 | NS_DECL_THREADSAFE_ISUPPORTSpublic: virtual nsresult QueryInterface(const nsIID& aIID , void** aInstancePtr) override; virtual MozExternalRefCountType AddRef(void) override; virtual MozExternalRefCountType Release (void) override; using HasThreadSafeRefCnt = std::true_type; protected : ::mozilla::ThreadSafeAutoRefCnt mRefCnt; nsAutoOwningThread _mOwningThread; public: |
| 32 | |
| 33 | HpackStaticTableReporter() = default; |
| 34 | |
| 35 | NS_IMETHODvirtual nsresult |
| 36 | CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, |
| 37 | bool aAnonymize) override { |
| 38 | MOZ_COLLECT_REPORT("explicit/network/hpack/static-table", KIND_HEAP,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/static-table" ), KIND_HEAP, UNITS_BYTES, gStaticHeaders->SizeOfIncludingThis (MallocSizeOf), nsLiteralCString("Memory usage of HPACK static table." ), aData) |
| 39 | UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/static-table" ), KIND_HEAP, UNITS_BYTES, gStaticHeaders->SizeOfIncludingThis (MallocSizeOf), nsLiteralCString("Memory usage of HPACK static table." ), aData) |
| 40 | gStaticHeaders->SizeOfIncludingThis(MallocSizeOf),(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/static-table" ), KIND_HEAP, UNITS_BYTES, gStaticHeaders->SizeOfIncludingThis (MallocSizeOf), nsLiteralCString("Memory usage of HPACK static table." ), aData) |
| 41 | "Memory usage of HPACK static table.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/static-table" ), KIND_HEAP, UNITS_BYTES, gStaticHeaders->SizeOfIncludingThis (MallocSizeOf), nsLiteralCString("Memory usage of HPACK static table." ), aData); |
| 42 | |
| 43 | return NS_OK; |
| 44 | } |
| 45 | |
| 46 | private: |
| 47 | MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)static size_t MallocSizeOf(const void* aPtr) { mozilla::dmd:: Report(aPtr); return moz_malloc_size_of(aPtr); } |
| 48 | |
| 49 | ~HpackStaticTableReporter() = default; |
| 50 | }; |
| 51 | |
| 52 | NS_IMPL_ISUPPORTS(HpackStaticTableReporter, nsIMemoryReporter)MozExternalRefCountType HpackStaticTableReporter::AddRef(void ) { static_assert(!std::is_destructible_v<HpackStaticTableReporter >, "Reference-counted class " "HpackStaticTableReporter" " should not have a public destructor. " "Make this class's destructor non-public"); do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 52); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 52; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("HpackStaticTableReporter" != nullptr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!("HpackStaticTableReporter" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"HpackStaticTableReporter\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 52); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"HpackStaticTableReporter\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 52; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership ("HpackStaticTableReporter" " not thread-safe"); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ("HpackStaticTableReporter" ), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType HpackStaticTableReporter::Release(void) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 52); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 52 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("HpackStaticTableReporter" != nullptr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!("HpackStaticTableReporter" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"HpackStaticTableReporter\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 52); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"HpackStaticTableReporter\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 52; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership ("HpackStaticTableReporter" " not thread-safe"); const char* const nametmp = "HpackStaticTableReporter"; nsrefcnt count = --mRefCnt ; NS_LogRelease((this), (count), (nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return 0; } return count; } nsresult HpackStaticTableReporter::QueryInterface(const nsIID& aIID , void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak (NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 52); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<HpackStaticTableReporter, nsIMemoryReporter >, int32_t( reinterpret_cast<char*>(static_cast<nsIMemoryReporter *>((HpackStaticTableReporter*)0x1000)) - reinterpret_cast< char*>((HpackStaticTableReporter*)0x1000))}, {&mozilla ::detail::kImplementedIID<HpackStaticTableReporter, nsISupports >, int32_t(reinterpret_cast<char*>(static_cast<nsISupports *>( static_cast<nsIMemoryReporter*>((HpackStaticTableReporter *)0x1000))) - reinterpret_cast<char*>((HpackStaticTableReporter *)0x1000))}, { nullptr, 0 } } ; static_assert(std::size(table ) > 1, "need at least 1 interface"); rv = NS_TableDrivenQI (static_cast<void*>(this), aIID, aInstancePtr, table); return rv; } |
| 53 | |
| 54 | class HpackDynamicTableReporter final : public nsIMemoryReporter { |
| 55 | public: |
| 56 | NS_DECL_THREADSAFE_ISUPPORTSpublic: virtual nsresult QueryInterface(const nsIID& aIID , void** aInstancePtr) override; virtual MozExternalRefCountType AddRef(void) override; virtual MozExternalRefCountType Release (void) override; using HasThreadSafeRefCnt = std::true_type; protected : ::mozilla::ThreadSafeAutoRefCnt mRefCnt; nsAutoOwningThread _mOwningThread; public: |
| 57 | |
| 58 | explicit HpackDynamicTableReporter(Http2BaseCompressor* aCompressor) |
| 59 | : mCompressor(aCompressor) {} |
| 60 | |
| 61 | NS_IMETHODvirtual nsresult |
| 62 | CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, |
| 63 | bool aAnonymize) override { |
| 64 | MutexAutoLock lock(mMutex); |
| 65 | if (mCompressor) { |
| 66 | MOZ_COLLECT_REPORT("explicit/network/hpack/dynamic-tables", KIND_HEAP,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/dynamic-tables" ), KIND_HEAP, UNITS_BYTES, mCompressor->SizeOfExcludingThis (MallocSizeOf), nsLiteralCString("Aggregate memory usage of HPACK dynamic tables." ), aData) |
| 67 | UNITS_BYTES,(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/dynamic-tables" ), KIND_HEAP, UNITS_BYTES, mCompressor->SizeOfExcludingThis (MallocSizeOf), nsLiteralCString("Aggregate memory usage of HPACK dynamic tables." ), aData) |
| 68 | mCompressor->SizeOfExcludingThis(MallocSizeOf),(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/dynamic-tables" ), KIND_HEAP, UNITS_BYTES, mCompressor->SizeOfExcludingThis (MallocSizeOf), nsLiteralCString("Aggregate memory usage of HPACK dynamic tables." ), aData) |
| 69 | "Aggregate memory usage of HPACK dynamic tables.")(void)aHandleReport->Callback(""_ns, nsLiteralCString("explicit/network/hpack/dynamic-tables" ), KIND_HEAP, UNITS_BYTES, mCompressor->SizeOfExcludingThis (MallocSizeOf), nsLiteralCString("Aggregate memory usage of HPACK dynamic tables." ), aData); |
| 70 | } |
| 71 | return NS_OK; |
| 72 | } |
| 73 | |
| 74 | private: |
| 75 | MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)static size_t MallocSizeOf(const void* aPtr) { mozilla::dmd:: Report(aPtr); return moz_malloc_size_of(aPtr); } |
| 76 | |
| 77 | ~HpackDynamicTableReporter() = default; |
| 78 | |
| 79 | Mutex mMutex{"HpackDynamicTableReporter"}; |
| 80 | Http2BaseCompressor* mCompressor MOZ_GUARDED_BY(mMutex)__attribute__((guarded_by(mMutex))); |
| 81 | |
| 82 | friend class Http2BaseCompressor; |
| 83 | }; |
| 84 | |
| 85 | NS_IMPL_ISUPPORTS(HpackDynamicTableReporter, nsIMemoryReporter)MozExternalRefCountType HpackDynamicTableReporter::AddRef(void ) { static_assert(!std::is_destructible_v<HpackDynamicTableReporter >, "Reference-counted class " "HpackDynamicTableReporter" " should not have a public destructor. " "Make this class's destructor non-public"); do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) >= 0" " (" "illegal refcnt" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 85); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) >= 0" ") (" "illegal refcnt" ")"); do { *((volatile int*)__null) = 85; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("HpackDynamicTableReporter" != nullptr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!("HpackDynamicTableReporter" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"HpackDynamicTableReporter\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 85); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"HpackDynamicTableReporter\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 85; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership ("HpackDynamicTableReporter" " not thread-safe"); nsrefcnt count = ++mRefCnt; NS_LogAddRef((this), (count), ("HpackDynamicTableReporter" ), (uint32_t)(sizeof(*this))); return count; } MozExternalRefCountType HpackDynamicTableReporter::Release(void) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(int32_t (mRefCnt) > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(int32_t(mRefCnt) > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("int32_t(mRefCnt) > 0" " (" "dup release" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 85); AnnotateMozCrashReason("MOZ_ASSERT" "(" "int32_t(mRefCnt) > 0" ") (" "dup release" ")"); do { *((volatile int*)__null) = 85 ; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); do { static_assert( mozilla::detail::AssertionConditionType <decltype("HpackDynamicTableReporter" != nullptr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!("HpackDynamicTableReporter" != nullptr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("\"HpackDynamicTableReporter\" != nullptr" " (" "Must specify a name" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 85); AnnotateMozCrashReason("MOZ_ASSERT" "(" "\"HpackDynamicTableReporter\" != nullptr" ") (" "Must specify a name" ")"); do { *((volatile int*)__null ) = 85; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); if (!mRefCnt.isThreadSafe) _mOwningThread.AssertOwnership ("HpackDynamicTableReporter" " not thread-safe"); const char* const nametmp = "HpackDynamicTableReporter"; nsrefcnt count = --mRefCnt; NS_LogRelease((this), (count), (nametmp)); if (count == 0) { mRefCnt = 1; delete (this); return 0; } return count ; } nsresult HpackDynamicTableReporter::QueryInterface(const nsIID & aIID, void** aInstancePtr) { do { if (!(aInstancePtr)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "QueryInterface requires a non-NULL destination!" , "aInstancePtr", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 85); MOZ_PretendNoReturn(); } } while (0); nsresult rv = NS_ERROR_FAILURE ; static_assert(1 > 0, "Need more arguments to NS_INTERFACE_TABLE" ); static const QITableEntry table[] = { {&mozilla::detail ::kImplementedIID<HpackDynamicTableReporter, nsIMemoryReporter >, int32_t( reinterpret_cast<char*>(static_cast<nsIMemoryReporter *>((HpackDynamicTableReporter*)0x1000)) - reinterpret_cast <char*>((HpackDynamicTableReporter*)0x1000))}, {&mozilla ::detail::kImplementedIID<HpackDynamicTableReporter, nsISupports >, int32_t(reinterpret_cast<char*>(static_cast<nsISupports *>( static_cast<nsIMemoryReporter*>((HpackDynamicTableReporter *)0x1000))) - reinterpret_cast<char*>((HpackDynamicTableReporter *)0x1000))}, { nullptr, 0 } } ; static_assert(std::size(table ) > 1, "need at least 1 interface"); rv = NS_TableDrivenQI (static_cast<void*>(this), aIID, aInstancePtr, table); return rv; } |
| 86 | |
| 87 | StaticRefPtr<HpackStaticTableReporter> gStaticReporter; |
| 88 | |
| 89 | void Http2CompressionCleanup() { |
| 90 | // this happens after the socket thread has been destroyed |
| 91 | delete gStaticHeaders; |
| 92 | gStaticHeaders = nullptr; |
| 93 | UnregisterStrongMemoryReporter(gStaticReporter); |
| 94 | gStaticReporter = nullptr; |
| 95 | } |
| 96 | |
| 97 | static void AddStaticElement(const nsCString& name, const nsCString& value) { |
| 98 | nvPair* pair = new nvPair(name, value); |
| 99 | gStaticHeaders->Push(pair); |
| 100 | } |
| 101 | |
| 102 | static void AddStaticElement(const nsCString& name) { |
| 103 | AddStaticElement(name, ""_ns); |
| 104 | } |
| 105 | |
| 106 | static void InitializeStaticHeaders() { |
| 107 | MOZ_ASSERT(OnSocketThread(), "not on socket thread")do { static_assert( mozilla::detail::AssertionConditionType< decltype(OnSocketThread())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(OnSocketThread()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("OnSocketThread()" " (" "not on socket thread" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 107); AnnotateMozCrashReason("MOZ_ASSERT" "(" "OnSocketThread()" ") (" "not on socket thread" ")"); do { *((volatile int*)__null ) = 107; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 108 | if (!gStaticHeaders) { |
| 109 | gStaticHeaders = new nsDeque<nvPair>(); |
| 110 | gStaticReporter = new HpackStaticTableReporter(); |
| 111 | RegisterStrongMemoryReporter(gStaticReporter); |
| 112 | AddStaticElement(":authority"_ns); |
| 113 | AddStaticElement(":method"_ns, "GET"_ns); |
| 114 | AddStaticElement(":method"_ns, "POST"_ns); |
| 115 | AddStaticElement(":path"_ns, "/"_ns); |
| 116 | AddStaticElement(":path"_ns, "/index.html"_ns); |
| 117 | AddStaticElement(":scheme"_ns, "http"_ns); |
| 118 | AddStaticElement(":scheme"_ns, "https"_ns); |
| 119 | AddStaticElement(":status"_ns, "200"_ns); |
| 120 | AddStaticElement(":status"_ns, "204"_ns); |
| 121 | AddStaticElement(":status"_ns, "206"_ns); |
| 122 | AddStaticElement(":status"_ns, "304"_ns); |
| 123 | AddStaticElement(":status"_ns, "400"_ns); |
| 124 | AddStaticElement(":status"_ns, "404"_ns); |
| 125 | AddStaticElement(":status"_ns, "500"_ns); |
| 126 | AddStaticElement("accept-charset"_ns); |
| 127 | AddStaticElement("accept-encoding"_ns, "gzip, deflate"_ns); |
| 128 | AddStaticElement("accept-language"_ns); |
| 129 | AddStaticElement("accept-ranges"_ns); |
| 130 | AddStaticElement("accept"_ns); |
| 131 | AddStaticElement("access-control-allow-origin"_ns); |
| 132 | AddStaticElement("age"_ns); |
| 133 | AddStaticElement("allow"_ns); |
| 134 | AddStaticElement("authorization"_ns); |
| 135 | AddStaticElement("cache-control"_ns); |
| 136 | AddStaticElement("content-disposition"_ns); |
| 137 | AddStaticElement("content-encoding"_ns); |
| 138 | AddStaticElement("content-language"_ns); |
| 139 | AddStaticElement("content-length"_ns); |
| 140 | AddStaticElement("content-location"_ns); |
| 141 | AddStaticElement("content-range"_ns); |
| 142 | AddStaticElement("content-type"_ns); |
| 143 | AddStaticElement("cookie"_ns); |
| 144 | AddStaticElement("date"_ns); |
| 145 | AddStaticElement("etag"_ns); |
| 146 | AddStaticElement("expect"_ns); |
| 147 | AddStaticElement("expires"_ns); |
| 148 | AddStaticElement("from"_ns); |
| 149 | AddStaticElement("host"_ns); |
| 150 | AddStaticElement("if-match"_ns); |
| 151 | AddStaticElement("if-modified-since"_ns); |
| 152 | AddStaticElement("if-none-match"_ns); |
| 153 | AddStaticElement("if-range"_ns); |
| 154 | AddStaticElement("if-unmodified-since"_ns); |
| 155 | AddStaticElement("last-modified"_ns); |
| 156 | AddStaticElement("link"_ns); |
| 157 | AddStaticElement("location"_ns); |
| 158 | AddStaticElement("max-forwards"_ns); |
| 159 | AddStaticElement("proxy-authenticate"_ns); |
| 160 | AddStaticElement("proxy-authorization"_ns); |
| 161 | AddStaticElement("range"_ns); |
| 162 | AddStaticElement("referer"_ns); |
| 163 | AddStaticElement("refresh"_ns); |
| 164 | AddStaticElement("retry-after"_ns); |
| 165 | AddStaticElement("server"_ns); |
| 166 | AddStaticElement("set-cookie"_ns); |
| 167 | AddStaticElement("strict-transport-security"_ns); |
| 168 | AddStaticElement("transfer-encoding"_ns); |
| 169 | AddStaticElement("user-agent"_ns); |
| 170 | AddStaticElement("vary"_ns); |
| 171 | AddStaticElement("via"_ns); |
| 172 | AddStaticElement("www-authenticate"_ns); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | size_t nvPair::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { |
| 177 | return mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + |
| 178 | mValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
| 179 | } |
| 180 | |
| 181 | size_t nvPair::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { |
| 182 | return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
| 183 | } |
| 184 | |
| 185 | nvFIFO::nvFIFO() { InitializeStaticHeaders(); } |
| 186 | |
| 187 | nvFIFO::~nvFIFO() { Clear(); } |
| 188 | |
| 189 | void nvFIFO::AddElement(const nsCString& name, const nsCString& value) { |
| 190 | nvPair* pair = new nvPair(name, value); |
| 191 | mByteCount += pair->Size(); |
| 192 | MutexAutoLock lock(mMutex); |
| 193 | mTable.PushFront(pair); |
| 194 | } |
| 195 | |
| 196 | void nvFIFO::AddElement(const nsCString& name) { AddElement(name, ""_ns); } |
| 197 | |
| 198 | void nvFIFO::RemoveElement() { |
| 199 | nvPair* pair = nullptr; |
| 200 | { |
| 201 | MutexAutoLock lock(mMutex); |
| 202 | pair = mTable.Pop(); |
| 203 | } |
| 204 | if (pair) { |
| 205 | mByteCount -= pair->Size(); |
| 206 | delete pair; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | uint32_t nvFIFO::ByteCount() const { return mByteCount; } |
| 211 | |
| 212 | uint32_t nvFIFO::Length() const { |
| 213 | return mTable.GetSize() + gStaticHeaders->GetSize(); |
| 214 | } |
| 215 | |
| 216 | uint32_t nvFIFO::VariableLength() const { return mTable.GetSize(); } |
| 217 | |
| 218 | size_t nvFIFO::StaticLength() const { return gStaticHeaders->GetSize(); } |
| 219 | |
| 220 | void nvFIFO::Clear() { |
| 221 | mByteCount = 0; |
| 222 | MutexAutoLock lock(mMutex); |
| 223 | while (mTable.GetSize()) { |
| 224 | delete mTable.Pop(); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | const nvPair* nvFIFO::operator[](size_t index) const { |
| 229 | // NWGH - ensure index > 0 |
| 230 | // NWGH - subtract 1 from index here |
| 231 | if (index >= (mTable.GetSize() + gStaticHeaders->GetSize())) { |
| 232 | MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType< decltype(false)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 232); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")"); do { *((volatile int*)__null) = 232; __attribute__((nomerge) ) ::abort(); } while (false); } } while (false); |
| 233 | NS_WARNING("nvFIFO Table Out of Range")NS_DebugBreak(NS_DEBUG_WARNING, "nvFIFO Table Out of Range", nullptr , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 233); |
| 234 | return nullptr; |
| 235 | } |
| 236 | if (index >= gStaticHeaders->GetSize()) { |
| 237 | return mTable.ObjectAt(index - gStaticHeaders->GetSize()); |
| 238 | } |
| 239 | return gStaticHeaders->ObjectAt(index); |
| 240 | } |
| 241 | |
| 242 | Http2BaseCompressor::Http2BaseCompressor() { |
| 243 | mDynamicReporter = new HpackDynamicTableReporter(this); |
| 244 | RegisterStrongMemoryReporter(mDynamicReporter); |
| 245 | } |
| 246 | |
| 247 | Http2BaseCompressor::~Http2BaseCompressor() { |
| 248 | if (mPeakSize) { |
| 249 | Telemetry::Accumulate(mPeakSizeID, mPeakSize); |
| 250 | } |
| 251 | if (mPeakCount) { |
| 252 | Telemetry::Accumulate(mPeakCountID, mPeakCount); |
| 253 | } |
| 254 | UnregisterStrongMemoryReporter(mDynamicReporter); |
| 255 | { |
| 256 | MutexAutoLock lock(mDynamicReporter->mMutex); |
| 257 | mDynamicReporter->mCompressor = nullptr; |
| 258 | } |
| 259 | mDynamicReporter = nullptr; |
| 260 | } |
| 261 | |
| 262 | size_t nvFIFO::SizeOfDynamicTable(mozilla::MallocSizeOf aMallocSizeOf) const { |
| 263 | size_t size = 0; |
| 264 | MutexAutoLock lock(mMutex); |
| 265 | for (const auto elem : mTable) { |
| 266 | size += elem->SizeOfIncludingThis(aMallocSizeOf); |
| 267 | } |
| 268 | return size; |
| 269 | } |
| 270 | |
| 271 | void Http2BaseCompressor::ClearHeaderTable() { mHeaderTable.Clear(); } |
| 272 | |
| 273 | size_t Http2BaseCompressor::SizeOfExcludingThis( |
| 274 | mozilla::MallocSizeOf aMallocSizeOf) const { |
| 275 | return mHeaderTable.SizeOfDynamicTable(aMallocSizeOf); |
| 276 | } |
| 277 | |
| 278 | void Http2BaseCompressor::MakeRoom(uint32_t amount, const char* direction) { |
| 279 | uint32_t countEvicted = 0; |
| 280 | uint32_t bytesEvicted = 0; |
| 281 | |
| 282 | // make room in the header table |
| 283 | while (mHeaderTable.VariableLength() && |
| 284 | ((mHeaderTable.ByteCount() + amount) > mMaxBuffer)) { |
| 285 | // NWGH - remove the "- 1" here |
| 286 | uint32_t index = mHeaderTable.Length() - 1; |
| 287 | LOG(("HTTP %s header table index %u %s %s removed for size.\n", direction,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP %s header table index %u %s %s removed for size.\n", direction , index, mHeaderTable[index]->mName.get(), mHeaderTable[index ]->mValue.get()); } } while (0) |
| 288 | index, mHeaderTable[index]->mName.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP %s header table index %u %s %s removed for size.\n", direction , index, mHeaderTable[index]->mName.get(), mHeaderTable[index ]->mValue.get()); } } while (0) |
| 289 | mHeaderTable[index]->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP %s header table index %u %s %s removed for size.\n", direction , index, mHeaderTable[index]->mName.get(), mHeaderTable[index ]->mValue.get()); } } while (0); |
| 290 | ++countEvicted; |
| 291 | bytesEvicted += mHeaderTable[index]->Size(); |
| 292 | mHeaderTable.RemoveElement(); |
| 293 | } |
| 294 | |
| 295 | if (!strcmp(direction, "decompressor")) { |
| 296 | Telemetry::Accumulate(Telemetry::HPACK_ELEMENTS_EVICTED_DECOMPRESSOR, |
| 297 | countEvicted); |
| 298 | Telemetry::Accumulate(Telemetry::HPACK_BYTES_EVICTED_DECOMPRESSOR, |
| 299 | bytesEvicted); |
| 300 | Telemetry::Accumulate( |
| 301 | Telemetry::HPACK_BYTES_EVICTED_RATIO_DECOMPRESSOR, |
| 302 | (uint32_t)((100.0 * (double)bytesEvicted) / (double)amount)); |
| 303 | } else { |
| 304 | Telemetry::Accumulate(Telemetry::HPACK_ELEMENTS_EVICTED_COMPRESSOR, |
| 305 | countEvicted); |
| 306 | Telemetry::Accumulate(Telemetry::HPACK_BYTES_EVICTED_COMPRESSOR, |
| 307 | bytesEvicted); |
| 308 | Telemetry::Accumulate( |
| 309 | Telemetry::HPACK_BYTES_EVICTED_RATIO_COMPRESSOR, |
| 310 | (uint32_t)((100.0 * (double)bytesEvicted) / (double)amount)); |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | void Http2BaseCompressor::DumpState(const char* preamble) { |
| 315 | if (!LOG_ENABLED()(__builtin_expect(!!(mozilla::detail::log_test(mozilla::net:: gHttpLog, mozilla::LogLevel::Verbose)), 0))) { |
| 316 | return; |
| 317 | } |
| 318 | |
| 319 | if (!mDumpTables) { |
| 320 | return; |
| 321 | } |
| 322 | |
| 323 | LOG(("%s", preamble))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "%s", preamble); } } while (0); |
| 324 | |
| 325 | LOG(("Header Table"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Header Table"); } } while (0); |
| 326 | uint32_t i; |
| 327 | uint32_t length = mHeaderTable.Length(); |
| 328 | uint32_t staticLength = mHeaderTable.StaticLength(); |
| 329 | // NWGH - make i = 1; i <= length; ++i |
| 330 | for (i = 0; i < length; ++i) { |
| 331 | const nvPair* pair = mHeaderTable[i]; |
| 332 | // NWGH - make this <= staticLength |
| 333 | LOG(("%sindex %u: %s %s", i < staticLength ? "static " : "", i,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "%sindex %u: %s %s", i < staticLength ? "static " : "", i , pair->mName.get(), pair->mValue.get()); } } while (0) |
| 334 | pair->mName.get(), pair->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "%sindex %u: %s %s", i < staticLength ? "static " : "", i , pair->mName.get(), pair->mValue.get()); } } while (0); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | void Http2BaseCompressor::SetMaxBufferSizeInternal(uint32_t maxBufferSize) { |
| 339 | MOZ_ASSERT(maxBufferSize <= mMaxBufferSetting)do { static_assert( mozilla::detail::AssertionConditionType< decltype(maxBufferSize <= mMaxBufferSetting)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(maxBufferSize <= mMaxBufferSetting))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("maxBufferSize <= mMaxBufferSetting" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 339); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maxBufferSize <= mMaxBufferSetting" ")"); do { *((volatile int*)__null) = 339; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 340 | |
| 341 | LOG(("Http2BaseCompressor::SetMaxBufferSizeInternal %u called",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2BaseCompressor::SetMaxBufferSizeInternal %u called", maxBufferSize ); } } while (0) |
| 342 | maxBufferSize))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2BaseCompressor::SetMaxBufferSizeInternal %u called", maxBufferSize ); } } while (0); |
| 343 | |
| 344 | while (mHeaderTable.VariableLength() && |
| 345 | (mHeaderTable.ByteCount() > maxBufferSize)) { |
| 346 | mHeaderTable.RemoveElement(); |
| 347 | } |
| 348 | |
| 349 | mMaxBuffer = maxBufferSize; |
| 350 | } |
| 351 | |
| 352 | nsresult Http2BaseCompressor::SetInitialMaxBufferSize(uint32_t maxBufferSize) { |
| 353 | MOZ_ASSERT(mSetInitialMaxBufferSizeAllowed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mSetInitialMaxBufferSizeAllowed)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mSetInitialMaxBufferSizeAllowed ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mSetInitialMaxBufferSizeAllowed", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 353); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mSetInitialMaxBufferSizeAllowed" ")"); do { *((volatile int*)__null) = 353; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 354 | |
| 355 | if (mSetInitialMaxBufferSizeAllowed) { |
| 356 | mMaxBufferSetting = maxBufferSize; |
| 357 | return NS_OK; |
| 358 | } |
| 359 | |
| 360 | return NS_ERROR_FAILURE; |
| 361 | } |
| 362 | |
| 363 | void Http2BaseCompressor::SetDumpTables(bool dumpTables) { |
| 364 | mDumpTables = dumpTables; |
| 365 | } |
| 366 | |
| 367 | nsresult Http2Decompressor::DecodeHeaderBlock(const uint8_t* data, |
| 368 | uint32_t datalen, |
| 369 | nsACString& output, bool isPush) { |
| 370 | mSetInitialMaxBufferSizeAllowed = false; |
| 371 | mOffset = 0; |
| 372 | mData = data; |
| 373 | mDataLen = datalen; |
| 374 | mOutput = &output; |
| 375 | // Add in some space to hopefully not have to reallocate while decompressing |
| 376 | // the headers. 512 bytes seems like a good enough number. |
| 377 | mOutput->Truncate(); |
| 378 | mOutput->SetCapacity(datalen + 512); |
| 379 | mHeaderStatus.Truncate(); |
| 380 | mHeaderHost.Truncate(); |
| 381 | mHeaderScheme.Truncate(); |
| 382 | mHeaderPath.Truncate(); |
| 383 | mHeaderMethod.Truncate(); |
| 384 | mSeenNonColonHeader = false; |
| 385 | mIsPush = isPush; |
| 386 | |
| 387 | nsresult rv = NS_OK; |
| 388 | nsresult softfail_rv = NS_OK; |
| 389 | while (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1))) && (mOffset < mDataLen)) { |
| 390 | bool modifiesTable = true; |
| 391 | const char* preamble = "Decompressor state after ?"; |
Value stored to 'preamble' during its initialization is never read | |
| 392 | if (mData[mOffset] & 0x80) { |
| 393 | rv = DoIndexed(); |
| 394 | preamble = "Decompressor state after indexed"; |
| 395 | } else if (mData[mOffset] & 0x40) { |
| 396 | rv = DoLiteralWithIncremental(); |
| 397 | preamble = "Decompressor state after literal with incremental"; |
| 398 | } else if (mData[mOffset] & 0x20) { |
| 399 | rv = DoContextUpdate(); |
| 400 | preamble = "Decompressor state after context update"; |
| 401 | } else if (mData[mOffset] & 0x10) { |
| 402 | modifiesTable = false; |
| 403 | rv = DoLiteralNeverIndexed(); |
| 404 | preamble = "Decompressor state after literal never index"; |
| 405 | } else { |
| 406 | modifiesTable = false; |
| 407 | rv = DoLiteralWithoutIndex(); |
| 408 | preamble = "Decompressor state after literal without index"; |
| 409 | } |
| 410 | DumpState(preamble); |
| 411 | if (rv == NS_ERROR_ILLEGAL_VALUE) { |
| 412 | if (modifiesTable) { |
| 413 | // Unfortunately, we can't count on our peer now having the same state |
| 414 | // as us, so let's terminate the session and we can try again later. |
| 415 | return NS_ERROR_FAILURE; |
| 416 | } |
| 417 | |
| 418 | // This is an http-level error that we can handle by resetting the stream |
| 419 | // in the upper layers. Let's note that we saw this, then continue |
| 420 | // decompressing until we either hit the end of the header block or find a |
| 421 | // hard failure. That way we won't get an inconsistent compression state |
| 422 | // with the server. |
| 423 | softfail_rv = rv; |
| 424 | rv = NS_OK; |
| 425 | } else if (rv == NS_ERROR_NET_RESET) { |
| 426 | // This happens when we detect connection-based auth being requested in |
| 427 | // the response headers. We'll paper over it for now, and the session will |
| 428 | // handle this as if it received RST_STREAM with HTTP_1_1_REQUIRED. |
| 429 | softfail_rv = rv; |
| 430 | rv = NS_OK; |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 435 | return rv; |
| 436 | } |
| 437 | |
| 438 | return softfail_rv; |
| 439 | } |
| 440 | |
| 441 | nsresult Http2Decompressor::DecodeInteger(uint32_t prefixLen, uint32_t& accum) { |
| 442 | accum = 0; |
| 443 | |
| 444 | if (prefixLen) { |
| 445 | uint32_t mask = (1 << prefixLen) - 1; |
| 446 | |
| 447 | accum = mData[mOffset] & mask; |
| 448 | ++mOffset; |
| 449 | |
| 450 | if (accum != mask) { |
| 451 | // the simple case for small values |
| 452 | return NS_OK; |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | uint32_t factor = 1; // 128 ^ 0 |
| 457 | |
| 458 | // we need a series of bytes. The high bit signifies if we need another one. |
| 459 | // The first one is a a factor of 128 ^ 0, the next 128 ^1, the next 128 ^2, |
| 460 | // .. |
| 461 | |
| 462 | if (mOffset >= mDataLen) { |
| 463 | NS_WARNING("Ran out of data to decode integer")NS_DebugBreak(NS_DEBUG_WARNING, "Ran out of data to decode integer" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 463); |
| 464 | // This is session-fatal. |
| 465 | return NS_ERROR_FAILURE; |
| 466 | } |
| 467 | bool chainBit = mData[mOffset] & 0x80; |
| 468 | accum += (mData[mOffset] & 0x7f) * factor; |
| 469 | |
| 470 | ++mOffset; |
| 471 | factor = factor * 128; |
| 472 | |
| 473 | while (chainBit) { |
| 474 | // really big offsets are just trawling for overflows |
| 475 | if (accum >= 0x800000) { |
| 476 | NS_WARNING("Decoding integer >= 0x800000")NS_DebugBreak(NS_DEBUG_WARNING, "Decoding integer >= 0x800000" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 476); |
| 477 | // This is not strictly fatal to the session, but given the fact that |
| 478 | // the value is way to large to be reasonable, let's just tell our peer |
| 479 | // to go away. |
| 480 | return NS_ERROR_FAILURE; |
| 481 | } |
| 482 | |
| 483 | if (mOffset >= mDataLen) { |
| 484 | NS_WARNING("Ran out of data to decode integer")NS_DebugBreak(NS_DEBUG_WARNING, "Ran out of data to decode integer" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 484); |
| 485 | // This is session-fatal. |
| 486 | return NS_ERROR_FAILURE; |
| 487 | } |
| 488 | chainBit = mData[mOffset] & 0x80; |
| 489 | accum += (mData[mOffset] & 0x7f) * factor; |
| 490 | ++mOffset; |
| 491 | factor = factor * 128; |
| 492 | } |
| 493 | return NS_OK; |
| 494 | } |
| 495 | |
| 496 | static bool HasConnectionBasedAuth(const nsACString& headerValue) { |
| 497 | for (const nsACString& authMethod : |
| 498 | nsCCharSeparatedTokenizer(headerValue, '\n').ToRange()) { |
| 499 | if (authMethod.LowerCaseEqualsLiteral("ntlm")) { |
| 500 | return true; |
| 501 | } |
| 502 | if (authMethod.LowerCaseEqualsLiteral("negotiate")) { |
| 503 | return true; |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | return false; |
| 508 | } |
| 509 | |
| 510 | nsresult Http2Decompressor::OutputHeader(const nsACString& name, |
| 511 | const nsACString& value) { |
| 512 | // exclusions |
| 513 | if (!mIsPush && |
| 514 | (name.EqualsLiteral("connection") || name.EqualsLiteral("host") || |
| 515 | name.EqualsLiteral("keep-alive") || |
| 516 | name.EqualsLiteral("proxy-connection") || name.EqualsLiteral("te") || |
| 517 | name.EqualsLiteral("transfer-encoding") || |
| 518 | name.EqualsLiteral("upgrade") || name.Equals(("accept-encoding")))) { |
| 519 | nsCString toLog(name); |
| 520 | LOG(("HTTP Decompressor illegal response header found, not gatewaying: %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor illegal response header found, not gatewaying: %s" , toLog.get()); } } while (0) |
| 521 | toLog.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor illegal response header found, not gatewaying: %s" , toLog.get()); } } while (0); |
| 522 | return NS_OK; |
| 523 | } |
| 524 | |
| 525 | // Bug 1663836: reject invalid HTTP response header names - RFC7540 Sec 10.3 |
| 526 | const char* cFirst = name.BeginReading(); |
| 527 | if (cFirst != nullptr && *cFirst == ':') { |
| 528 | ++cFirst; |
| 529 | } |
| 530 | if (!nsHttp::IsValidToken(cFirst, name.EndReading())) { |
| 531 | nsCString toLog(name); |
| 532 | LOG(("HTTP Decompressor invalid response header found. [%s]\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor invalid response header found. [%s]\n", toLog .get()); } } while (0) |
| 533 | toLog.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor invalid response header found. [%s]\n", toLog .get()); } } while (0); |
| 534 | return NS_ERROR_ILLEGAL_VALUE; |
| 535 | } |
| 536 | |
| 537 | // Look for upper case characters in the name. |
| 538 | for (const char* cPtr = name.BeginReading(); cPtr && cPtr < name.EndReading(); |
| 539 | ++cPtr) { |
| 540 | if (*cPtr <= 'Z' && *cPtr >= 'A') { |
| 541 | nsCString toLog(name); |
| 542 | LOG(("HTTP Decompressor upper case response header found. [%s]\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor upper case response header found. [%s]\n" , toLog.get()); } } while (0) |
| 543 | toLog.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor upper case response header found. [%s]\n" , toLog.get()); } } while (0); |
| 544 | return NS_ERROR_ILLEGAL_VALUE; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | // Look for CR, LF or NUL in value - could be smuggling (RFC7540 Sec 10.3) |
| 549 | // treat as malformed |
| 550 | if (!nsHttp::IsReasonableHeaderValue(value)) { |
| 551 | return NS_ERROR_ILLEGAL_VALUE; |
| 552 | } |
| 553 | |
| 554 | // Status comes first |
| 555 | if (name.EqualsLiteral(":status")) { |
| 556 | nsAutoCString status("HTTP/2 "_ns); |
| 557 | status.Append(value); |
| 558 | status.AppendLiteral("\r\n"); |
| 559 | mOutput->Insert(status, 0); |
| 560 | mHeaderStatus = value; |
| 561 | } else if (name.EqualsLiteral(":authority")) { |
| 562 | mHeaderHost = value; |
| 563 | } else if (name.EqualsLiteral(":scheme")) { |
| 564 | mHeaderScheme = value; |
| 565 | } else if (name.EqualsLiteral(":path")) { |
| 566 | mHeaderPath = value; |
| 567 | } else if (name.EqualsLiteral(":method")) { |
| 568 | mHeaderMethod = value; |
| 569 | } |
| 570 | |
| 571 | // http/2 transport level headers shouldn't be gatewayed into http/1 |
| 572 | bool isColonHeader = false; |
| 573 | for (const char* cPtr = name.BeginReading(); cPtr && cPtr < name.EndReading(); |
| 574 | ++cPtr) { |
| 575 | if (*cPtr == ':') { |
| 576 | isColonHeader = true; |
| 577 | break; |
| 578 | } |
| 579 | if (*cPtr != ' ' && *cPtr != '\t') { |
| 580 | isColonHeader = false; |
| 581 | break; |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | if (isColonHeader) { |
| 586 | // :status is the only pseudo-header field allowed in received HEADERS |
| 587 | // frames, PUSH_PROMISE allows the other pseudo-header fields |
| 588 | if (!name.EqualsLiteral(":status") && !mIsPush) { |
| 589 | LOG(("HTTP Decompressor found illegal response pseudo-header %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor found illegal response pseudo-header %s" , name.BeginReading()); } } while (0) |
| 590 | name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor found illegal response pseudo-header %s" , name.BeginReading()); } } while (0); |
| 591 | return NS_ERROR_ILLEGAL_VALUE; |
| 592 | } |
| 593 | if (mSeenNonColonHeader) { |
| 594 | LOG(("HTTP Decompressor found illegal : header %s", name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor found illegal : header %s", name.BeginReading ()); } } while (0); |
| 595 | return NS_ERROR_ILLEGAL_VALUE; |
| 596 | } |
| 597 | LOG(("HTTP Decompressor not gatewaying %s into http/1",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor not gatewaying %s into http/1", name.BeginReading ()); } } while (0) |
| 598 | name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP Decompressor not gatewaying %s into http/1", name.BeginReading ()); } } while (0); |
| 599 | return NS_OK; |
| 600 | } |
| 601 | |
| 602 | LOG(("Http2Decompressor::OutputHeader %s %s", name.BeginReading(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::OutputHeader %s %s", name.BeginReading( ), value.BeginReading()); } } while (0) |
| 603 | value.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::OutputHeader %s %s", name.BeginReading( ), value.BeginReading()); } } while (0); |
| 604 | mSeenNonColonHeader = true; |
| 605 | mOutput->Append(name); |
| 606 | mOutput->AppendLiteral(": "); |
| 607 | mOutput->Append(value); |
| 608 | mOutput->AppendLiteral("\r\n"); |
| 609 | |
| 610 | // Need to check if the server is going to try to speak connection-based auth |
| 611 | // with us. If so, we need to kill this via h2, and dial back with http/1.1. |
| 612 | // Technically speaking, the server should've just reset or goaway'd us with |
| 613 | // HTTP_1_1_REQUIRED, but there are some busted servers out there, so we need |
| 614 | // to check on our own to work around them. |
| 615 | if (name.EqualsLiteral("www-authenticate") || |
| 616 | name.EqualsLiteral("proxy-authenticate")) { |
| 617 | if (HasConnectionBasedAuth(value)) { |
| 618 | LOG3(("Http2Decompressor %p connection-based auth found in %s", this,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Info)), 0))) { mozilla:: detail::log_print(moz_real_module, mozilla::LogLevel::Info, "Http2Decompressor %p connection-based auth found in %s" , this, name.BeginReading()); } } while (0) |
| 619 | name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Info)), 0))) { mozilla:: detail::log_print(moz_real_module, mozilla::LogLevel::Info, "Http2Decompressor %p connection-based auth found in %s" , this, name.BeginReading()); } } while (0); |
| 620 | return NS_ERROR_NET_RESET; |
| 621 | } |
| 622 | } |
| 623 | return NS_OK; |
| 624 | } |
| 625 | |
| 626 | nsresult Http2Decompressor::OutputHeader(uint32_t index) { |
| 627 | // NWGH - make this < index |
| 628 | // bounds check |
| 629 | if (mHeaderTable.Length() <= index) { |
| 630 | LOG(("Http2Decompressor::OutputHeader index too large %u", index))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::OutputHeader index too large %u", index ); } } while (0); |
| 631 | // This is session-fatal. |
| 632 | return NS_ERROR_FAILURE; |
| 633 | } |
| 634 | |
| 635 | return OutputHeader(mHeaderTable[index]->mName, mHeaderTable[index]->mValue); |
| 636 | } |
| 637 | |
| 638 | nsresult Http2Decompressor::CopyHeaderString(uint32_t index, nsACString& name) { |
| 639 | // NWGH - make this < index |
| 640 | // bounds check |
| 641 | if (mHeaderTable.Length() <= index) { |
| 642 | // This is session-fatal. |
| 643 | return NS_ERROR_FAILURE; |
| 644 | } |
| 645 | |
| 646 | name = mHeaderTable[index]->mName; |
| 647 | return NS_OK; |
| 648 | } |
| 649 | |
| 650 | nsresult Http2Decompressor::CopyStringFromInput(uint32_t bytes, |
| 651 | nsACString& val) { |
| 652 | if (mOffset + bytes > mDataLen) { |
| 653 | // This is session-fatal. |
| 654 | return NS_ERROR_FAILURE; |
| 655 | } |
| 656 | |
| 657 | val.Assign(reinterpret_cast<const char*>(mData) + mOffset, bytes); |
| 658 | mOffset += bytes; |
| 659 | return NS_OK; |
| 660 | } |
| 661 | |
| 662 | nsresult Http2Decompressor::DecodeFinalHuffmanCharacter( |
| 663 | const HuffmanIncomingTable* table, uint8_t& c, uint8_t& bitsLeft) { |
| 664 | MOZ_ASSERT(mOffset <= mDataLen)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mOffset <= mDataLen)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mOffset <= mDataLen))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mOffset <= mDataLen" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 664); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mOffset <= mDataLen" ")"); do { *((volatile int*)__null) = 664; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 665 | if (mOffset > mDataLen) { |
| 666 | NS_WARNING("DecodeFinalHuffmanCharacter would read beyond end of buffer")NS_DebugBreak(NS_DEBUG_WARNING, "DecodeFinalHuffmanCharacter would read beyond end of buffer" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 666); |
| 667 | return NS_ERROR_FAILURE; |
| 668 | } |
| 669 | uint8_t mask = (1 << bitsLeft) - 1; |
| 670 | uint8_t idx = mData[mOffset - 1] & mask; |
| 671 | idx <<= (8 - bitsLeft); |
| 672 | // Don't update bitsLeft yet, because we need to check that value against the |
| 673 | // number of bits used by our encoding later on. We'll update when we are sure |
| 674 | // how many bits we've actually used. |
| 675 | |
| 676 | if (table->IndexHasANextTable(idx)) { |
| 677 | // Can't chain to another table when we're all out of bits in the encoding |
| 678 | LOG(("DecodeFinalHuffmanCharacter trying to chain when we're out of bits"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "DecodeFinalHuffmanCharacter trying to chain when we're out of bits" ); } } while (0); |
| 679 | return NS_ERROR_FAILURE; |
| 680 | } |
| 681 | |
| 682 | const HuffmanIncomingEntry* entry = table->Entry(idx); |
| 683 | |
| 684 | if (bitsLeft < entry->mPrefixLen) { |
| 685 | // We don't have enough bits to actually make a match, this is some sort of |
| 686 | // invalid coding |
| 687 | LOG(("DecodeFinalHuffmanCharacter does't have enough bits to match"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "DecodeFinalHuffmanCharacter does't have enough bits to match" ); } } while (0); |
| 688 | return NS_ERROR_FAILURE; |
| 689 | } |
| 690 | |
| 691 | // This is a character! |
| 692 | if (entry->mValue == 256) { |
| 693 | // EOS |
| 694 | LOG(("DecodeFinalHuffmanCharacter actually decoded an EOS"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "DecodeFinalHuffmanCharacter actually decoded an EOS"); } } while (0); |
| 695 | return NS_ERROR_FAILURE; |
| 696 | } |
| 697 | c = static_cast<uint8_t>(entry->mValue & 0xFF); |
| 698 | bitsLeft -= entry->mPrefixLen; |
| 699 | |
| 700 | return NS_OK; |
| 701 | } |
| 702 | |
| 703 | uint8_t Http2Decompressor::ExtractByte(uint8_t bitsLeft, |
| 704 | uint32_t& bytesConsumed) { |
| 705 | MOZ_DIAGNOSTIC_ASSERT(mOffset < mDataLen)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mOffset < mDataLen)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mOffset < mDataLen))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("mOffset < mDataLen" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 705); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mOffset < mDataLen" ")"); do { *((volatile int*)__null) = 705; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 706 | uint8_t rv; |
| 707 | |
| 708 | if (bitsLeft) { |
| 709 | // Need to extract bitsLeft bits from the previous byte, and 8 - bitsLeft |
| 710 | // bits from the current byte |
| 711 | uint8_t mask = (1 << bitsLeft) - 1; |
| 712 | rv = (mData[mOffset - 1] & mask) << (8 - bitsLeft); |
| 713 | rv |= (mData[mOffset] & ~mask) >> bitsLeft; |
| 714 | } else { |
| 715 | rv = mData[mOffset]; |
| 716 | } |
| 717 | |
| 718 | // We always update these here, under the assumption that all 8 bits we got |
| 719 | // here will be used. These may be re-adjusted later in the case that we don't |
| 720 | // use up all 8 bits of the byte. |
| 721 | ++mOffset; |
| 722 | ++bytesConsumed; |
| 723 | |
| 724 | return rv; |
| 725 | } |
| 726 | |
| 727 | nsresult Http2Decompressor::DecodeHuffmanCharacter( |
| 728 | const HuffmanIncomingTable* table, uint8_t& c, uint32_t& bytesConsumed, |
| 729 | uint8_t& bitsLeft) { |
| 730 | uint8_t idx = ExtractByte(bitsLeft, bytesConsumed); |
| 731 | |
| 732 | if (table->IndexHasANextTable(idx)) { |
| 733 | if (mOffset >= mDataLen) { |
| 734 | if (!bitsLeft || (mOffset > mDataLen)) { |
| 735 | // TODO - does this get me into trouble in the new world? |
| 736 | // No info left in input to try to consume, we're done |
| 737 | LOG(("DecodeHuffmanCharacter all out of bits to consume, can't chain"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "DecodeHuffmanCharacter all out of bits to consume, can't chain" ); } } while (0); |
| 738 | return NS_ERROR_FAILURE; |
| 739 | } |
| 740 | |
| 741 | // We might get lucky here! |
| 742 | return DecodeFinalHuffmanCharacter(table->NextTable(idx), c, bitsLeft); |
| 743 | } |
| 744 | |
| 745 | // We're sorry, Mario, but your princess is in another castle |
| 746 | return DecodeHuffmanCharacter(table->NextTable(idx), c, bytesConsumed, |
| 747 | bitsLeft); |
| 748 | } |
| 749 | |
| 750 | const HuffmanIncomingEntry* entry = table->Entry(idx); |
| 751 | if (entry->mValue == 256) { |
| 752 | LOG(("DecodeHuffmanCharacter found an actual EOS"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "DecodeHuffmanCharacter found an actual EOS"); } } while (0 ); |
| 753 | return NS_ERROR_FAILURE; |
| 754 | } |
| 755 | c = static_cast<uint8_t>(entry->mValue & 0xFF); |
| 756 | |
| 757 | // Need to adjust bitsLeft (and possibly other values) because we may not have |
| 758 | // consumed all of the bits of the byte we extracted. |
| 759 | if (entry->mPrefixLen <= bitsLeft) { |
| 760 | bitsLeft -= entry->mPrefixLen; |
| 761 | --mOffset; |
| 762 | --bytesConsumed; |
| 763 | } else { |
| 764 | bitsLeft = 8 - (entry->mPrefixLen - bitsLeft); |
| 765 | } |
| 766 | MOZ_ASSERT(bitsLeft < 8)do { static_assert( mozilla::detail::AssertionConditionType< decltype(bitsLeft < 8)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(bitsLeft < 8))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("bitsLeft < 8" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "bitsLeft < 8" ")"); do { *((volatile int*)__null) = 766; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 767 | |
| 768 | return NS_OK; |
| 769 | } |
| 770 | |
| 771 | nsresult Http2Decompressor::CopyHuffmanStringFromInput(uint32_t bytes, |
| 772 | nsACString& val) { |
| 773 | if (mOffset + bytes > mDataLen) { |
| 774 | LOG(("CopyHuffmanStringFromInput not enough data"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput not enough data"); } } while (0 ); |
| 775 | return NS_ERROR_FAILURE; |
| 776 | } |
| 777 | |
| 778 | uint32_t bytesRead = 0; |
| 779 | uint8_t bitsLeft = 0; |
| 780 | nsAutoCString buf; |
| 781 | nsresult rv; |
| 782 | uint8_t c; |
| 783 | |
| 784 | while (bytesRead < bytes) { |
| 785 | uint32_t bytesConsumed = 0; |
| 786 | rv = DecodeHuffmanCharacter(&HuffmanIncomingRoot, c, bytesConsumed, |
| 787 | bitsLeft); |
| 788 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 789 | LOG(("CopyHuffmanStringFromInput failed to decode a character"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput failed to decode a character"); } } while (0); |
| 790 | return rv; |
| 791 | } |
| 792 | |
| 793 | bytesRead += bytesConsumed; |
| 794 | buf.Append(c); |
| 795 | } |
| 796 | |
| 797 | if (bytesRead > bytes) { |
| 798 | LOG(("CopyHuffmanStringFromInput read more bytes than was allowed!"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput read more bytes than was allowed!" ); } } while (0); |
| 799 | return NS_ERROR_FAILURE; |
| 800 | } |
| 801 | |
| 802 | if (bitsLeft) { |
| 803 | // The shortest valid code is 4 bits, so we know there can be at most one |
| 804 | // character left that our loop didn't decode. Check to see if that's the |
| 805 | // case, and if so, add it to our output. |
| 806 | rv = DecodeFinalHuffmanCharacter(&HuffmanIncomingRoot, c, bitsLeft); |
| 807 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 808 | buf.Append(c); |
| 809 | } |
| 810 | } |
| 811 | |
| 812 | if (bitsLeft > 7) { |
| 813 | LOG(("CopyHuffmanStringFromInput more than 7 bits of padding"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput more than 7 bits of padding"); } } while (0); |
| 814 | return NS_ERROR_FAILURE; |
| 815 | } |
| 816 | |
| 817 | if (bitsLeft) { |
| 818 | // Any bits left at this point must belong to the EOS symbol, so make sure |
| 819 | // they make sense (ie, are all ones) |
| 820 | uint8_t mask = (1 << bitsLeft) - 1; |
| 821 | uint8_t bits = mData[mOffset - 1] & mask; |
| 822 | if (bits != mask) { |
| 823 | LOG(do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput ran out of data but found possible " "non-EOS symbol"); } } while (0) |
| 824 | ("CopyHuffmanStringFromInput ran out of data but found possible "do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput ran out of data but found possible " "non-EOS symbol"); } } while (0) |
| 825 | "non-EOS symbol"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput ran out of data but found possible " "non-EOS symbol"); } } while (0); |
| 826 | return NS_ERROR_FAILURE; |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | val = buf; |
| 831 | LOG(("CopyHuffmanStringFromInput decoded a full string!"))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "CopyHuffmanStringFromInput decoded a full string!"); } } while (0); |
| 832 | return NS_OK; |
| 833 | } |
| 834 | |
| 835 | nsresult Http2Decompressor::DoIndexed() { |
| 836 | // this starts with a 1 bit pattern |
| 837 | MOZ_ASSERT(mData[mOffset] & 0x80)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mData[mOffset] & 0x80)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mData[mOffset] & 0x80))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("mData[mOffset] & 0x80" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 837); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mData[mOffset] & 0x80" ")"); do { *((volatile int*)__null) = 837; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 838 | |
| 839 | // This is a 7 bit prefix |
| 840 | |
| 841 | uint32_t index; |
| 842 | nsresult rv = DecodeInteger(7, index); |
| 843 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 844 | return rv; |
| 845 | } |
| 846 | |
| 847 | LOG(("HTTP decompressor indexed entry %u\n", index))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor indexed entry %u\n", index); } } while ( 0); |
| 848 | |
| 849 | if (index == 0) { |
| 850 | return NS_ERROR_FAILURE; |
| 851 | } |
| 852 | // NWGH - remove this line, since we'll keep everything 1-indexed |
| 853 | index--; // Internally, we 0-index everything, since this is, y'know, C++ |
| 854 | |
| 855 | return OutputHeader(index); |
| 856 | } |
| 857 | |
| 858 | nsresult Http2Decompressor::DoLiteralInternal(nsACString& name, |
| 859 | nsACString& value, |
| 860 | uint32_t namePrefixLen) { |
| 861 | // guts of doliteralwithoutindex and doliteralwithincremental |
| 862 | MOZ_ASSERT(((mData[mOffset] & 0xF0) == 0x00) || // withoutindexdo { static_assert( mozilla::detail::AssertionConditionType< decltype(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" ")"); do { *((volatile int*)__null) = 864; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 863 | ((mData[mOffset] & 0xF0) == 0x10) || // neverindexeddo { static_assert( mozilla::detail::AssertionConditionType< decltype(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" ")"); do { *((volatile int*)__null) = 864; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 864 | ((mData[mOffset] & 0xC0) == 0x40))do { static_assert( mozilla::detail::AssertionConditionType< decltype(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset ] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "((mData[mOffset] & 0xF0) == 0x00) || ((mData[mOffset] & 0xF0) == 0x10) || ((mData[mOffset] & 0xC0) == 0x40)" ")"); do { *((volatile int*)__null) = 864; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); // withincremental |
| 865 | |
| 866 | // first let's get the name |
| 867 | uint32_t index; |
| 868 | nsresult rv = DecodeInteger(namePrefixLen, index); |
| 869 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 870 | return rv; |
| 871 | } |
| 872 | |
| 873 | // sanity check |
| 874 | if (mOffset >= mDataLen) { |
| 875 | NS_WARNING("Http2 Decompressor ran out of data")NS_DebugBreak(NS_DEBUG_WARNING, "Http2 Decompressor ran out of data" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 875); |
| 876 | // This is session-fatal |
| 877 | return NS_ERROR_FAILURE; |
| 878 | } |
| 879 | |
| 880 | bool isHuffmanEncoded; |
| 881 | |
| 882 | if (!index) { |
| 883 | // name is embedded as a literal |
| 884 | uint32_t nameLen; |
| 885 | isHuffmanEncoded = mData[mOffset] & (1 << 7); |
| 886 | rv = DecodeInteger(7, nameLen); |
| 887 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 888 | if (isHuffmanEncoded) { |
| 889 | rv = CopyHuffmanStringFromInput(nameLen, name); |
| 890 | } else { |
| 891 | rv = CopyStringFromInput(nameLen, name); |
| 892 | } |
| 893 | } |
| 894 | LOG(("Http2Decompressor::DoLiteralInternal literal name %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoLiteralInternal literal name %s", name .BeginReading()); } } while (0) |
| 895 | name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoLiteralInternal literal name %s", name .BeginReading()); } } while (0); |
| 896 | } else { |
| 897 | // NWGH - make this index, not index - 1 |
| 898 | // name is from headertable |
| 899 | rv = CopyHeaderString(index - 1, name); |
| 900 | LOG(("Http2Decompressor::DoLiteralInternal indexed name %d %s", index,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoLiteralInternal indexed name %d %s", index , name.BeginReading()); } } while (0) |
| 901 | name.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoLiteralInternal indexed name %d %s", index , name.BeginReading()); } } while (0); |
| 902 | } |
| 903 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 904 | return rv; |
| 905 | } |
| 906 | |
| 907 | // sanity check |
| 908 | if (mOffset >= mDataLen) { |
| 909 | NS_WARNING("Http2 Decompressor ran out of data")NS_DebugBreak(NS_DEBUG_WARNING, "Http2 Decompressor ran out of data" , nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 909); |
| 910 | // This is session-fatal |
| 911 | return NS_ERROR_FAILURE; |
| 912 | } |
| 913 | |
| 914 | // now the value |
| 915 | uint32_t valueLen; |
| 916 | isHuffmanEncoded = mData[mOffset] & (1 << 7); |
| 917 | rv = DecodeInteger(7, valueLen); |
| 918 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 919 | if (isHuffmanEncoded) { |
| 920 | rv = CopyHuffmanStringFromInput(valueLen, value); |
| 921 | } else { |
| 922 | rv = CopyStringFromInput(valueLen, value); |
| 923 | } |
| 924 | } |
| 925 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 926 | return rv; |
| 927 | } |
| 928 | |
| 929 | int32_t newline = 0; |
| 930 | while ((newline = value.FindChar('\n', newline)) != -1) { |
| 931 | if (value[newline + 1] == ' ' || value[newline + 1] == '\t') { |
| 932 | LOG(("Http2Decompressor::Disallowing folded header value %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::Disallowing folded header value %s", value .BeginReading()); } } while (0) |
| 933 | value.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::Disallowing folded header value %s", value .BeginReading()); } } while (0); |
| 934 | return NS_ERROR_ILLEGAL_VALUE; |
| 935 | } |
| 936 | // Increment this to avoid always finding the same newline and looping |
| 937 | // forever |
| 938 | ++newline; |
| 939 | } |
| 940 | |
| 941 | LOG(("Http2Decompressor::DoLiteralInternal value %s", value.BeginReading()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoLiteralInternal value %s", value.BeginReading ()); } } while (0); |
| 942 | return NS_OK; |
| 943 | } |
| 944 | |
| 945 | nsresult Http2Decompressor::DoLiteralWithoutIndex() { |
| 946 | // this starts with 0000 bit pattern |
| 947 | MOZ_ASSERT((mData[mOffset] & 0xF0) == 0x00)do { static_assert( mozilla::detail::AssertionConditionType< decltype((mData[mOffset] & 0xF0) == 0x00)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!((mData[mOffset] & 0xF0) == 0x00))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(mData[mOffset] & 0xF0) == 0x00", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mData[mOffset] & 0xF0) == 0x00" ")"); do { *((volatile int*)__null) = 947; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 948 | |
| 949 | nsAutoCString name, value; |
| 950 | nsresult rv = DoLiteralInternal(name, value, 4); |
| 951 | |
| 952 | LOG(("HTTP decompressor literal without index %s %s\n", name.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal without index %s %s\n", name.get (), value.get()); } } while (0) |
| 953 | value.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal without index %s %s\n", name.get (), value.get()); } } while (0); |
| 954 | |
| 955 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 956 | rv = OutputHeader(name, value); |
| 957 | } |
| 958 | return rv; |
| 959 | } |
| 960 | |
| 961 | nsresult Http2Decompressor::DoLiteralWithIncremental() { |
| 962 | // this starts with 01 bit pattern |
| 963 | MOZ_ASSERT((mData[mOffset] & 0xC0) == 0x40)do { static_assert( mozilla::detail::AssertionConditionType< decltype((mData[mOffset] & 0xC0) == 0x40)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!((mData[mOffset] & 0xC0) == 0x40))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(mData[mOffset] & 0xC0) == 0x40", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 963); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mData[mOffset] & 0xC0) == 0x40" ")"); do { *((volatile int*)__null) = 963; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 964 | |
| 965 | nsAutoCString name, value; |
| 966 | nsresult rv = DoLiteralInternal(name, value, 6); |
| 967 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 968 | rv = OutputHeader(name, value); |
| 969 | } |
| 970 | // Let NET_RESET continue on so that we don't get out of sync, as it is just |
| 971 | // used to kill the stream, not the session. |
| 972 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0))) && rv != NS_ERROR_NET_RESET) { |
| 973 | return rv; |
| 974 | } |
| 975 | |
| 976 | uint32_t room = nvPair(name, value).Size(); |
| 977 | if (room > mMaxBuffer) { |
| 978 | ClearHeaderTable(); |
| 979 | LOG(do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index not inserted due to size %u %s " "%s\n", room, name.get(), value.get()); } } while (0) |
| 980 | ("HTTP decompressor literal with index not inserted due to size %u %s "do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index not inserted due to size %u %s " "%s\n", room, name.get(), value.get()); } } while (0) |
| 981 | "%s\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index not inserted due to size %u %s " "%s\n", room, name.get(), value.get()); } } while (0) |
| 982 | room, name.get(), value.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index not inserted due to size %u %s " "%s\n", room, name.get(), value.get()); } } while (0); |
| 983 | DumpState("Decompressor state after ClearHeaderTable"); |
| 984 | return rv; |
| 985 | } |
| 986 | |
| 987 | MakeRoom(room, "decompressor"); |
| 988 | |
| 989 | // Incremental Indexing implicitly adds a row to the header table. |
| 990 | mHeaderTable.AddElement(name, value); |
| 991 | |
| 992 | uint32_t currentSize = mHeaderTable.ByteCount(); |
| 993 | if (currentSize > mPeakSize) { |
| 994 | mPeakSize = currentSize; |
| 995 | } |
| 996 | |
| 997 | uint32_t currentCount = mHeaderTable.VariableLength(); |
| 998 | if (currentCount > mPeakCount) { |
| 999 | mPeakCount = currentCount; |
| 1000 | } |
| 1001 | |
| 1002 | LOG(("HTTP decompressor literal with index 0 %s %s\n", name.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index 0 %s %s\n", name.get( ), value.get()); } } while (0) |
| 1003 | value.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal with index 0 %s %s\n", name.get( ), value.get()); } } while (0); |
| 1004 | |
| 1005 | return rv; |
| 1006 | } |
| 1007 | |
| 1008 | nsresult Http2Decompressor::DoLiteralNeverIndexed() { |
| 1009 | // This starts with 0001 bit pattern |
| 1010 | MOZ_ASSERT((mData[mOffset] & 0xF0) == 0x10)do { static_assert( mozilla::detail::AssertionConditionType< decltype((mData[mOffset] & 0xF0) == 0x10)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!((mData[mOffset] & 0xF0) == 0x10))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(mData[mOffset] & 0xF0) == 0x10", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 1010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mData[mOffset] & 0xF0) == 0x10" ")"); do { *((volatile int*)__null) = 1010; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1011 | |
| 1012 | nsAutoCString name, value; |
| 1013 | nsresult rv = DoLiteralInternal(name, value, 4); |
| 1014 | |
| 1015 | LOG(("HTTP decompressor literal never indexed %s %s\n", name.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal never indexed %s %s\n", name.get (), value.get()); } } while (0) |
| 1016 | value.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP decompressor literal never indexed %s %s\n", name.get (), value.get()); } } while (0); |
| 1017 | |
| 1018 | if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) { |
| 1019 | rv = OutputHeader(name, value); |
| 1020 | } |
| 1021 | return rv; |
| 1022 | } |
| 1023 | |
| 1024 | nsresult Http2Decompressor::DoContextUpdate() { |
| 1025 | // This starts with 001 bit pattern |
| 1026 | MOZ_ASSERT((mData[mOffset] & 0xE0) == 0x20)do { static_assert( mozilla::detail::AssertionConditionType< decltype((mData[mOffset] & 0xE0) == 0x20)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!((mData[mOffset] & 0xE0) == 0x20))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(mData[mOffset] & 0xE0) == 0x20", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/protocol/http/Http2Compression.cpp" , 1026); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mData[mOffset] & 0xE0) == 0x20" ")"); do { *((volatile int*)__null) = 1026; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1027 | |
| 1028 | // Getting here means we have to adjust the max table size, because the |
| 1029 | // compressor on the other end has signaled to us through HPACK (not H2) |
| 1030 | // that it's using a size different from the currently-negotiated size. |
| 1031 | // This change could either come about because we've sent a |
| 1032 | // SETTINGS_HEADER_TABLE_SIZE, or because the encoder has decided that |
| 1033 | // the current negotiated size doesn't fit its needs (for whatever reason) |
| 1034 | // and so it needs to change it (either up to the max allowed by our SETTING, |
| 1035 | // or down to some value below that) |
| 1036 | uint32_t newMaxSize; |
| 1037 | nsresult rv = DecodeInteger(5, newMaxSize); |
| 1038 | LOG(("Http2Decompressor::DoContextUpdate new maximum size %u", newMaxSize))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Decompressor::DoContextUpdate new maximum size %u", newMaxSize ); } } while (0); |
| 1039 | if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) { |
| 1040 | return rv; |
| 1041 | } |
| 1042 | |
| 1043 | if (newMaxSize > mMaxBufferSetting) { |
| 1044 | // This is fatal to the session - peer is trying to use a table larger |
| 1045 | // than we have made available. |
| 1046 | return NS_ERROR_FAILURE; |
| 1047 | } |
| 1048 | |
| 1049 | SetMaxBufferSizeInternal(newMaxSize); |
| 1050 | |
| 1051 | return NS_OK; |
| 1052 | } |
| 1053 | |
| 1054 | ///////////////////////////////////////////////////////////////// |
| 1055 | |
| 1056 | nsresult Http2Compressor::EncodeHeaderBlock( |
| 1057 | const nsCString& nvInput, const nsACString& method, const nsACString& path, |
| 1058 | const nsACString& host, const nsACString& scheme, |
| 1059 | const nsACString& protocol, bool simpleConnectForm, nsACString& output) { |
| 1060 | mSetInitialMaxBufferSizeAllowed = false; |
| 1061 | mOutput = &output; |
| 1062 | output.Truncate(); |
| 1063 | mParsedContentLength = -1; |
| 1064 | |
| 1065 | bool isWebsocket = (!simpleConnectForm && !protocol.IsEmpty()); |
| 1066 | |
| 1067 | // first thing's first - context size updates (if necessary) |
| 1068 | if (mBufferSizeChangeWaiting) { |
| 1069 | if (mLowestBufferSizeWaiting < mMaxBufferSetting) { |
| 1070 | EncodeTableSizeChange(mLowestBufferSizeWaiting); |
| 1071 | } |
| 1072 | EncodeTableSizeChange(mMaxBufferSetting); |
| 1073 | mBufferSizeChangeWaiting = false; |
| 1074 | } |
| 1075 | |
| 1076 | // colon headers first |
| 1077 | if (!simpleConnectForm) { |
| 1078 | ProcessHeader(nvPair(":method"_ns, method), false, false); |
| 1079 | ProcessHeader(nvPair(":path"_ns, path), true, false); |
| 1080 | ProcessHeader(nvPair(":authority"_ns, host), false, false); |
| 1081 | ProcessHeader(nvPair(":scheme"_ns, scheme), false, false); |
| 1082 | if (isWebsocket) { |
| 1083 | ProcessHeader(nvPair(":protocol"_ns, protocol), false, false); |
| 1084 | } |
| 1085 | } else { |
| 1086 | ProcessHeader(nvPair(":method"_ns, method), false, false); |
| 1087 | ProcessHeader(nvPair(":authority"_ns, host), false, false); |
| 1088 | } |
| 1089 | |
| 1090 | // now the non colon headers |
| 1091 | const char* beginBuffer = nvInput.BeginReading(); |
| 1092 | |
| 1093 | // This strips off the HTTP/1 method+path+version |
| 1094 | int32_t crlfIndex = nvInput.Find("\r\n"); |
| 1095 | while (true) { |
| 1096 | int32_t startIndex = crlfIndex + 2; |
| 1097 | |
| 1098 | crlfIndex = nvInput.Find("\r\n", startIndex); |
| 1099 | if (crlfIndex == -1) { |
| 1100 | break; |
| 1101 | } |
| 1102 | |
| 1103 | int32_t colonIndex = Substring(nvInput, 0, crlfIndex).Find(":", startIndex); |
| 1104 | if (colonIndex == -1) { |
| 1105 | break; |
| 1106 | } |
| 1107 | |
| 1108 | nsDependentCSubstring name = |
| 1109 | Substring(beginBuffer + startIndex, beginBuffer + colonIndex); |
| 1110 | // all header names are lower case in http/2 |
| 1111 | ToLowerCase(name); |
| 1112 | |
| 1113 | // exclusions |
| 1114 | if (name.EqualsLiteral("connection") || name.EqualsLiteral("host") || |
| 1115 | name.EqualsLiteral("keep-alive") || |
| 1116 | name.EqualsLiteral("proxy-connection") || name.EqualsLiteral("te") || |
| 1117 | name.EqualsLiteral("transfer-encoding") || |
| 1118 | name.EqualsLiteral("upgrade") || |
| 1119 | name.EqualsLiteral("sec-websocket-key")) { |
| 1120 | continue; |
| 1121 | } |
| 1122 | |
| 1123 | // colon headers are for http/2 and this is http/1 input, so that |
| 1124 | // is probably a smuggling attack of some kind |
| 1125 | bool isColonHeader = false; |
| 1126 | for (const char* cPtr = name.BeginReading(); |
| 1127 | cPtr && cPtr < name.EndReading(); ++cPtr) { |
| 1128 | if (*cPtr == ':') { |
| 1129 | isColonHeader = true; |
| 1130 | break; |
| 1131 | } |
| 1132 | if (*cPtr != ' ' && *cPtr != '\t') { |
| 1133 | isColonHeader = false; |
| 1134 | break; |
| 1135 | } |
| 1136 | } |
| 1137 | if (isColonHeader) { |
| 1138 | continue; |
| 1139 | } |
| 1140 | |
| 1141 | int32_t valueIndex = colonIndex + 1; |
| 1142 | |
| 1143 | while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ') { |
| 1144 | ++valueIndex; |
| 1145 | } |
| 1146 | |
| 1147 | nsDependentCSubstring value = |
| 1148 | Substring(beginBuffer + valueIndex, beginBuffer + crlfIndex); |
| 1149 | |
| 1150 | if (name.EqualsLiteral("content-length")) { |
| 1151 | int64_t len; |
| 1152 | nsCString tmp(value); |
| 1153 | if (nsHttp::ParseInt64(tmp.get(), nullptr, &len)) { |
| 1154 | mParsedContentLength = len; |
| 1155 | } |
| 1156 | } |
| 1157 | |
| 1158 | if (name.EqualsLiteral("cookie")) { |
| 1159 | // cookie crumbling |
| 1160 | bool haveMoreCookies = true; |
| 1161 | int32_t nextCookie = valueIndex; |
| 1162 | while (haveMoreCookies) { |
| 1163 | int32_t semiSpaceIndex = |
| 1164 | Substring(nvInput, 0, crlfIndex).Find("; ", nextCookie); |
| 1165 | if (semiSpaceIndex == -1) { |
| 1166 | haveMoreCookies = false; |
| 1167 | semiSpaceIndex = crlfIndex; |
| 1168 | } |
| 1169 | nsDependentCSubstring cookie = |
| 1170 | Substring(beginBuffer + nextCookie, beginBuffer + semiSpaceIndex); |
| 1171 | // cookies less than 20 bytes are not indexed |
| 1172 | ProcessHeader(nvPair(name, cookie), false, cookie.Length() < 20); |
| 1173 | nextCookie = semiSpaceIndex + 2; |
| 1174 | } |
| 1175 | } else { |
| 1176 | // allow indexing of every non-cookie except authorization |
| 1177 | ProcessHeader(nvPair(name, value), false, |
| 1178 | name.EqualsLiteral("authorization")); |
| 1179 | } |
| 1180 | } |
| 1181 | |
| 1182 | // NB: This is a *really* ugly hack, but to do this in the right place (the |
| 1183 | // transaction) would require totally reworking how/when the transaction |
| 1184 | // creates its request stream, which is not worth the effort and risk of |
| 1185 | // breakage just to add one header only to h2 connections. |
| 1186 | if (!simpleConnectForm && !isWebsocket) { |
| 1187 | // Add in TE: trailers for regular requests |
| 1188 | nsAutoCString te("te"); |
| 1189 | nsAutoCString trailers("trailers"); |
| 1190 | ProcessHeader(nvPair(te, trailers), false, false); |
| 1191 | } |
| 1192 | |
| 1193 | mOutput = nullptr; |
| 1194 | DumpState("Compressor state after EncodeHeaderBlock"); |
| 1195 | return NS_OK; |
| 1196 | } |
| 1197 | |
| 1198 | void Http2Compressor::DoOutput(Http2Compressor::outputCode code, |
| 1199 | const class nvPair* pair, uint32_t index) { |
| 1200 | // start Byte needs to be calculated from the offset after |
| 1201 | // the opcode has been written out in case the output stream |
| 1202 | // buffer gets resized/relocated |
| 1203 | uint32_t offset = mOutput->Length(); |
| 1204 | uint8_t* startByte; |
| 1205 | |
| 1206 | switch (code) { |
| 1207 | case kNeverIndexedLiteral: |
| 1208 | LOG(do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p neverindex literal with name reference %u %s " "%s\n", this, index, pair->mName.get(), pair->mValue.get ()); } } while (0) |
| 1209 | ("HTTP compressor %p neverindex literal with name reference %u %s "do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p neverindex literal with name reference %u %s " "%s\n", this, index, pair->mName.get(), pair->mValue.get ()); } } while (0) |
| 1210 | "%s\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p neverindex literal with name reference %u %s " "%s\n", this, index, pair->mName.get(), pair->mValue.get ()); } } while (0) |
| 1211 | this, index, pair->mName.get(), pair->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p neverindex literal with name reference %u %s " "%s\n", this, index, pair->mName.get(), pair->mValue.get ()); } } while (0); |
| 1212 | |
| 1213 | // In this case, the index will have already been adjusted to be 1-based |
| 1214 | // instead of 0-based. |
| 1215 | EncodeInteger(4, index); // 0001 4 bit prefix |
| 1216 | startByte = |
| 1217 | reinterpret_cast<unsigned char*>(mOutput->BeginWriting()) + offset; |
| 1218 | *startByte = (*startByte & 0x0f) | 0x10; |
| 1219 | |
| 1220 | if (!index) { |
| 1221 | HuffmanAppend(pair->mName); |
| 1222 | } |
| 1223 | |
| 1224 | HuffmanAppend(pair->mValue); |
| 1225 | break; |
| 1226 | |
| 1227 | case kPlainLiteral: |
| 1228 | LOG(("HTTP compressor %p noindex literal with name reference %u %s %s\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p noindex literal with name reference %u %s %s\n" , this, index, pair->mName.get(), pair->mValue.get()); } } while (0) |
| 1229 | this, index, pair->mName.get(), pair->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p noindex literal with name reference %u %s %s\n" , this, index, pair->mName.get(), pair->mValue.get()); } } while (0); |
| 1230 | |
| 1231 | // In this case, the index will have already been adjusted to be 1-based |
| 1232 | // instead of 0-based. |
| 1233 | EncodeInteger(4, index); // 0000 4 bit prefix |
| 1234 | startByte = |
| 1235 | reinterpret_cast<unsigned char*>(mOutput->BeginWriting()) + offset; |
| 1236 | *startByte = *startByte & 0x0f; |
| 1237 | |
| 1238 | if (!index) { |
| 1239 | HuffmanAppend(pair->mName); |
| 1240 | } |
| 1241 | |
| 1242 | HuffmanAppend(pair->mValue); |
| 1243 | break; |
| 1244 | |
| 1245 | case kIndexedLiteral: |
| 1246 | LOG(("HTTP compressor %p literal with name reference %u %s %s\n", this,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p literal with name reference %u %s %s\n" , this, index, pair->mName.get(), pair->mValue.get()); } } while (0) |
| 1247 | index, pair->mName.get(), pair->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p literal with name reference %u %s %s\n" , this, index, pair->mName.get(), pair->mValue.get()); } } while (0); |
| 1248 | |
| 1249 | // In this case, the index will have already been adjusted to be 1-based |
| 1250 | // instead of 0-based. |
| 1251 | EncodeInteger(6, index); // 01 2 bit prefix |
| 1252 | startByte = |
| 1253 | reinterpret_cast<unsigned char*>(mOutput->BeginWriting()) + offset; |
| 1254 | *startByte = (*startByte & 0x3f) | 0x40; |
| 1255 | |
| 1256 | if (!index) { |
| 1257 | HuffmanAppend(pair->mName); |
| 1258 | } |
| 1259 | |
| 1260 | HuffmanAppend(pair->mValue); |
| 1261 | break; |
| 1262 | |
| 1263 | case kIndex: |
| 1264 | LOG(("HTTP compressor %p index %u %s %s\n", this, index,do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p index %u %s %s\n", this, index, pair-> mName.get(), pair->mValue.get()); } } while (0) |
| 1265 | pair->mName.get(), pair->mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p index %u %s %s\n", this, index, pair-> mName.get(), pair->mValue.get()); } } while (0); |
| 1266 | // NWGH - make this plain old index instead of index + 1 |
| 1267 | // In this case, we are passed the raw 0-based C index, and need to |
| 1268 | // increment to make it 1-based and comply with the spec |
| 1269 | EncodeInteger(7, index + 1); |
| 1270 | startByte = |
| 1271 | reinterpret_cast<unsigned char*>(mOutput->BeginWriting()) + offset; |
| 1272 | *startByte = *startByte | 0x80; // 1 1 bit prefix |
| 1273 | break; |
| 1274 | } |
| 1275 | } |
| 1276 | |
| 1277 | // writes the encoded integer onto the output |
| 1278 | void Http2Compressor::EncodeInteger(uint32_t prefixLen, uint32_t val) { |
| 1279 | uint32_t mask = (1 << prefixLen) - 1; |
| 1280 | uint8_t tmp; |
| 1281 | |
| 1282 | if (val < mask) { |
| 1283 | // 1 byte encoding! |
| 1284 | tmp = val; |
| 1285 | mOutput->Append(reinterpret_cast<char*>(&tmp), 1); |
| 1286 | return; |
| 1287 | } |
| 1288 | |
| 1289 | if (mask) { |
| 1290 | val -= mask; |
| 1291 | tmp = mask; |
| 1292 | mOutput->Append(reinterpret_cast<char*>(&tmp), 1); |
| 1293 | } |
| 1294 | |
| 1295 | uint32_t q, r; |
| 1296 | do { |
| 1297 | q = val / 128; |
| 1298 | r = val % 128; |
| 1299 | tmp = r; |
| 1300 | if (q) { |
| 1301 | tmp |= 0x80; // chain bit |
| 1302 | } |
| 1303 | val = q; |
| 1304 | mOutput->Append(reinterpret_cast<char*>(&tmp), 1); |
| 1305 | } while (q); |
| 1306 | } |
| 1307 | |
| 1308 | void Http2Compressor::HuffmanAppend(const nsCString& value) { |
| 1309 | nsAutoCString buf; |
| 1310 | uint8_t bitsLeft = 8; |
| 1311 | uint32_t length = value.Length(); |
| 1312 | uint32_t offset; |
| 1313 | uint8_t* startByte; |
| 1314 | |
| 1315 | for (uint32_t i = 0; i < length; ++i) { |
| 1316 | uint8_t idx = static_cast<uint8_t>(value[i]); |
| 1317 | uint8_t huffLength = HuffmanOutgoing[idx].mLength; |
| 1318 | uint32_t huffValue = HuffmanOutgoing[idx].mValue; |
| 1319 | |
| 1320 | if (bitsLeft < 8) { |
| 1321 | // Fill in the least significant <bitsLeft> bits of the previous byte |
| 1322 | // first |
| 1323 | uint32_t val; |
| 1324 | if (huffLength >= bitsLeft) { |
| 1325 | val = huffValue & ~((1 << (huffLength - bitsLeft)) - 1); |
| 1326 | val >>= (huffLength - bitsLeft); |
| 1327 | } else { |
| 1328 | val = huffValue << (bitsLeft - huffLength); |
| 1329 | } |
| 1330 | val &= ((1 << bitsLeft) - 1); |
| 1331 | offset = buf.Length() - 1; |
| 1332 | startByte = reinterpret_cast<unsigned char*>(buf.BeginWriting()) + offset; |
| 1333 | *startByte = *startByte | static_cast<uint8_t>(val & 0xFF); |
| 1334 | if (huffLength >= bitsLeft) { |
| 1335 | huffLength -= bitsLeft; |
| 1336 | bitsLeft = 8; |
| 1337 | } else { |
| 1338 | bitsLeft -= huffLength; |
| 1339 | huffLength = 0; |
| 1340 | } |
| 1341 | } |
| 1342 | |
| 1343 | while (huffLength >= 8) { |
| 1344 | uint32_t mask = ~((1 << (huffLength - 8)) - 1); |
| 1345 | uint8_t val = ((huffValue & mask) >> (huffLength - 8)) & 0xFF; |
| 1346 | buf.Append(reinterpret_cast<char*>(&val), 1); |
| 1347 | huffLength -= 8; |
| 1348 | } |
| 1349 | |
| 1350 | if (huffLength) { |
| 1351 | // Fill in the most significant <huffLength> bits of the next byte |
| 1352 | bitsLeft = 8 - huffLength; |
| 1353 | uint8_t val = (huffValue & ((1 << huffLength) - 1)) << bitsLeft; |
| 1354 | buf.Append(reinterpret_cast<char*>(&val), 1); |
| 1355 | } |
| 1356 | } |
| 1357 | |
| 1358 | if (bitsLeft != 8) { |
| 1359 | // Pad the last <bitsLeft> bits with ones, which corresponds to the EOS |
| 1360 | // encoding |
| 1361 | uint8_t val = (1 << bitsLeft) - 1; |
| 1362 | offset = buf.Length() - 1; |
| 1363 | startByte = reinterpret_cast<unsigned char*>(buf.BeginWriting()) + offset; |
| 1364 | *startByte = *startByte | val; |
| 1365 | } |
| 1366 | |
| 1367 | // Now we know how long our encoded string is, we can fill in our length |
| 1368 | uint32_t bufLength = buf.Length(); |
| 1369 | offset = mOutput->Length(); |
| 1370 | EncodeInteger(7, bufLength); |
| 1371 | startByte = |
| 1372 | reinterpret_cast<unsigned char*>(mOutput->BeginWriting()) + offset; |
| 1373 | *startByte = *startByte | 0x80; |
| 1374 | |
| 1375 | // Finally, we can add our REAL data! |
| 1376 | mOutput->Append(buf); |
| 1377 | LOG(do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::HuffmanAppend %p encoded %d byte original on %d " "bytes.\n", this, length, bufLength); } } while (0) |
| 1378 | ("Http2Compressor::HuffmanAppend %p encoded %d byte original on %d "do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::HuffmanAppend %p encoded %d byte original on %d " "bytes.\n", this, length, bufLength); } } while (0) |
| 1379 | "bytes.\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::HuffmanAppend %p encoded %d byte original on %d " "bytes.\n", this, length, bufLength); } } while (0) |
| 1380 | this, length, bufLength))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::HuffmanAppend %p encoded %d byte original on %d " "bytes.\n", this, length, bufLength); } } while (0); |
| 1381 | } |
| 1382 | |
| 1383 | void Http2Compressor::ProcessHeader(const nvPair inputPair, bool noLocalIndex, |
| 1384 | bool neverIndex) { |
| 1385 | uint32_t newSize = inputPair.Size(); |
| 1386 | uint32_t headerTableSize = mHeaderTable.Length(); |
| 1387 | uint32_t matchedIndex = 0u; |
| 1388 | uint32_t nameReference = 0u; |
| 1389 | bool match = false; |
| 1390 | |
| 1391 | LOG(("Http2Compressor::ProcessHeader %s %s", inputPair.mName.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::ProcessHeader %s %s", inputPair.mName.get (), inputPair.mValue.get()); } } while (0) |
| 1392 | inputPair.mValue.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "Http2Compressor::ProcessHeader %s %s", inputPair.mName.get (), inputPair.mValue.get()); } } while (0); |
| 1393 | |
| 1394 | // NWGH - make this index = 1; index <= headerTableSize; ++index |
| 1395 | for (uint32_t index = 0; index < headerTableSize; ++index) { |
| 1396 | if (mHeaderTable[index]->mName.Equals(inputPair.mName)) { |
| 1397 | // NWGH - make this nameReference = index |
| 1398 | nameReference = index + 1; |
| 1399 | if (mHeaderTable[index]->mValue.Equals(inputPair.mValue)) { |
| 1400 | match = true; |
| 1401 | matchedIndex = index; |
| 1402 | break; |
| 1403 | } |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | // We need to emit a new literal |
| 1408 | if (!match || noLocalIndex || neverIndex) { |
| 1409 | if (neverIndex) { |
| 1410 | DoOutput(kNeverIndexedLiteral, &inputPair, nameReference); |
| 1411 | DumpState("Compressor state after literal never index"); |
| 1412 | return; |
| 1413 | } |
| 1414 | |
| 1415 | if (noLocalIndex || (newSize > (mMaxBuffer / 2)) || (mMaxBuffer < 128)) { |
| 1416 | DoOutput(kPlainLiteral, &inputPair, nameReference); |
| 1417 | DumpState("Compressor state after literal without index"); |
| 1418 | return; |
| 1419 | } |
| 1420 | |
| 1421 | // make sure to makeroom() first so that any implied items |
| 1422 | // get preserved. |
| 1423 | MakeRoom(newSize, "compressor"); |
| 1424 | DoOutput(kIndexedLiteral, &inputPair, nameReference); |
| 1425 | |
| 1426 | mHeaderTable.AddElement(inputPair.mName, inputPair.mValue); |
| 1427 | LOG(("HTTP compressor %p new literal placed at index 0\n", this))do { const ::mozilla::LogModule* moz_real_module = mozilla::net ::gHttpLog; if ((__builtin_expect(!!(mozilla::detail::log_test (moz_real_module, mozilla::LogLevel::Verbose)), 0))) { mozilla ::detail::log_print(moz_real_module, mozilla::LogLevel::Verbose , "HTTP compressor %p new literal placed at index 0\n", this) ; } } while (0); |
| 1428 | DumpState("Compressor state after literal with index"); |
| 1429 | return; |
| 1430 | } |
| 1431 | |
| 1432 | // emit an index |
| 1433 | DoOutput(kIndex, &inputPair, matchedIndex); |
| 1434 | |
| 1435 | DumpState("Compressor state after index"); |
| 1436 | } |
| 1437 | |
| 1438 | void Http2Compressor::EncodeTableSizeChange(uint32_t newMaxSize) { |
| 1439 | uint32_t offset = mOutput->Length(); |
| 1440 | EncodeInteger(5, newMaxSize); |
| 1441 | uint8_t* startByte = |
| 1442 | reinterpret_cast<uint8_t*>(mOutput->BeginWriting()) + offset; |
| 1443 | *startByte = *startByte | 0x20; |
| 1444 | } |
| 1445 | |
| 1446 | void Http2Compressor::SetMaxBufferSize(uint32_t maxBufferSize) { |
| 1447 | mMaxBufferSetting = maxBufferSize; |
| 1448 | SetMaxBufferSizeInternal(maxBufferSize); |
| 1449 | if (!mBufferSizeChangeWaiting) { |
| 1450 | mBufferSizeChangeWaiting = true; |
| 1451 | mLowestBufferSizeWaiting = maxBufferSize; |
| 1452 | } else if (maxBufferSize < mLowestBufferSizeWaiting) { |
| 1453 | mLowestBufferSizeWaiting = maxBufferSize; |
| 1454 | } |
| 1455 | } |
| 1456 | |
| 1457 | } // namespace net |
| 1458 | } // namespace mozilla |