nsMIMEInputStream.cpp (15597B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * The MIME stream separates headers and a datastream. It also allows 8 * automatic creation of the content-length header. 9 */ 10 11 #include "nsMIMEInputStream.h" 12 13 #include "ipc/IPCMessageUtils.h" 14 #include "mozilla/Mutex.h" 15 #include "mozilla/ipc/InputStreamUtils.h" 16 #include "nsCOMPtr.h" 17 #include "nsComponentManagerUtils.h" 18 #include "nsIAsyncInputStream.h" 19 #include "nsIClassInfoImpl.h" 20 #include "nsIHttpHeaderVisitor.h" 21 #include "nsIIPCSerializableInputStream.h" 22 #include "nsIInputStreamLength.h" 23 #include "nsIMIMEInputStream.h" 24 #include "nsISeekableStream.h" 25 #include "nsString.h" 26 27 using namespace mozilla::ipc; 28 using mozilla::Maybe; 29 30 class nsMIMEInputStream : public nsIMIMEInputStream, 31 public nsISeekableStream, 32 public nsIIPCSerializableInputStream, 33 public nsIAsyncInputStream, 34 public nsIInputStreamCallback, 35 public nsIInputStreamLength, 36 public nsIAsyncInputStreamLength, 37 public nsIInputStreamLengthCallback, 38 public nsICloneableInputStream { 39 virtual ~nsMIMEInputStream() = default; 40 41 public: 42 nsMIMEInputStream() = default; 43 44 NS_DECL_THREADSAFE_ISUPPORTS 45 NS_DECL_NSIINPUTSTREAM 46 NS_DECL_NSIMIMEINPUTSTREAM 47 NS_DECL_NSISEEKABLESTREAM 48 NS_DECL_NSITELLABLESTREAM 49 NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM 50 NS_DECL_NSIASYNCINPUTSTREAM 51 NS_DECL_NSIINPUTSTREAMCALLBACK 52 NS_DECL_NSIINPUTSTREAMLENGTH 53 NS_DECL_NSIASYNCINPUTSTREAMLENGTH 54 NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK 55 NS_DECL_NSICLONEABLEINPUTSTREAM 56 57 private: 58 struct MOZ_STACK_CLASS ReadSegmentsState { 59 nsCOMPtr<nsIInputStream> mThisStream; 60 nsWriteSegmentFun mWriter{nullptr}; 61 void* mClosure{nullptr}; 62 }; 63 static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure, 64 const char* aFromRawSegment, uint32_t aToOffset, 65 uint32_t aCount, uint32_t* aWriteCount); 66 67 bool IsSeekableInputStream() const; 68 bool IsAsyncInputStream() const; 69 bool IsInputStreamLength() const; 70 bool IsAsyncInputStreamLength() const; 71 bool IsCloneableInputStream() const; 72 73 nsTArray<HeaderEntry> mHeaders; 74 75 nsCOMPtr<nsIInputStream> mStream; 76 mozilla::Atomic<bool, mozilla::Relaxed> mStartedReading{false}; 77 78 mozilla::Mutex mMutex{"nsMIMEInputStream::mMutex"}; 79 nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback MOZ_GUARDED_BY(mMutex); 80 81 // This is protected by mutex. 82 nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback; 83 }; 84 85 NS_IMPL_ADDREF(nsMIMEInputStream) 86 NS_IMPL_RELEASE(nsMIMEInputStream) 87 88 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE, 89 NS_MIMEINPUTSTREAM_CID) 90 91 NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream) 92 NS_INTERFACE_MAP_ENTRY(nsIMIMEInputStream) 93 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMIMEInputStream) 94 NS_INTERFACE_MAP_ENTRY(nsITellableStream) 95 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableInputStream()) 96 NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) 97 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, IsAsyncInputStream()) 98 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback, 99 IsAsyncInputStream()) 100 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream) 101 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength, 102 IsInputStreamLength()) 103 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength, 104 IsAsyncInputStreamLength()) 105 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback, 106 IsAsyncInputStreamLength()) 107 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, 108 IsCloneableInputStream()) 109 NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream) 110 NS_INTERFACE_MAP_END 111 112 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream, nsIMIMEInputStream, 113 nsIAsyncInputStream, nsIInputStream, 114 nsISeekableStream, nsITellableStream) 115 116 NS_IMETHODIMP 117 nsMIMEInputStream::AddHeader(const char* aName, const char* aValue) { 118 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); 119 120 HeaderEntry* entry = mHeaders.AppendElement(); 121 entry->name().Append(aName); 122 entry->value().Append(aValue); 123 124 return NS_OK; 125 } 126 127 NS_IMETHODIMP 128 nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor* visitor) { 129 nsresult rv; 130 131 for (auto& header : mHeaders) { 132 rv = visitor->VisitHeader(header.name(), header.value()); 133 if (NS_FAILED(rv)) { 134 return rv; 135 } 136 } 137 return NS_OK; 138 } 139 140 NS_IMETHODIMP 141 nsMIMEInputStream::SetData(nsIInputStream* aStream) { 142 NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE); 143 144 mStream = aStream; 145 return NS_OK; 146 } 147 148 NS_IMETHODIMP 149 nsMIMEInputStream::GetData(nsIInputStream** aStream) { 150 NS_ENSURE_ARG_POINTER(aStream); 151 *aStream = do_AddRef(mStream).take(); 152 return NS_OK; 153 } 154 155 #define INITSTREAMS \ 156 if (!mStartedReading) { \ 157 NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \ 158 mStartedReading = true; \ 159 } 160 161 // Reset mStartedReading when Seek-ing to start 162 NS_IMETHODIMP 163 nsMIMEInputStream::Seek(int32_t whence, int64_t offset) { 164 NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); 165 166 nsresult rv; 167 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream); 168 169 if (whence == NS_SEEK_SET && offset == 0) { 170 rv = stream->Seek(whence, offset); 171 if (NS_SUCCEEDED(rv)) mStartedReading = false; 172 } else { 173 INITSTREAMS; 174 rv = stream->Seek(whence, offset); 175 } 176 177 return rv; 178 } 179 180 // Proxy ReadSegments since we need to be a good little nsIInputStream 181 NS_IMETHODIMP nsMIMEInputStream::ReadSegments(nsWriteSegmentFun aWriter, 182 void* aClosure, uint32_t aCount, 183 uint32_t* _retval) { 184 INITSTREAMS; 185 ReadSegmentsState state; 186 // Disambiguate ambiguous nsIInputStream. 187 state.mThisStream = 188 static_cast<nsIInputStream*>(static_cast<nsIMIMEInputStream*>(this)); 189 state.mWriter = aWriter; 190 state.mClosure = aClosure; 191 return mStream->ReadSegments(ReadSegCb, &state, aCount, _retval); 192 } 193 194 nsresult nsMIMEInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure, 195 const char* aFromRawSegment, 196 uint32_t aToOffset, uint32_t aCount, 197 uint32_t* aWriteCount) { 198 ReadSegmentsState* state = (ReadSegmentsState*)aClosure; 199 return (state->mWriter)(state->mThisStream, state->mClosure, aFromRawSegment, 200 aToOffset, aCount, aWriteCount); 201 } 202 203 /** 204 * Forward everything else to the mStream after calling INITSTREAMS 205 */ 206 207 // nsIInputStream 208 NS_IMETHODIMP nsMIMEInputStream::Close(void) { 209 INITSTREAMS; 210 return mStream->Close(); 211 } 212 NS_IMETHODIMP nsMIMEInputStream::Available(uint64_t* _retval) { 213 INITSTREAMS; 214 return mStream->Available(_retval); 215 } 216 NS_IMETHODIMP nsMIMEInputStream::StreamStatus() { 217 INITSTREAMS; 218 return mStream->StreamStatus(); 219 } 220 NS_IMETHODIMP nsMIMEInputStream::Read(char* buf, uint32_t count, 221 uint32_t* _retval) { 222 INITSTREAMS; 223 return mStream->Read(buf, count, _retval); 224 } 225 NS_IMETHODIMP nsMIMEInputStream::IsNonBlocking(bool* aNonBlocking) { 226 INITSTREAMS; 227 return mStream->IsNonBlocking(aNonBlocking); 228 } 229 230 // nsIAsyncInputStream 231 NS_IMETHODIMP 232 nsMIMEInputStream::CloseWithStatus(nsresult aStatus) { 233 INITSTREAMS; 234 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream); 235 return asyncStream->CloseWithStatus(aStatus); 236 } 237 238 NS_IMETHODIMP 239 nsMIMEInputStream::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags, 240 uint32_t aRequestedCount, 241 nsIEventTarget* aEventTarget) { 242 INITSTREAMS; 243 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream); 244 if (NS_WARN_IF(!asyncStream)) { 245 return NS_ERROR_FAILURE; 246 } 247 248 nsCOMPtr<nsIInputStreamCallback> callback = aCallback ? this : nullptr; 249 { 250 mozilla::MutexAutoLock lock(mMutex); 251 if (NS_WARN_IF(mAsyncWaitCallback && aCallback && 252 mAsyncWaitCallback != aCallback)) { 253 return NS_ERROR_FAILURE; 254 } 255 256 mAsyncWaitCallback = aCallback; 257 } 258 259 return asyncStream->AsyncWait(callback, aFlags, aRequestedCount, 260 aEventTarget); 261 } 262 263 // nsIInputStreamCallback 264 265 NS_IMETHODIMP 266 nsMIMEInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream) { 267 nsCOMPtr<nsIInputStreamCallback> callback; 268 269 { 270 mozilla::MutexAutoLock lock(mMutex); 271 272 // We have been canceled in the meanwhile. 273 if (!mAsyncWaitCallback) { 274 return NS_OK; 275 } 276 277 callback.swap(mAsyncWaitCallback); 278 } 279 280 MOZ_ASSERT(callback); 281 return callback->OnInputStreamReady(this); 282 } 283 284 // nsITellableStream 285 NS_IMETHODIMP nsMIMEInputStream::Tell(int64_t* _retval) { 286 INITSTREAMS; 287 nsCOMPtr<nsITellableStream> stream = do_QueryInterface(mStream); 288 return stream->Tell(_retval); 289 } 290 291 // nsISeekableStream 292 NS_IMETHODIMP nsMIMEInputStream::SetEOF(void) { 293 INITSTREAMS; 294 nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream); 295 return stream->SetEOF(); 296 } 297 298 /** 299 * Factory method used by do_CreateInstance 300 */ 301 302 nsresult nsMIMEInputStreamConstructor(REFNSIID iid, void** result) { 303 *result = nullptr; 304 305 RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream(); 306 if (!inst) return NS_ERROR_OUT_OF_MEMORY; 307 308 return inst->QueryInterface(iid, result); 309 } 310 311 void nsMIMEInputStream::SerializedComplexity(uint32_t aMaxSize, 312 uint32_t* aSizeUsed, 313 uint32_t* aPipes, 314 uint32_t* aTransferables) { 315 if (nsCOMPtr<nsIIPCSerializableInputStream> serializable = 316 do_QueryInterface(mStream)) { 317 InputStreamHelper::SerializedComplexity(mStream, aMaxSize, aSizeUsed, 318 aPipes, aTransferables); 319 } else { 320 *aPipes = 1; 321 } 322 } 323 324 void nsMIMEInputStream::Serialize(InputStreamParams& aParams, uint32_t aMaxSize, 325 uint32_t* aSizeUsed) { 326 MOZ_ASSERT(aSizeUsed); 327 *aSizeUsed = 0; 328 329 MIMEInputStreamParams params; 330 params.headers() = mHeaders.Clone(); 331 params.startedReading() = mStartedReading; 332 333 if (!mStream) { 334 aParams = params; 335 return; 336 } 337 338 InputStreamParams wrappedParams; 339 340 if (nsCOMPtr<nsIIPCSerializableInputStream> serializable = 341 do_QueryInterface(mStream)) { 342 InputStreamHelper::SerializeInputStream(mStream, wrappedParams, aMaxSize, 343 aSizeUsed); 344 } else { 345 // Falling back to sending the underlying stream over a pipe when 346 // sending an nsMIMEInputStream over IPC is potentially wasteful 347 // if it is sent several times. This can possibly happen with 348 // fission. There are two ways to improve this, see bug 1648369 349 // and bug 1648370. 350 InputStreamHelper::SerializeInputStreamAsPipe(mStream, wrappedParams); 351 } 352 353 NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None, 354 "Wrapped stream failed to serialize!"); 355 356 params.optionalStream().emplace(wrappedParams); 357 aParams = params; 358 } 359 360 bool nsMIMEInputStream::Deserialize(const InputStreamParams& aParams) { 361 if (aParams.type() != InputStreamParams::TMIMEInputStreamParams) { 362 NS_ERROR("Received unknown parameters from the other process!"); 363 return false; 364 } 365 366 const MIMEInputStreamParams& params = aParams.get_MIMEInputStreamParams(); 367 const Maybe<InputStreamParams>& wrappedParams = params.optionalStream(); 368 369 if (wrappedParams.isSome()) { 370 nsCOMPtr<nsIInputStream> stream; 371 stream = InputStreamHelper::DeserializeInputStream(wrappedParams.ref()); 372 if (!stream) { 373 NS_WARNING("Failed to deserialize wrapped stream!"); 374 return false; 375 } 376 377 MOZ_ALWAYS_SUCCEEDS(SetData(stream)); 378 } 379 380 mHeaders = params.headers().Clone(); 381 mStartedReading = params.startedReading(); 382 383 return true; 384 } 385 386 NS_IMETHODIMP 387 nsMIMEInputStream::Length(int64_t* aLength) { 388 nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream); 389 if (NS_WARN_IF(!stream)) { 390 return NS_ERROR_FAILURE; 391 } 392 393 return stream->Length(aLength); 394 } 395 396 NS_IMETHODIMP 397 nsMIMEInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback, 398 nsIEventTarget* aEventTarget) { 399 nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream); 400 if (NS_WARN_IF(!stream)) { 401 return NS_ERROR_FAILURE; 402 } 403 404 nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr; 405 { 406 mozilla::MutexAutoLock lock(mMutex); 407 mAsyncInputStreamLengthCallback = aCallback; 408 } 409 410 return stream->AsyncLengthWait(callback, aEventTarget); 411 } 412 413 NS_IMETHODIMP 414 nsMIMEInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream, 415 int64_t aLength) { 416 nsCOMPtr<nsIInputStreamLengthCallback> callback; 417 { 418 mozilla::MutexAutoLock lock(mMutex); 419 // We have been canceled in the meanwhile. 420 if (!mAsyncInputStreamLengthCallback) { 421 return NS_OK; 422 } 423 424 callback.swap(mAsyncInputStreamLengthCallback); 425 } 426 427 MOZ_ASSERT(callback); 428 return callback->OnInputStreamLengthReady(this, aLength); 429 } 430 431 bool nsMIMEInputStream::IsSeekableInputStream() const { 432 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); 433 return !!seekable; 434 } 435 436 bool nsMIMEInputStream::IsAsyncInputStream() const { 437 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream); 438 return !!asyncStream; 439 } 440 441 bool nsMIMEInputStream::IsInputStreamLength() const { 442 nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream); 443 return !!stream; 444 } 445 446 bool nsMIMEInputStream::IsAsyncInputStreamLength() const { 447 nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream); 448 return !!stream; 449 } 450 451 bool nsMIMEInputStream::IsCloneableInputStream() const { 452 nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream); 453 return !!stream; 454 } 455 456 // nsICloneableInputStream interface 457 458 NS_IMETHODIMP 459 nsMIMEInputStream::GetCloneable(bool* aCloneable) { 460 nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream); 461 if (!mStream) { 462 return NS_ERROR_FAILURE; 463 } 464 465 return stream->GetCloneable(aCloneable); 466 } 467 468 NS_IMETHODIMP 469 nsMIMEInputStream::Clone(nsIInputStream** aResult) { 470 nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream); 471 if (!mStream) { 472 return NS_ERROR_FAILURE; 473 } 474 475 nsCOMPtr<nsIInputStream> clonedStream; 476 nsresult rv = stream->Clone(getter_AddRefs(clonedStream)); 477 if (NS_WARN_IF(NS_FAILED(rv))) { 478 return rv; 479 } 480 481 nsCOMPtr<nsIMIMEInputStream> mimeStream = new nsMIMEInputStream(); 482 483 rv = mimeStream->SetData(clonedStream); 484 if (NS_WARN_IF(NS_FAILED(rv))) { 485 return rv; 486 } 487 488 for (const HeaderEntry& entry : mHeaders) { 489 rv = mimeStream->AddHeader(entry.name().get(), entry.value().get()); 490 MOZ_ASSERT(NS_SUCCEEDED(rv)); 491 } 492 493 static_cast<nsMIMEInputStream*>(mimeStream.get())->mStartedReading = 494 static_cast<bool>(mStartedReading); 495 496 mimeStream.forget(aResult); 497 return NS_OK; 498 }