IPCStreamUtils.cpp (5752B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 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 #include "IPCStreamUtils.h" 8 9 #include "ipc/IPCMessageUtilsSpecializations.h" 10 11 #include "nsIHttpHeaderVisitor.h" 12 #include "nsIIPCSerializableInputStream.h" 13 #include "mozIRemoteLazyInputStream.h" 14 15 #include "mozilla/Assertions.h" 16 #include "mozilla/dom/File.h" 17 #include "mozilla/ipc/IPCStream.h" 18 #include "mozilla/ipc/InputStreamUtils.h" 19 #include "mozilla/InputStreamLengthHelper.h" 20 #include "mozilla/RemoteLazyInputStreamParent.h" 21 #include "nsIMIMEInputStream.h" 22 #include "nsNetCID.h" 23 24 using namespace mozilla::dom; 25 26 namespace mozilla::ipc { 27 28 namespace { 29 30 class MIMEStreamHeaderVisitor final : public nsIHttpHeaderVisitor { 31 public: 32 explicit MIMEStreamHeaderVisitor( 33 nsTArray<mozilla::ipc::HeaderEntry>& aHeaders) 34 : mHeaders(aHeaders) {} 35 36 NS_DECL_ISUPPORTS 37 NS_IMETHOD VisitHeader(const nsACString& aName, 38 const nsACString& aValue) override { 39 auto el = mHeaders.AppendElement(); 40 el->name() = aName; 41 el->value() = aValue; 42 return NS_OK; 43 } 44 45 private: 46 ~MIMEStreamHeaderVisitor() = default; 47 48 nsTArray<mozilla::ipc::HeaderEntry>& mHeaders; 49 }; 50 51 NS_IMPL_ISUPPORTS(MIMEStreamHeaderVisitor, nsIHttpHeaderVisitor) 52 53 static bool SerializeLazyInputStream(nsIInputStream* aStream, 54 IPCStream& aValue) { 55 MOZ_ASSERT(aStream); 56 MOZ_ASSERT(XRE_IsParentProcess()); 57 58 // If we're serializing a MIME stream, ensure we preserve header data which 59 // would not be preserved by a RemoteLazyInputStream wrapper. 60 if (nsCOMPtr<nsIMIMEInputStream> mimeStream = do_QueryInterface(aStream)) { 61 MIMEInputStreamParams params; 62 params.startedReading() = false; 63 64 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 65 new MIMEStreamHeaderVisitor(params.headers()); 66 if (NS_WARN_IF(NS_FAILED(mimeStream->VisitHeaders(visitor)))) { 67 return false; 68 } 69 70 nsCOMPtr<nsIInputStream> dataStream; 71 if (NS_FAILED(mimeStream->GetData(getter_AddRefs(dataStream)))) { 72 return false; 73 } 74 if (dataStream) { 75 IPCStream data; 76 if (!SerializeLazyInputStream(dataStream, data)) { 77 return false; 78 } 79 params.optionalStream().emplace(std::move(data.stream())); 80 } 81 82 aValue.stream() = std::move(params); 83 return true; 84 } 85 86 RefPtr<RemoteLazyInputStream> lazyStream = 87 RemoteLazyInputStream::WrapStream(aStream); 88 if (NS_WARN_IF(!lazyStream)) { 89 return false; 90 } 91 92 aValue.stream() = RemoteLazyInputStreamParams(WrapNotNull(lazyStream)); 93 94 return true; 95 } 96 97 } // anonymous namespace 98 99 bool SerializeIPCStream(already_AddRefed<nsIInputStream> aInputStream, 100 IPCStream& aValue, bool aAllowLazy) { 101 nsCOMPtr<nsIInputStream> stream(std::move(aInputStream)); 102 if (!stream) { 103 MOZ_ASSERT_UNREACHABLE( 104 "Use the Maybe<...> overload to serialize optional nsIInputStreams"); 105 return false; 106 } 107 108 // When requesting a delayed start stream from the parent process, serialize 109 // it as a remote lazy stream to avoid bloating payloads. 110 if (aAllowLazy && XRE_IsParentProcess()) { 111 return SerializeLazyInputStream(stream, aValue); 112 } 113 114 if (nsCOMPtr<nsIIPCSerializableInputStream> serializable = 115 do_QueryInterface(stream)) { 116 // If you change this size, please also update the payload size in 117 // test_reload_large_postdata.html. 118 const uint64_t kTooLargeStream = 1024 * 1024; 119 120 uint32_t sizeUsed = 0; 121 serializable->Serialize(aValue.stream(), kTooLargeStream, &sizeUsed); 122 123 MOZ_ASSERT(sizeUsed <= kTooLargeStream); 124 125 if (aValue.stream().type() == InputStreamParams::T__None) { 126 MOZ_CRASH("Serialize failed!"); 127 } 128 return true; 129 } 130 131 InputStreamHelper::SerializeInputStreamAsPipe(stream, aValue.stream()); 132 if (aValue.stream().type() == InputStreamParams::T__None) { 133 // This can fail from OOM, etc. We will likely MOZ_CRASH now 134 return false; 135 } 136 return true; 137 } 138 139 bool SerializeIPCStream(already_AddRefed<nsIInputStream> aInputStream, 140 Maybe<IPCStream>& aValue, bool aAllowLazy) { 141 nsCOMPtr<nsIInputStream> stream(std::move(aInputStream)); 142 if (!stream) { 143 aValue.reset(); 144 return true; 145 } 146 147 IPCStream value; 148 if (SerializeIPCStream(stream.forget(), value, aAllowLazy)) { 149 aValue = Some(value); 150 return true; 151 } 152 return false; 153 } 154 155 already_AddRefed<nsIInputStream> DeserializeIPCStream(const IPCStream& aValue) { 156 return InputStreamHelper::DeserializeInputStream(aValue.stream()); 157 } 158 159 already_AddRefed<nsIInputStream> DeserializeIPCStream( 160 const Maybe<IPCStream>& aValue) { 161 if (aValue.isNothing()) { 162 return nullptr; 163 } 164 165 return DeserializeIPCStream(aValue.ref()); 166 } 167 168 } // namespace mozilla::ipc 169 170 void IPC::ParamTraits<nsIInputStream*>::Write(IPC::MessageWriter* aWriter, 171 nsIInputStream* aParam) { 172 mozilla::Maybe<mozilla::ipc::IPCStream> stream; 173 if (!mozilla::ipc::SerializeIPCStream(do_AddRef(aParam), stream, 174 /* aAllowLazy */ true)) { 175 MOZ_CRASH("Failed to serialize nsIInputStream"); 176 } 177 178 WriteParam(aWriter, stream); 179 } 180 181 bool IPC::ParamTraits<nsIInputStream*>::Read(IPC::MessageReader* aReader, 182 RefPtr<nsIInputStream>* aResult) { 183 mozilla::Maybe<mozilla::ipc::IPCStream> ipcStream; 184 if (!ReadParam(aReader, &ipcStream)) { 185 return false; 186 } 187 188 *aResult = mozilla::ipc::DeserializeIPCStream(ipcStream); 189 return true; 190 }