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 #include "Http2Session.h"
11 #include "nsHttp.h"
12 #include "nsHttpHandler.h"
13 #include "nsHttpRequestHead.h"
14 #include "nsISocketProvider.h"
15 #include "nsSocketProviderService.h"
16 #include "nsISSLSocketControl.h"
17 #include "nsISocketTransport.h"
18 #include "nsISupportsPriority.h"
19 #include "nsNetAddr.h"
20 #include "prerror.h"
21 #include "prio.h"
22 #include "TunnelUtils.h"
23 #include "nsNetCID.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsSocketTransport2.h"
27 #include "nsSocketTransportService2.h"
28 #include "mozilla/AutoRestore.h"
29 #include "mozilla/Mutex.h"
30
31 #if defined(FUZZING)
32 # include "FuzzySecurityInfo.h"
33 # include "mozilla/StaticPrefs_fuzzing.h"
34 #endif
35
36 namespace mozilla {
37 namespace net {
38
39 static PRDescIdentity sLayerIdentity;
40 static PRIOMethods sLayerMethods;
41 static PRIOMethods* sLayerMethodsPtr = nullptr;
42
TLSFilterTransaction(nsAHttpTransaction * aWrapped,const char * aTLSHost,int32_t aTLSPort,nsAHttpSegmentReader * aReader,nsAHttpSegmentWriter * aWriter)43 TLSFilterTransaction::TLSFilterTransaction(nsAHttpTransaction* aWrapped,
44 const char* aTLSHost,
45 int32_t aTLSPort,
46 nsAHttpSegmentReader* aReader,
47 nsAHttpSegmentWriter* aWriter)
48 : mTransaction(aWrapped),
49 mEncryptedTextUsed(0),
50 mEncryptedTextSize(0),
51 mSegmentReader(aReader),
52 mSegmentWriter(aWriter),
53 mFilterReadCode(NS_ERROR_NOT_INITIALIZED),
54 mFilterReadAmount(0),
55 mInOnReadSegment(false),
56 mForce(false),
57 mReadSegmentReturnValue(NS_OK),
58 mCloseReason(NS_ERROR_UNEXPECTED),
59 mNudgeCounter(0) {
60 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
61 LOG(("TLSFilterTransaction ctor %p\n", this));
62
63 nsCOMPtr<nsISocketProvider> provider;
64 nsCOMPtr<nsISocketProviderService> spserv =
65 nsSocketProviderService::GetOrCreate();
66
67 if (spserv) {
68 spserv->GetSocketProvider("ssl", getter_AddRefs(provider));
69 }
70
71 // Install an NSPR layer to handle getpeername() with a failure. This is kind
72 // of silly, but the default one used by the pipe asserts when called and the
73 // nss code calls it to see if we are connected to a real socket or not.
74 if (!sLayerMethodsPtr) {
75 // one time initialization
76 sLayerIdentity = PR_GetUniqueIdentity("TLSFilterTransaction Layer");
77 sLayerMethods = *PR_GetDefaultIOMethods();
78 sLayerMethods.getpeername = GetPeerName;
79 sLayerMethods.getsocketoption = GetSocketOption;
80 sLayerMethods.setsocketoption = SetSocketOption;
81 sLayerMethods.read = FilterRead;
82 sLayerMethods.write = FilterWrite;
83 sLayerMethods.send = FilterSend;
84 sLayerMethods.recv = FilterRecv;
85 sLayerMethods.close = FilterClose;
86 sLayerMethodsPtr = &sLayerMethods;
87 }
88
89 mFD = PR_CreateIOLayerStub(sLayerIdentity, &sLayerMethods);
90
91 bool addTLSLayer = true;
92 #ifdef FUZZING
93 addTLSLayer = !StaticPrefs::fuzzing_necko_enabled();
94 if (!addTLSLayer) {
95 SOCKET_LOG(("Skipping TLS layer in TLSFilterTransaction for fuzzing.\n"));
96
97 mSecInfo = static_cast<nsISupports*>(
98 static_cast<nsISSLSocketControl*>(new FuzzySecurityInfo()));
99 }
100 #endif
101
102 if (provider && mFD) {
103 mFD->secret = reinterpret_cast<PRFilePrivate*>(this);
104
105 if (addTLSLayer) {
106 provider->AddToSocket(PR_AF_INET, aTLSHost, aTLSPort, nullptr,
107 OriginAttributes(), 0, 0, mFD,
108 getter_AddRefs(mSecInfo));
109 }
110 }
111
112 if (mTransaction) {
113 nsCOMPtr<nsIInterfaceRequestor> callbacks;
114 mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
115 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(mSecInfo));
116 if (secCtrl) {
117 secCtrl->SetNotificationCallbacks(callbacks);
118 }
119 }
120 }
121
~TLSFilterTransaction()122 TLSFilterTransaction::~TLSFilterTransaction() {
123 LOG(("TLSFilterTransaction dtor %p\n", this));
124
125 // Prevent call to OnReadSegment from FilterOutput, our mSegmentReader is now
126 // an invalid pointer.
127 mInOnReadSegment = true;
128
129 Cleanup();
130 }
131
Cleanup()132 void TLSFilterTransaction::Cleanup() {
133 LOG(("TLSFilterTransaction::Cleanup %p", this));
134
135 if (mTransaction) {
136 mTransaction->Close(NS_ERROR_ABORT);
137 mTransaction = nullptr;
138 }
139
140 if (mFD) {
141 PR_Close(mFD);
142 mFD = nullptr;
143 }
144 mSecInfo = nullptr;
145 if (mTimer) {
146 mTimer->Cancel();
147 mTimer = nullptr;
148 }
149 }
150
Close(nsresult aReason)151 void TLSFilterTransaction::Close(nsresult aReason) {
152 LOG(("TLSFilterTransaction::Close %p %" PRIx32, this,
153 static_cast<uint32_t>(aReason)));
154
155 if (!mTransaction) {
156 return;
157 }
158
159 if (mTimer) {
160 mTimer->Cancel();
161 mTimer = nullptr;
162 }
163 mTransaction->Close(aReason);
164 mTransaction = nullptr;
165
166 if (gHttpHandler->Bug1563538()) {
167 if (NS_FAILED(aReason)) {
168 mCloseReason = aReason;
169 } else {
170 mCloseReason = NS_BASE_STREAM_CLOSED;
171 }
172 } else {
173 MOZ_ASSERT(NS_ERROR_UNEXPECTED == mCloseReason);
174 }
175 }
176
OnReadSegment(const char * aData,uint32_t aCount,uint32_t * outCountRead)177 nsresult TLSFilterTransaction::OnReadSegment(const char* aData, uint32_t aCount,
178 uint32_t* outCountRead) {
179 LOG(("TLSFilterTransaction %p OnReadSegment %d (buffered %d)\n", this, aCount,
180 mEncryptedTextUsed));
181
182 mReadSegmentReturnValue = NS_OK;
183 MOZ_ASSERT(mSegmentReader);
184 if (!mSecInfo) {
185 return NS_ERROR_FAILURE;
186 }
187
188 nsresult rv;
189 *outCountRead = 0;
190
191 // get rid of buffer first
192 if (mEncryptedTextUsed) {
193 rv = mSegmentReader->CommitToSegmentSize(mEncryptedTextUsed, mForce);
194 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
195 return rv;
196 }
197
198 uint32_t amt;
199 rv = mSegmentReader->OnReadSegment(mEncryptedText.get(), mEncryptedTextUsed,
200 &amt);
201 if (NS_FAILED(rv)) {
202 return rv;
203 }
204
205 mEncryptedTextUsed -= amt;
206 if (mEncryptedTextUsed) {
207 memmove(mEncryptedText.get(), &mEncryptedText[amt], mEncryptedTextUsed);
208 return NS_OK;
209 }
210 }
211
212 // encrypt for network write
213 // write aData down the SSL layer into the FilterWrite() method where it will
214 // be queued into mEncryptedText. We need to copy it like this in order to
215 // guarantee atomic writes
216
217 EnsureBuffer(mEncryptedText, aCount + 4096, 0, mEncryptedTextSize);
218
219 // Prevents call to OnReadSegment from inside FilterOutput, as we handle it
220 // here.
221 AutoRestore<bool> inOnReadSegment(mInOnReadSegment);
222 mInOnReadSegment = true;
223
224 while (aCount > 0) {
225 int32_t written = PR_Write(mFD, aData, aCount);
226 LOG(("TLSFilterTransaction %p OnReadSegment PRWrite(%d) = %d %d\n", this,
227 aCount, written, PR_GetError() == PR_WOULD_BLOCK_ERROR));
228
229 if (written < 1) {
230 if (*outCountRead) {
231 return NS_OK;
232 }
233 // mTransaction ReadSegments actually obscures this code, so
234 // keep it in a member var for this::ReadSegments to inspect. Similar
235 // to nsHttpConnection::mSocketOutCondition
236 PRErrorCode code = PR_GetError();
237 mReadSegmentReturnValue = ErrorAccordingToNSPR(code);
238
239 return mReadSegmentReturnValue;
240 }
241 aCount -= written;
242 aData += written;
243 *outCountRead += written;
244 mNudgeCounter = 0;
245 }
246
247 LOG(("TLSFilterTransaction %p OnReadSegment2 (buffered %d)\n", this,
248 mEncryptedTextUsed));
249
250 uint32_t amt = 0;
251 if (mEncryptedTextUsed) {
252 // If we are tunneled on spdy CommitToSegmentSize will prevent partial
253 // writes that could interfere with multiplexing. H1 is fine with
254 // partial writes.
255 rv = mSegmentReader->CommitToSegmentSize(mEncryptedTextUsed, mForce);
256 if (rv != NS_BASE_STREAM_WOULD_BLOCK) {
257 rv = mSegmentReader->OnReadSegment(mEncryptedText.get(),
258 mEncryptedTextUsed, &amt);
259 }
260
261 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
262 // return OK because all the data was consumed and stored in this buffer
263 // It is fine if the connection is null. We are likely a websocket and
264 // thus writing push is ensured by the caller.
265 if (Connection()) {
266 Connection()->TransactionHasDataToWrite(this);
267 }
268 return NS_OK;
269 }
270 if (NS_FAILED(rv)) {
271 return rv;
272 }
273 }
274
275 if (amt == mEncryptedTextUsed) {
276 mEncryptedText = nullptr;
277 mEncryptedTextUsed = 0;
278 mEncryptedTextSize = 0;
279 } else {
280 memmove(mEncryptedText.get(), &mEncryptedText[amt],
281 mEncryptedTextUsed - amt);
282 mEncryptedTextUsed -= amt;
283 }
284 return NS_OK;
285 }
286
FilterOutput(const char * aBuf,int32_t aAmount)287 int32_t TLSFilterTransaction::FilterOutput(const char* aBuf, int32_t aAmount) {
288 EnsureBuffer(mEncryptedText, mEncryptedTextUsed + aAmount, mEncryptedTextUsed,
289 mEncryptedTextSize);
290 memcpy(&mEncryptedText[mEncryptedTextUsed], aBuf, aAmount);
291 mEncryptedTextUsed += aAmount;
292
293 LOG(("TLSFilterTransaction::FilterOutput %p %d buffered=%u mSegmentReader=%p",
294 this, aAmount, mEncryptedTextUsed, mSegmentReader));
295
296 if (!mInOnReadSegment) {
297 // When called externally, we must make sure any newly written data is
298 // actually sent to the higher level connection.
299 // This also covers the case when PR_Read() wrote a re-negotioation
300 // response.
301 uint32_t notUsed;
302 Unused << OnReadSegment("", 0, ¬Used);
303 }
304
305 return aAmount;
306 }
307
CommitToSegmentSize(uint32_t size,bool forceCommitment)308 nsresult TLSFilterTransaction::CommitToSegmentSize(uint32_t size,
309 bool forceCommitment) {
310 if (!mSegmentReader) {
311 return NS_ERROR_FAILURE;
312 }
313
314 // pad the commit by a little bit to leave room for encryption overhead
315 // this isn't foolproof and we may still have to buffer, but its a good start
316 mForce = forceCommitment;
317 return mSegmentReader->CommitToSegmentSize(size + 1024, forceCommitment);
318 }
319
OnWriteSegment(char * aData,uint32_t aCount,uint32_t * outCountRead)320 nsresult TLSFilterTransaction::OnWriteSegment(char* aData, uint32_t aCount,
321 uint32_t* outCountRead) {
322 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
323 MOZ_ASSERT(mSegmentWriter);
324 LOG(("TLSFilterTransaction::OnWriteSegment %p max=%d\n", this, aCount));
325 if (!mSecInfo) {
326 return NS_ERROR_FAILURE;
327 }
328
329 // this will call through to FilterInput to get data from the higher
330 // level connection before removing the local TLS layer
331 mFilterReadCode = NS_OK;
332 mFilterReadAmount = 0;
333 int32_t bytesRead = PR_Read(mFD, aData, aCount);
334 if (bytesRead == -1) {
335 PRErrorCode code = PR_GetError();
336 if (code == PR_WOULD_BLOCK_ERROR) {
337 LOG(
338 ("TLSFilterTransaction::OnWriteSegment %p PR_Read would block, "
339 "actual read: %d\n",
340 this, mFilterReadAmount));
341
342 if (mFilterReadAmount == 0 && NS_SUCCEEDED(mFilterReadCode)) {
343 // No reading happened, but also no error occured, hence there is no
344 // condition to break the `again` loop, propagate WOULD_BLOCK through
345 // mFilterReadCode to break it and poll the socket again for reading.
346 mFilterReadCode = NS_BASE_STREAM_WOULD_BLOCK;
347 }
348 return NS_BASE_STREAM_WOULD_BLOCK;
349 }
350 // If reading from the socket succeeded (NS_SUCCEEDED(mFilterReadCode)),
351 // but the nss layer encountered an error remember the error.
352 if (NS_SUCCEEDED(mFilterReadCode)) {
353 mFilterReadCode = ErrorAccordingToNSPR(code);
354 LOG(("TLSFilterTransaction::OnWriteSegment %p nss error %" PRIx32 ".\n",
355 this, static_cast<uint32_t>(mFilterReadCode)));
356 }
357 return mFilterReadCode;
358 }
359 *outCountRead = bytesRead;
360
361 if (NS_SUCCEEDED(mFilterReadCode) && !bytesRead) {
362 LOG(
363 ("TLSFilterTransaction::OnWriteSegment %p "
364 "Second layer of TLS stripping results in STREAM_CLOSED\n",
365 this));
366 mFilterReadCode = NS_BASE_STREAM_CLOSED;
367 }
368
369 LOG(("TLSFilterTransaction::OnWriteSegment %p rv=%" PRIx32 " didread=%d "
370 "2 layers of ssl stripped to plaintext\n",
371 this, static_cast<uint32_t>(mFilterReadCode), bytesRead));
372 return mFilterReadCode;
373 }
374
FilterInput(char * aBuf,int32_t aAmount)375 int32_t TLSFilterTransaction::FilterInput(char* aBuf, int32_t aAmount) {
376 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
377 MOZ_ASSERT(mSegmentWriter);
378 LOG(("TLSFilterTransaction::FilterInput max=%d\n", aAmount));
379
380 uint32_t outCountRead = 0;
381 mFilterReadCode =
382 mSegmentWriter->OnWriteSegment(aBuf, aAmount, &outCountRead);
383 if (NS_SUCCEEDED(mFilterReadCode) && outCountRead) {
384 LOG(("TLSFilterTransaction::FilterInput rv=%" PRIx32
385 " read=%d input from net "
386 "1 layer stripped, 1 still on\n",
387 static_cast<uint32_t>(mFilterReadCode), outCountRead));
388 if (mReadSegmentReturnValue == NS_BASE_STREAM_WOULD_BLOCK) {
389 mNudgeCounter = 0;
390 }
391
392 mFilterReadAmount += outCountRead;
393 }
394 if (mFilterReadCode == NS_BASE_STREAM_WOULD_BLOCK) {
395 PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
396 return -1;
397 }
398 return outCountRead;
399 }
400
ReadSegments(nsAHttpSegmentReader * aReader,uint32_t aCount,uint32_t * outCountRead)401 nsresult TLSFilterTransaction::ReadSegments(nsAHttpSegmentReader* aReader,
402 uint32_t aCount,
403 uint32_t* outCountRead) {
404 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
405 LOG(("TLSFilterTransaction::ReadSegments %p max=%d\n", this, aCount));
406
407 if (!mTransaction) {
408 return mCloseReason;
409 }
410
411 mReadSegmentReturnValue = NS_OK;
412 mSegmentReader = aReader;
413 nsresult rv = mTransaction->ReadSegments(this, aCount, outCountRead);
414
415 // mSegmentReader is left assigned (not nullified) because we want to be able
416 // to call OnReadSegment directly, it expects mSegmentReader be non-null.
417
418 LOG(("TLSFilterTransaction %p called trans->ReadSegments rv=%" PRIx32 " %d\n",
419 this, static_cast<uint32_t>(rv), *outCountRead));
420 if (NS_SUCCEEDED(rv) &&
421 (mReadSegmentReturnValue == NS_BASE_STREAM_WOULD_BLOCK)) {
422 LOG(("TLSFilterTransaction %p read segment blocked found rv=%" PRIx32 "\n",
423 this, static_cast<uint32_t>(rv)));
424 if (Connection()) {
425 Unused << Connection()->ForceSend();
426 }
427 }
428
429 return NS_SUCCEEDED(rv) ? mReadSegmentReturnValue : rv;
430 }
431
WriteSegmentsAgain(nsAHttpSegmentWriter * aWriter,uint32_t aCount,uint32_t * outCountWritten,bool * again)432 nsresult TLSFilterTransaction::WriteSegmentsAgain(nsAHttpSegmentWriter* aWriter,
433 uint32_t aCount,
434 uint32_t* outCountWritten,
435 bool* again) {
436 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
437 LOG(("TLSFilterTransaction::WriteSegmentsAgain %p max=%d\n", this, aCount));
438
439 if (!mTransaction) {
440 return mCloseReason;
441 }
442
443 mSegmentWriter = aWriter;
444
445 nsresult rv =
446 mTransaction->WriteSegmentsAgain(this, aCount, outCountWritten, again);
447
448 if (NS_SUCCEEDED(rv) && !(*outCountWritten) && NS_FAILED(mFilterReadCode)) {
449 // nsPipe turns failures into silent OK.. undo that!
450 rv = mFilterReadCode;
451 if (Connection() && (mFilterReadCode == NS_BASE_STREAM_WOULD_BLOCK)) {
452 Unused << Connection()->ResumeRecv();
453 }
454 }
455 LOG(("TLSFilterTransaction %p called trans->WriteSegments rv=%" PRIx32
456 " %d\n",
457 this, static_cast<uint32_t>(rv), *outCountWritten));
458 return rv;
459 }
460
WriteSegments(nsAHttpSegmentWriter * aWriter,uint32_t aCount,uint32_t * outCountWritten)461 nsresult TLSFilterTransaction::WriteSegments(nsAHttpSegmentWriter* aWriter,
462 uint32_t aCount,
463 uint32_t* outCountWritten) {
464 bool again = false;
465 return WriteSegmentsAgain(aWriter, aCount, outCountWritten, &again);
466 }
467
GetTransactionSecurityInfo(nsISupports ** outSecInfo)468 nsresult TLSFilterTransaction::GetTransactionSecurityInfo(
469 nsISupports** outSecInfo) {
470 if (!mSecInfo) {
471 return NS_ERROR_FAILURE;
472 }
473
474 nsCOMPtr<nsISupports> temp(mSecInfo);
475 temp.forget(outSecInfo);
476 return NS_OK;
477 }
478
NudgeTunnel(NudgeTunnelCallback * aCallback)479 nsresult TLSFilterTransaction::NudgeTunnel(NudgeTunnelCallback* aCallback) {
480 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
481 LOG(("TLSFilterTransaction %p NudgeTunnel\n", this));
482 mNudgeCallback = nullptr;
483
484 if (!mSecInfo) {
485 return NS_ERROR_FAILURE;
486 }
487
488 nsCOMPtr<nsISSLSocketControl> ssl(do_QueryInterface(mSecInfo));
489 nsresult rv = ssl ? ssl->DriveHandshake() : NS_ERROR_FAILURE;
490 if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
491 // fatal handshake failure
492 LOG(("TLSFilterTransaction %p Fatal Handshake Failure: %d\n", this,
493 PR_GetError()));
494 return NS_ERROR_FAILURE;
495 }
496
497 uint32_t notUsed;
498 Unused << OnReadSegment("", 0, ¬Used);
499
500 // The SSL Layer does some unusual things with PR_Poll that makes it a bad
501 // match for multiplexed SSL sessions. We work around this by manually polling
502 // for the moment during the brief handshake phase or otherwise blocked on
503 // write. Thankfully this is a pretty unusual state. NSPR doesn't help us here
504 // - asserting when polling without the NSPR IO layer on the bottom of the
505 // stack. As a follow-on we can do some NSPR and maybe libssl changes to make
506 // this more event driven, but this is acceptable for getting started.
507
508 uint32_t counter = mNudgeCounter++;
509 uint32_t delay;
510
511 if (!counter) {
512 delay = 0;
513 } else if (counter < 8) { // up to 48ms at 6
514 delay = 6;
515 } else if (counter < 34) { // up to 499 ms at 17ms
516 delay = 17;
517 } else { // after that at 51ms (3 old windows ticks)
518 delay = 51;
519 }
520
521 if (!mTimer) {
522 mTimer = NS_NewTimer();
523 }
524
525 mNudgeCallback = aCallback;
526 if (!mTimer || NS_FAILED(mTimer->InitWithCallback(this, delay,
527 nsITimer::TYPE_ONE_SHOT))) {
528 return StartTimerCallback();
529 }
530
531 LOG(("TLSFilterTransaction %p NudgeTunnel timer started\n", this));
532 return NS_OK;
533 }
534
535 NS_IMETHODIMP
Notify(nsITimer * timer)536 TLSFilterTransaction::Notify(nsITimer* timer) {
537 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
538 LOG(("TLSFilterTransaction %p NudgeTunnel notify\n", this));
539
540 if (timer != mTimer) {
541 return NS_ERROR_UNEXPECTED;
542 }
543 nsresult rv = StartTimerCallback();
544 if (NS_FAILED(rv)) {
545 Close(rv);
546 }
547 return NS_OK;
548 }
549
550 NS_IMETHODIMP
GetName(nsACString & aName)551 TLSFilterTransaction::GetName(nsACString& aName) {
552 aName.AssignLiteral("TLSFilterTransaction");
553 return NS_OK;
554 }
555
StartTimerCallback()556 nsresult TLSFilterTransaction::StartTimerCallback() {
557 LOG(("TLSFilterTransaction %p NudgeTunnel StartTimerCallback %p\n", this,
558 mNudgeCallback.get()));
559
560 if (mNudgeCallback) {
561 // This class can be called re-entrantly, so cleanup m* before ->on()
562 RefPtr<NudgeTunnelCallback> cb(mNudgeCallback);
563 mNudgeCallback = nullptr;
564 return cb->OnTunnelNudged(this);
565 }
566 return NS_OK;
567 }
568
HasDataToRecv()569 bool TLSFilterTransaction::HasDataToRecv() {
570 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
571 if (!mFD) {
572 return false;
573 }
574 int32_t n = 0;
575 char c;
576 n = PR_Recv(mFD, &c, 1, PR_MSG_PEEK, 0);
577 return n > 0;
578 }
579
GetPeerName(PRFileDesc * aFD,PRNetAddr * addr)580 PRStatus TLSFilterTransaction::GetPeerName(PRFileDesc* aFD, PRNetAddr* addr) {
581 NetAddr peeraddr;
582 TLSFilterTransaction* self =
583 reinterpret_cast<TLSFilterTransaction*>(aFD->secret);
584
585 if (!self->mTransaction ||
586 NS_FAILED(self->mTransaction->Connection()->Transport()->GetPeerAddr(
587 &peeraddr))) {
588 return PR_FAILURE;
589 }
590 NetAddrToPRNetAddr(&peeraddr, addr);
591 return PR_SUCCESS;
592 }
593
GetSocketOption(PRFileDesc * aFD,PRSocketOptionData * aOpt)594 PRStatus TLSFilterTransaction::GetSocketOption(PRFileDesc* aFD,
595 PRSocketOptionData* aOpt) {
596 if (aOpt->option == PR_SockOpt_Nonblocking) {
597 aOpt->value.non_blocking = PR_TRUE;
598 return PR_SUCCESS;
599 }
600 return PR_FAILURE;
601 }
602
SetSocketOption(PRFileDesc * aFD,const PRSocketOptionData * aOpt)603 PRStatus TLSFilterTransaction::SetSocketOption(PRFileDesc* aFD,
604 const PRSocketOptionData* aOpt) {
605 return PR_FAILURE;
606 }
607
FilterClose(PRFileDesc * aFD)608 PRStatus TLSFilterTransaction::FilterClose(PRFileDesc* aFD) {
609 return PR_SUCCESS;
610 }
611
FilterWrite(PRFileDesc * aFD,const void * aBuf,int32_t aAmount)612 int32_t TLSFilterTransaction::FilterWrite(PRFileDesc* aFD, const void* aBuf,
613 int32_t aAmount) {
614 TLSFilterTransaction* self =
615 reinterpret_cast<TLSFilterTransaction*>(aFD->secret);
616 return self->FilterOutput(static_cast<const char*>(aBuf), aAmount);
617 }
618
FilterSend(PRFileDesc * aFD,const void * aBuf,int32_t aAmount,int,PRIntervalTime)619 int32_t TLSFilterTransaction::FilterSend(PRFileDesc* aFD, const void* aBuf,
620 int32_t aAmount, int, PRIntervalTime) {
621 return FilterWrite(aFD, aBuf, aAmount);
622 }
623
FilterRead(PRFileDesc * aFD,void * aBuf,int32_t aAmount)624 int32_t TLSFilterTransaction::FilterRead(PRFileDesc* aFD, void* aBuf,
625 int32_t aAmount) {
626 TLSFilterTransaction* self =
627 reinterpret_cast<TLSFilterTransaction*>(aFD->secret);
628 return self->FilterInput(static_cast<char*>(aBuf), aAmount);
629 }
630
FilterRecv(PRFileDesc * aFD,void * aBuf,int32_t aAmount,int,PRIntervalTime)631 int32_t TLSFilterTransaction::FilterRecv(PRFileDesc* aFD, void* aBuf,
632 int32_t aAmount, int, PRIntervalTime) {
633 return FilterRead(aFD, aBuf, aAmount);
634 }
635
636 /////
637 // The other methods of TLSFilterTransaction just call mTransaction->method
638 /////
639
SetConnection(nsAHttpConnection * aConnection)640 void TLSFilterTransaction::SetConnection(nsAHttpConnection* aConnection) {
641 if (!mTransaction) {
642 return;
643 }
644
645 mTransaction->SetConnection(aConnection);
646 }
647
Connection()648 nsAHttpConnection* TLSFilterTransaction::Connection() {
649 if (!mTransaction) {
650 return nullptr;
651 }
652 return mTransaction->Connection();
653 }
654
GetSecurityCallbacks(nsIInterfaceRequestor ** outCB)655 void TLSFilterTransaction::GetSecurityCallbacks(nsIInterfaceRequestor** outCB) {
656 if (!mTransaction) {
657 return;
658 }
659 mTransaction->GetSecurityCallbacks(outCB);
660 }
661
OnTransportStatus(nsITransport * aTransport,nsresult aStatus,int64_t aProgress)662 void TLSFilterTransaction::OnTransportStatus(nsITransport* aTransport,
663 nsresult aStatus,
664 int64_t aProgress) {
665 if (!mTransaction) {
666 return;
667 }
668 mTransaction->OnTransportStatus(aTransport, aStatus, aProgress);
669 }
670
ConnectionInfo()671 nsHttpConnectionInfo* TLSFilterTransaction::ConnectionInfo() {
672 if (!mTransaction) {
673 return nullptr;
674 }
675 return mTransaction->ConnectionInfo();
676 }
677
IsDone()678 bool TLSFilterTransaction::IsDone() {
679 if (!mTransaction) {
680 return true;
681 }
682 return mTransaction->IsDone();
683 }
684
Status()685 nsresult TLSFilterTransaction::Status() {
686 if (!mTransaction) {
687 return NS_ERROR_UNEXPECTED;
688 }
689
690 return mTransaction->Status();
691 }
692
Caps()693 uint32_t TLSFilterTransaction::Caps() {
694 if (!mTransaction) {
695 return 0;
696 }
697
698 return mTransaction->Caps();
699 }
700
SetProxyConnectFailed()701 void TLSFilterTransaction::SetProxyConnectFailed() {
702 if (!mTransaction) {
703 return;
704 }
705
706 mTransaction->SetProxyConnectFailed();
707 }
708
RequestHead()709 nsHttpRequestHead* TLSFilterTransaction::RequestHead() {
710 if (!mTransaction) {
711 return nullptr;
712 }
713
714 return mTransaction->RequestHead();
715 }
716
Http1xTransactionCount()717 uint32_t TLSFilterTransaction::Http1xTransactionCount() {
718 if (!mTransaction) {
719 return 0;
720 }
721
722 return mTransaction->Http1xTransactionCount();
723 }
724
TakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction>> & outTransactions)725 nsresult TLSFilterTransaction::TakeSubTransactions(
726 nsTArray<RefPtr<nsAHttpTransaction> >& outTransactions) {
727 LOG(("TLSFilterTransaction::TakeSubTransactions [this=%p] mTransaction %p\n",
728 this, mTransaction.get()));
729
730 if (!mTransaction) {
731 return NS_ERROR_UNEXPECTED;
732 }
733
734 if (mTransaction->TakeSubTransactions(outTransactions) ==
735 NS_ERROR_NOT_IMPLEMENTED) {
736 outTransactions.AppendElement(mTransaction);
737 }
738 mTransaction = nullptr;
739
740 return NS_OK;
741 }
742
SetProxiedTransaction(nsAHttpTransaction * aTrans,nsAHttpTransaction * aSpdyConnectTransaction)743 nsresult TLSFilterTransaction::SetProxiedTransaction(
744 nsAHttpTransaction* aTrans, nsAHttpTransaction* aSpdyConnectTransaction) {
745 LOG(
746 ("TLSFilterTransaction::SetProxiedTransaction [this=%p] aTrans=%p, "
747 "aSpdyConnectTransaction=%p\n",
748 this, aTrans, aSpdyConnectTransaction));
749
750 mTransaction = aTrans;
751
752 // Reverting mCloseReason to the default value for consistency to indicate we
753 // are no longer in closed state.
754 mCloseReason = NS_ERROR_UNEXPECTED;
755
756 nsCOMPtr<nsIInterfaceRequestor> callbacks;
757 mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
758 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(mSecInfo));
759 if (secCtrl && callbacks) {
760 secCtrl->SetNotificationCallbacks(callbacks);
761 }
762
763 mWeakTrans = do_GetWeakReference(aSpdyConnectTransaction);
764
765 return NS_OK;
766 }
767
IsNullTransaction()768 bool TLSFilterTransaction::IsNullTransaction() {
769 if (!mTransaction) {
770 return false;
771 }
772 return mTransaction->IsNullTransaction();
773 }
774
QueryNullTransaction()775 NullHttpTransaction* TLSFilterTransaction::QueryNullTransaction() {
776 if (!mTransaction) {
777 return nullptr;
778 }
779 return mTransaction->QueryNullTransaction();
780 }
781
QueryHttpTransaction()782 nsHttpTransaction* TLSFilterTransaction::QueryHttpTransaction() {
783 if (!mTransaction) {
784 return nullptr;
785 }
786 return mTransaction->QueryHttpTransaction();
787 }
788
789 class SocketInWrapper : public nsIAsyncInputStream,
790 public nsAHttpSegmentWriter {
791 NS_DECL_THREADSAFE_ISUPPORTS
792 NS_FORWARD_NSIASYNCINPUTSTREAM(mStream->)
793
SocketInWrapper(nsIAsyncInputStream * aWrapped,TLSFilterTransaction * aFilter)794 SocketInWrapper(nsIAsyncInputStream* aWrapped, TLSFilterTransaction* aFilter)
795 : mStream(aWrapped), mTLSFilter(aFilter) {}
796
Close()797 NS_IMETHOD Close() override {
798 mTLSFilter = nullptr;
799 return mStream->Close();
800 }
801
Available(uint64_t * _retval)802 NS_IMETHOD Available(uint64_t* _retval) override {
803 return mStream->Available(_retval);
804 }
805
IsNonBlocking(bool * _retval)806 NS_IMETHOD IsNonBlocking(bool* _retval) override {
807 return mStream->IsNonBlocking(_retval);
808 }
809
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * _retval)810 NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
811 uint32_t aCount, uint32_t* _retval) override {
812 return mStream->ReadSegments(aWriter, aClosure, aCount, _retval);
813 }
814
815 // finally, ones that don't get forwarded :)
816 NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* _retval) override;
817 virtual nsresult OnWriteSegment(char* segment, uint32_t count,
818 uint32_t* countWritten) override;
819
820 private:
821 virtual ~SocketInWrapper() = default;
822 ;
823
824 nsCOMPtr<nsIAsyncInputStream> mStream;
825 RefPtr<TLSFilterTransaction> mTLSFilter;
826 };
827
OnWriteSegment(char * segment,uint32_t count,uint32_t * countWritten)828 nsresult SocketInWrapper::OnWriteSegment(char* segment, uint32_t count,
829 uint32_t* countWritten) {
830 LOG(("SocketInWrapper OnWriteSegment %d %p filter=%p\n", count, this,
831 mTLSFilter.get()));
832
833 nsresult rv = mStream->Read(segment, count, countWritten);
834 LOG(("SocketInWrapper OnWriteSegment %p wrapped read %" PRIx32 " %d\n", this,
835 static_cast<uint32_t>(rv), *countWritten));
836 return rv;
837 }
838
839 NS_IMETHODIMP
Read(char * aBuf,uint32_t aCount,uint32_t * _retval)840 SocketInWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
841 LOG(("SocketInWrapper Read %d %p filter=%p\n", aCount, this,
842 mTLSFilter.get()));
843
844 if (!mTLSFilter) {
845 return NS_ERROR_UNEXPECTED; // protect potentially dangling mTLSFilter
846 }
847
848 // mTLSFilter->mSegmentWriter MUST be this at ctor time
849 return mTLSFilter->OnWriteSegment(aBuf, aCount, _retval);
850 }
851
852 class SocketOutWrapper : public nsIAsyncOutputStream,
853 public nsAHttpSegmentReader {
854 NS_DECL_THREADSAFE_ISUPPORTS
855 NS_FORWARD_NSIASYNCOUTPUTSTREAM(mStream->)
856
SocketOutWrapper(nsIAsyncOutputStream * aWrapped,TLSFilterTransaction * aFilter)857 SocketOutWrapper(nsIAsyncOutputStream* aWrapped,
858 TLSFilterTransaction* aFilter)
859 : mStream(aWrapped), mTLSFilter(aFilter) {}
860
Close()861 NS_IMETHOD Close() override {
862 mTLSFilter = nullptr;
863 return mStream->Close();
864 }
865
Flush()866 NS_IMETHOD Flush() override { return mStream->Flush(); }
867
IsNonBlocking(bool * _retval)868 NS_IMETHOD IsNonBlocking(bool* _retval) override {
869 return mStream->IsNonBlocking(_retval);
870 }
871
WriteSegments(nsReadSegmentFun aReader,void * aClosure,uint32_t aCount,uint32_t * _retval)872 NS_IMETHOD WriteSegments(nsReadSegmentFun aReader, void* aClosure,
873 uint32_t aCount, uint32_t* _retval) override {
874 return mStream->WriteSegments(aReader, aClosure, aCount, _retval);
875 }
876
WriteFrom(nsIInputStream * aFromStream,uint32_t aCount,uint32_t * _retval)877 NS_IMETHOD WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
878 uint32_t* _retval) override {
879 return mStream->WriteFrom(aFromStream, aCount, _retval);
880 }
881
882 // finally, ones that don't get forwarded :)
883 NS_IMETHOD Write(const char* aBuf, uint32_t aCount,
884 uint32_t* _retval) override;
885 virtual nsresult OnReadSegment(const char* segment, uint32_t count,
886 uint32_t* countWritten) override;
887
888 private:
889 virtual ~SocketOutWrapper() = default;
890 ;
891
892 nsCOMPtr<nsIAsyncOutputStream> mStream;
893 RefPtr<TLSFilterTransaction> mTLSFilter;
894 };
895
OnReadSegment(const char * segment,uint32_t count,uint32_t * countWritten)896 nsresult SocketOutWrapper::OnReadSegment(const char* segment, uint32_t count,
897 uint32_t* countWritten) {
898 return mStream->Write(segment, count, countWritten);
899 }
900
901 NS_IMETHODIMP
Write(const char * aBuf,uint32_t aCount,uint32_t * _retval)902 SocketOutWrapper::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) {
903 LOG(("SocketOutWrapper Write %d %p filter=%p\n", aCount, this,
904 mTLSFilter.get()));
905
906 // mTLSFilter->mSegmentReader MUST be this at ctor time
907 if (!mTLSFilter) {
908 return NS_ERROR_UNEXPECTED; // protect potentially dangling mTLSFilter
909 }
910
911 return mTLSFilter->OnReadSegment(aBuf, aCount, _retval);
912 }
913
newIODriver(nsIAsyncInputStream * aSocketIn,nsIAsyncOutputStream * aSocketOut,nsIAsyncInputStream ** outSocketIn,nsIAsyncOutputStream ** outSocketOut)914 void TLSFilterTransaction::newIODriver(nsIAsyncInputStream* aSocketIn,
915 nsIAsyncOutputStream* aSocketOut,
916 nsIAsyncInputStream** outSocketIn,
917 nsIAsyncOutputStream** outSocketOut) {
918 SocketInWrapper* inputWrapper = new SocketInWrapper(aSocketIn, this);
919 mSegmentWriter = inputWrapper;
920 nsCOMPtr<nsIAsyncInputStream> newIn(inputWrapper);
921 newIn.forget(outSocketIn);
922
923 SocketOutWrapper* outputWrapper = new SocketOutWrapper(aSocketOut, this);
924 mSegmentReader = outputWrapper;
925 nsCOMPtr<nsIAsyncOutputStream> newOut(outputWrapper);
926 newOut.forget(outSocketOut);
927 }
928
QuerySpdyConnectTransaction()929 SpdyConnectTransaction* TLSFilterTransaction::QuerySpdyConnectTransaction() {
930 if (!mTransaction) {
931 return nullptr;
932 }
933 return mTransaction->QuerySpdyConnectTransaction();
934 }
935
936 class WeakTransProxy final : public nsISupports {
937 public:
938 NS_DECL_THREADSAFE_ISUPPORTS
939
WeakTransProxy(SpdyConnectTransaction * aTrans)940 explicit WeakTransProxy(SpdyConnectTransaction* aTrans) {
941 MOZ_ASSERT(OnSocketThread());
942 mWeakTrans = do_GetWeakReference(aTrans);
943 }
944
QueryTransaction()945 already_AddRefed<NullHttpTransaction> QueryTransaction() {
946 MOZ_ASSERT(OnSocketThread());
947 RefPtr<NullHttpTransaction> trans = do_QueryReferent(mWeakTrans);
948 return trans.forget();
949 }
950
951 private:
~WeakTransProxy()952 ~WeakTransProxy() { MOZ_ASSERT(OnSocketThread()); }
953
954 nsWeakPtr mWeakTrans;
955 };
956
957 NS_IMPL_ISUPPORTS(WeakTransProxy, nsISupports)
958
959 class WeakTransFreeProxy final : public Runnable {
960 public:
WeakTransFreeProxy(WeakTransProxy * proxy)961 explicit WeakTransFreeProxy(WeakTransProxy* proxy)
962 : Runnable("WeakTransFreeProxy"), mProxy(proxy) {}
963
Run()964 NS_IMETHOD Run() override {
965 MOZ_ASSERT(OnSocketThread());
966 mProxy = nullptr;
967 return NS_OK;
968 }
969
Dispatch()970 void Dispatch() {
971 MOZ_ASSERT(!OnSocketThread());
972 nsCOMPtr<nsIEventTarget> sts =
973 do_GetService("@mozilla.org/network/socket-transport-service;1");
974 Unused << sts->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
975 }
976
977 private:
978 RefPtr<WeakTransProxy> mProxy;
979 };
980
981 class SocketTransportShim : public nsISocketTransport {
982 public:
983 NS_DECL_THREADSAFE_ISUPPORTS
984 NS_DECL_NSITRANSPORT
985 NS_DECL_NSISOCKETTRANSPORT
986
SocketTransportShim(SpdyConnectTransaction * aTrans,nsISocketTransport * aWrapped,bool aIsWebsocket)987 explicit SocketTransportShim(SpdyConnectTransaction* aTrans,
988 nsISocketTransport* aWrapped, bool aIsWebsocket)
989 : mWrapped(aWrapped), mIsWebsocket(aIsWebsocket) {
990 mWeakTrans = new WeakTransProxy(aTrans);
991 }
992
993 private:
~SocketTransportShim()994 virtual ~SocketTransportShim() {
995 if (!OnSocketThread()) {
996 RefPtr<WeakTransFreeProxy> p = new WeakTransFreeProxy(mWeakTrans);
997 mWeakTrans = nullptr;
998 p->Dispatch();
999 }
1000 }
1001
1002 nsCOMPtr<nsISocketTransport> mWrapped;
1003 bool mIsWebsocket;
1004 nsCOMPtr<nsIInterfaceRequestor> mSecurityCallbacks;
1005 RefPtr<WeakTransProxy> mWeakTrans; // SpdyConnectTransaction *
1006 };
1007
1008 class OutputStreamShim : public nsIAsyncOutputStream {
1009 public:
1010 NS_DECL_THREADSAFE_ISUPPORTS
1011 NS_DECL_NSIOUTPUTSTREAM
1012 NS_DECL_NSIASYNCOUTPUTSTREAM
1013
1014 friend class SpdyConnectTransaction;
1015 friend class WebsocketHasDataToWrite;
1016 friend class OutputCloseTransaction;
1017
OutputStreamShim(SpdyConnectTransaction * aTrans,bool aIsWebsocket)1018 OutputStreamShim(SpdyConnectTransaction* aTrans, bool aIsWebsocket)
1019 : mCallback(nullptr),
1020 mStatus(NS_OK),
1021 mMutex("OutputStreamShim"),
1022 mIsWebsocket(aIsWebsocket) {
1023 mWeakTrans = new WeakTransProxy(aTrans);
1024 }
1025
1026 already_AddRefed<nsIOutputStreamCallback> TakeCallback();
1027
1028 private:
~OutputStreamShim()1029 virtual ~OutputStreamShim() {
1030 if (!OnSocketThread()) {
1031 RefPtr<WeakTransFreeProxy> p = new WeakTransFreeProxy(mWeakTrans);
1032 mWeakTrans = nullptr;
1033 p->Dispatch();
1034 }
1035 }
1036
1037 RefPtr<WeakTransProxy> mWeakTrans; // SpdyConnectTransaction *
1038 nsCOMPtr<nsIOutputStreamCallback> mCallback;
1039 nsresult mStatus;
1040 mozilla::Mutex mMutex;
1041
1042 // Websockets
1043 bool mIsWebsocket;
1044 nsresult CallTransactionHasDataToWrite();
1045 nsresult CloseTransaction(nsresult reason);
1046 };
1047
1048 class InputStreamShim : public nsIAsyncInputStream {
1049 public:
1050 NS_DECL_THREADSAFE_ISUPPORTS
1051 NS_DECL_NSIINPUTSTREAM
1052 NS_DECL_NSIASYNCINPUTSTREAM
1053
1054 friend class SpdyConnectTransaction;
1055 friend class InputCloseTransaction;
1056
InputStreamShim(SpdyConnectTransaction * aTrans,bool aIsWebsocket)1057 InputStreamShim(SpdyConnectTransaction* aTrans, bool aIsWebsocket)
1058 : mCallback(nullptr),
1059 mStatus(NS_OK),
1060 mMutex("InputStreamShim"),
1061 mIsWebsocket(aIsWebsocket) {
1062 mWeakTrans = new WeakTransProxy(aTrans);
1063 }
1064
1065 already_AddRefed<nsIInputStreamCallback> TakeCallback();
1066 bool HasCallback();
1067
1068 private:
~InputStreamShim()1069 virtual ~InputStreamShim() {
1070 if (!OnSocketThread()) {
1071 RefPtr<WeakTransFreeProxy> p = new WeakTransFreeProxy(mWeakTrans);
1072 mWeakTrans = nullptr;
1073 p->Dispatch();
1074 }
1075 }
1076
1077 RefPtr<WeakTransProxy> mWeakTrans; // SpdyConnectTransaction *
1078 nsCOMPtr<nsIInputStreamCallback> mCallback;
1079 nsresult mStatus;
1080 mozilla::Mutex mMutex;
1081
1082 // Websockets
1083 bool mIsWebsocket;
1084 nsresult CloseTransaction(nsresult reason);
1085 };
1086
SpdyConnectTransaction(nsHttpConnectionInfo * ci,nsIInterfaceRequestor * callbacks,uint32_t caps,nsHttpTransaction * trans,nsAHttpConnection * session,bool isWebsocket)1087 SpdyConnectTransaction::SpdyConnectTransaction(
1088 nsHttpConnectionInfo* ci, nsIInterfaceRequestor* callbacks, uint32_t caps,
1089 nsHttpTransaction* trans, nsAHttpConnection* session, bool isWebsocket)
1090 : NullHttpTransaction(ci, callbacks, caps | NS_HTTP_ALLOW_KEEPALIVE),
1091 mConnectStringOffset(0),
1092 mSession(session),
1093 mSegmentReader(nullptr),
1094 mInputDataSize(0),
1095 mInputDataUsed(0),
1096 mInputDataOffset(0),
1097 mOutputDataSize(0),
1098 mOutputDataUsed(0),
1099 mOutputDataOffset(0),
1100 mForcePlainText(false),
1101 mIsWebsocket(isWebsocket),
1102 mConnRefTaken(false),
1103 mCreateShimErrorCalled(false) {
1104 LOG(("SpdyConnectTransaction ctor %p\n", this));
1105
1106 mTimestampSyn = TimeStamp::Now();
1107 mRequestHead = new nsHttpRequestHead();
1108 if (mIsWebsocket) {
1109 // Ensure our request head has all the websocket headers duplicated from the
1110 // original transaction before calling the boilerplate stuff to create the
1111 // rest of the CONNECT headers.
1112 trans->RequestHead()->Enter();
1113 mRequestHead->SetHeaders(trans->RequestHead()->Headers());
1114 trans->RequestHead()->Exit();
1115 }
1116 DebugOnly<nsresult> rv = nsHttpConnection::MakeConnectString(
1117 trans, mRequestHead, mConnectString, mIsWebsocket);
1118 MOZ_ASSERT(NS_SUCCEEDED(rv));
1119 mDrivingTransaction = trans;
1120 }
1121
~SpdyConnectTransaction()1122 SpdyConnectTransaction::~SpdyConnectTransaction() {
1123 LOG(("SpdyConnectTransaction dtor %p\n", this));
1124
1125 MOZ_ASSERT(OnSocketThread());
1126
1127 if (mDrivingTransaction) {
1128 // requeue it I guess. This should be gone.
1129 mDrivingTransaction->SetH2WSTransaction(nullptr);
1130 Unused << gHttpHandler->InitiateTransaction(
1131 mDrivingTransaction, mDrivingTransaction->Priority());
1132 }
1133 }
1134
ForcePlainText()1135 void SpdyConnectTransaction::ForcePlainText() {
1136 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1137 MOZ_ASSERT(!mInputDataUsed && !mInputDataSize && !mInputDataOffset);
1138 MOZ_ASSERT(!mForcePlainText);
1139 MOZ_ASSERT(!mTunnelTransport, "call before mapstreamtohttpconnection");
1140 MOZ_ASSERT(!mIsWebsocket);
1141
1142 mForcePlainText = true;
1143 }
1144
MapStreamToHttpConnection(nsISocketTransport * aTransport,nsHttpConnectionInfo * aConnInfo,const nsACString & aFlat407Headers,int32_t aHttpResponseCode)1145 bool SpdyConnectTransaction::MapStreamToHttpConnection(
1146 nsISocketTransport* aTransport, nsHttpConnectionInfo* aConnInfo,
1147 const nsACString& aFlat407Headers, int32_t aHttpResponseCode) {
1148 MOZ_ASSERT(OnSocketThread());
1149
1150 if (aHttpResponseCode >= 100 && aHttpResponseCode < 200) {
1151 LOG(
1152 ("SpdyConnectTransaction::MapStreamToHttpConnection %p skip "
1153 "pre-response with response code %d",
1154 this, aHttpResponseCode));
1155 return false;
1156 }
1157
1158 mTunnelTransport = new SocketTransportShim(this, aTransport, mIsWebsocket);
1159 mTunnelStreamIn = new InputStreamShim(this, mIsWebsocket);
1160 mTunnelStreamOut = new OutputStreamShim(this, mIsWebsocket);
1161 mTunneledConn = new nsHttpConnection();
1162
1163 // If aHttpResponseCode is -1, it means that proxy connect is not used. We
1164 // should not call HttpProxyResponseToErrorCode(), since this will create a
1165 // shim error.
1166 if (aHttpResponseCode > 0 && aHttpResponseCode != 200) {
1167 nsresult err = HttpProxyResponseToErrorCode(aHttpResponseCode);
1168 if (NS_FAILED(err)) {
1169 CreateShimError(err);
1170 }
1171 }
1172
1173 // this new http connection has a specific hashkey (i.e. to a particular
1174 // host via the tunnel) and is associated with the tunnel streams
1175 LOG(("SpdyConnectTransaction %p new httpconnection %p %s\n", this,
1176 mTunneledConn.get(), aConnInfo->HashKey().get()));
1177
1178 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1179 GetSecurityCallbacks(getter_AddRefs(callbacks));
1180 mTunneledConn->SetTransactionCaps(Caps());
1181 MOZ_ASSERT(aConnInfo->UsingHttpsProxy() || mIsWebsocket);
1182 TimeDuration rtt = TimeStamp::Now() - mTimestampSyn;
1183 DebugOnly<nsresult> rv = mTunneledConn->Init(
1184 aConnInfo, gHttpHandler->ConnMgr()->MaxRequestDelay(), mTunnelTransport,
1185 mTunnelStreamIn, mTunnelStreamOut, true, NS_OK, callbacks,
1186 PR_MillisecondsToInterval(static_cast<uint32_t>(rtt.ToMilliseconds())),
1187 false);
1188 MOZ_ASSERT(NS_SUCCEEDED(rv));
1189 if (mForcePlainText) {
1190 mTunneledConn->ForcePlainText();
1191 } else if (mIsWebsocket) {
1192 LOG(("SpdyConnectTransaction::MapStreamToHttpConnection %p websocket",
1193 this));
1194 // Let the root transaction know about us, so it can pass our own conn
1195 // to the websocket.
1196 mDrivingTransaction->SetH2WSTransaction(this);
1197 } else {
1198 mTunneledConn->SetupSecondaryTLS(this);
1199 mTunneledConn->SetInSpdyTunnel(true);
1200 }
1201
1202 // make the originating transaction stick to the tunneled conn
1203 RefPtr<nsAHttpConnection> wrappedConn =
1204 gHttpHandler->ConnMgr()->MakeConnectionHandle(mTunneledConn);
1205 mDrivingTransaction->SetConnection(wrappedConn);
1206 mDrivingTransaction->MakeSticky();
1207
1208 if (!mIsWebsocket) {
1209 mDrivingTransaction->OnProxyConnectComplete(aHttpResponseCode);
1210
1211 if (aHttpResponseCode == 407) {
1212 mDrivingTransaction->SetFlat407Headers(aFlat407Headers);
1213 mDrivingTransaction->SetProxyConnectFailed();
1214 }
1215
1216 // jump the priority and start the dispatcher
1217 Unused << gHttpHandler->InitiateTransaction(
1218 mDrivingTransaction, nsISupportsPriority::PRIORITY_HIGHEST - 60);
1219 mDrivingTransaction = nullptr;
1220 }
1221
1222 return true;
1223 }
1224
Flush(uint32_t count,uint32_t * countRead)1225 nsresult SpdyConnectTransaction::Flush(uint32_t count, uint32_t* countRead) {
1226 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1227 LOG(("SpdyConnectTransaction::Flush %p count %d avail %d\n", this, count,
1228 mOutputDataUsed - mOutputDataOffset));
1229
1230 if (!mSegmentReader) {
1231 return NS_ERROR_UNEXPECTED;
1232 }
1233
1234 *countRead = 0;
1235 count = std::min(count, (mOutputDataUsed - mOutputDataOffset));
1236 if (count) {
1237 nsresult rv;
1238 rv = mSegmentReader->OnReadSegment(&mOutputData[mOutputDataOffset], count,
1239 countRead);
1240 if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
1241 LOG(("SpdyConnectTransaction::Flush %p Error %" PRIx32 "\n", this,
1242 static_cast<uint32_t>(rv)));
1243 CreateShimError(rv);
1244 return rv;
1245 }
1246 }
1247
1248 mOutputDataOffset += *countRead;
1249 if (mOutputDataOffset == mOutputDataUsed) {
1250 mOutputDataOffset = mOutputDataUsed = 0;
1251 }
1252 if (!(*countRead)) {
1253 return NS_BASE_STREAM_WOULD_BLOCK;
1254 }
1255
1256 if (mOutputDataUsed != mOutputDataOffset) {
1257 LOG(("SpdyConnectTransaction::Flush %p Incomplete %d\n", this,
1258 mOutputDataUsed - mOutputDataOffset));
1259 mSession->TransactionHasDataToWrite(this);
1260 }
1261
1262 return NS_OK;
1263 }
1264
ReadSegments(nsAHttpSegmentReader * reader,uint32_t count,uint32_t * countRead)1265 nsresult SpdyConnectTransaction::ReadSegments(nsAHttpSegmentReader* reader,
1266 uint32_t count,
1267 uint32_t* countRead) {
1268 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1269 LOG(("SpdyConnectTransaction::ReadSegments %p count %d conn %p\n", this,
1270 count, mTunneledConn.get()));
1271
1272 mSegmentReader = reader;
1273
1274 // spdy stream carrying tunnel is not setup yet.
1275 if (!mTunneledConn) {
1276 uint32_t toWrite = mConnectString.Length() - mConnectStringOffset;
1277 toWrite = std::min(toWrite, count);
1278 *countRead = toWrite;
1279 if (toWrite) {
1280 nsresult rv = mSegmentReader->OnReadSegment(
1281 mConnectString.BeginReading() + mConnectStringOffset, toWrite,
1282 countRead);
1283 if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
1284 LOG(
1285 ("SpdyConnectTransaction::ReadSegments %p OnReadSegmentError "
1286 "%" PRIx32 "\n",
1287 this, static_cast<uint32_t>(rv)));
1288 CreateShimError(rv);
1289 } else {
1290 mConnectStringOffset += toWrite;
1291 if (mConnectString.Length() == mConnectStringOffset) {
1292 mConnectString.Truncate();
1293 mConnectStringOffset = 0;
1294 }
1295 }
1296 return rv;
1297 }
1298
1299 LOG(("SpdyConnectTransaciton::ReadSegments %p connect request consumed",
1300 this));
1301 return NS_BASE_STREAM_WOULD_BLOCK;
1302 }
1303
1304 if (mForcePlainText) {
1305 // this path just ignores sending the request so that we can
1306 // send a synthetic reply in writesegments()
1307 LOG(
1308 ("SpdyConnectTransaciton::ReadSegments %p dropping %d output bytes "
1309 "due to synthetic reply\n",
1310 this, mOutputDataUsed - mOutputDataOffset));
1311 *countRead = mOutputDataUsed - mOutputDataOffset;
1312 mOutputDataOffset = mOutputDataUsed = 0;
1313 mTunneledConn->DontReuse();
1314 return NS_OK;
1315 }
1316
1317 *countRead = 0;
1318 nsresult rv = Flush(count, countRead);
1319 if (!(*countRead)) {
1320 return NS_BASE_STREAM_WOULD_BLOCK;
1321 }
1322
1323 nsCOMPtr<nsIOutputStreamCallback> cb = mTunnelStreamOut->TakeCallback();
1324 if (!cb) {
1325 return NS_BASE_STREAM_WOULD_BLOCK;
1326 }
1327
1328 // See if there is any more data available
1329 rv = cb->OnOutputStreamReady(mTunnelStreamOut);
1330 if (NS_FAILED(rv)) {
1331 return rv;
1332 }
1333
1334 // Write out anything that may have come out of the stream just above
1335 uint32_t subtotal;
1336 count -= *countRead;
1337 rv = Flush(count, &subtotal);
1338 *countRead += subtotal;
1339
1340 return rv;
1341 }
1342
CreateShimError(nsresult code)1343 void SpdyConnectTransaction::CreateShimError(nsresult code) {
1344 LOG(("SpdyConnectTransaction::CreateShimError %p 0x%08" PRIx32, this,
1345 static_cast<uint32_t>(code)));
1346
1347 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1348 MOZ_ASSERT(NS_FAILED(code));
1349
1350 MOZ_ASSERT(!mCreateShimErrorCalled);
1351 if (mCreateShimErrorCalled) {
1352 return;
1353 }
1354 mCreateShimErrorCalled = true;
1355
1356 if (mTunnelStreamOut && NS_SUCCEEDED(mTunnelStreamOut->mStatus)) {
1357 mTunnelStreamOut->mStatus = code;
1358 }
1359
1360 if (mTunnelStreamIn && NS_SUCCEEDED(mTunnelStreamIn->mStatus)) {
1361 mTunnelStreamIn->mStatus = code;
1362 }
1363
1364 if (mTunnelStreamIn) {
1365 nsCOMPtr<nsIInputStreamCallback> cb = mTunnelStreamIn->TakeCallback();
1366 if (cb) {
1367 cb->OnInputStreamReady(mTunnelStreamIn);
1368 }
1369 }
1370
1371 if (mTunnelStreamOut) {
1372 nsCOMPtr<nsIOutputStreamCallback> cb = mTunnelStreamOut->TakeCallback();
1373 if (cb) {
1374 cb->OnOutputStreamReady(mTunnelStreamOut);
1375 }
1376 }
1377 mCreateShimErrorCalled = false;
1378 }
1379
WriteDataToBuffer(nsAHttpSegmentWriter * writer,uint32_t count,uint32_t * countWritten)1380 nsresult SpdyConnectTransaction::WriteDataToBuffer(nsAHttpSegmentWriter* writer,
1381 uint32_t count,
1382 uint32_t* countWritten) {
1383 EnsureBuffer(mInputData, mInputDataUsed + count, mInputDataUsed,
1384 mInputDataSize);
1385 nsresult rv =
1386 writer->OnWriteSegment(&mInputData[mInputDataUsed], count, countWritten);
1387 if (NS_FAILED(rv)) {
1388 if (rv != NS_BASE_STREAM_WOULD_BLOCK) {
1389 LOG(
1390 ("SpdyConnectTransaction::WriteSegments wrapped writer %p Error "
1391 "%" PRIx32 "\n",
1392 this, static_cast<uint32_t>(rv)));
1393 CreateShimError(rv);
1394 }
1395 return rv;
1396 }
1397 mInputDataUsed += *countWritten;
1398 LOG(
1399 ("SpdyConnectTransaction %p %d new bytes [%d total] of ciphered data "
1400 "buffered\n",
1401 this, *countWritten, mInputDataUsed - mInputDataOffset));
1402
1403 return rv;
1404 }
1405
WriteSegments(nsAHttpSegmentWriter * writer,uint32_t count,uint32_t * countWritten)1406 nsresult SpdyConnectTransaction::WriteSegments(nsAHttpSegmentWriter* writer,
1407 uint32_t count,
1408 uint32_t* countWritten) {
1409 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1410 LOG(("SpdyConnectTransaction::WriteSegments %p max=%d", this, count));
1411
1412 // For websockets, we need to forward the initial response through to the base
1413 // transaction so the normal websocket plumbing can do all the things it needs
1414 // to do.
1415 if (mIsWebsocket) {
1416 return WebsocketWriteSegments(writer, count, countWritten);
1417 }
1418
1419 // first call into the tunnel stream to get the demux'd data out of the
1420 // spdy session.
1421 nsresult rv = WriteDataToBuffer(writer, count, countWritten);
1422 if (NS_FAILED(rv)) {
1423 return rv;
1424 }
1425
1426 nsCOMPtr<nsIInputStreamCallback> cb =
1427 mTunneledConn ? mTunnelStreamIn->TakeCallback() : nullptr;
1428 LOG(("SpdyConnectTransaction::WriteSegments %p cb=%p", this, cb.get()));
1429
1430 if (!cb) {
1431 return NS_BASE_STREAM_WOULD_BLOCK;
1432 }
1433
1434 rv = cb->OnInputStreamReady(mTunnelStreamIn);
1435 LOG(
1436 ("SpdyConnectTransaction::WriteSegments %p "
1437 "after InputStreamReady callback %d total of ciphered data buffered "
1438 "rv=%" PRIx32 "\n",
1439 this, mInputDataUsed - mInputDataOffset, static_cast<uint32_t>(rv)));
1440 LOG(
1441 ("SpdyConnectTransaction::WriteSegments %p "
1442 "goodput %p out %" PRId64 "\n",
1443 this, mTunneledConn.get(), mTunneledConn->ContentBytesWritten()));
1444 if (NS_SUCCEEDED(rv) && !mTunneledConn->ContentBytesWritten()) {
1445 nsCOMPtr<nsIOutputStreamCallback> ocb = mTunnelStreamOut->TakeCallback();
1446 mTunnelStreamOut->AsyncWait(ocb, 0, 0, nullptr);
1447 }
1448 return rv;
1449 }
1450
WebsocketWriteSegments(nsAHttpSegmentWriter * writer,uint32_t count,uint32_t * countWritten)1451 nsresult SpdyConnectTransaction::WebsocketWriteSegments(
1452 nsAHttpSegmentWriter* writer, uint32_t count, uint32_t* countWritten) {
1453 MOZ_ASSERT(OnSocketThread());
1454 MOZ_ASSERT(mIsWebsocket);
1455 LOG(("SpdyConnectTransaction::WebsocketWriteSegments %p max=%d", this,
1456 count));
1457
1458 if (mDrivingTransaction && !mDrivingTransaction->IsDone()) {
1459 // Transaction hasn't received end of headers yet, so keep passing data to
1460 // it until it has. Then we can take over.
1461 nsresult rv =
1462 mDrivingTransaction->WriteSegments(writer, count, countWritten);
1463 if (NS_SUCCEEDED(rv) && mDrivingTransaction->IsDone() && !mConnRefTaken) {
1464 mDrivingTransaction->Close(NS_OK);
1465 }
1466 }
1467
1468 if (!mConnRefTaken) {
1469 // Force driving transaction to finish so the websocket channel can get its
1470 // notifications correctly and start driving.
1471 MOZ_ASSERT(mDrivingTransaction);
1472 mDrivingTransaction->Close(NS_OK);
1473 }
1474
1475 nsresult rv = WriteDataToBuffer(writer, count, countWritten);
1476 if (NS_SUCCEEDED(rv)) {
1477 if (!mTunneledConn) {
1478 return NS_BASE_STREAM_WOULD_BLOCK;
1479 }
1480 nsCOMPtr<nsIInputStreamCallback> cb = mTunnelStreamIn->TakeCallback();
1481 if (!cb) {
1482 return NS_BASE_STREAM_WOULD_BLOCK;
1483 }
1484 rv = cb->OnInputStreamReady(mTunnelStreamIn);
1485 }
1486
1487 return rv;
1488 }
1489
ConnectedReadyForInput()1490 bool SpdyConnectTransaction::ConnectedReadyForInput() {
1491 return mTunneledConn && mTunnelStreamIn->HasCallback();
1492 }
1493
RequestHead()1494 nsHttpRequestHead* SpdyConnectTransaction::RequestHead() {
1495 return mRequestHead;
1496 }
1497
Close(nsresult code)1498 void SpdyConnectTransaction::Close(nsresult code) {
1499 LOG(("SpdyConnectTransaction close %p %" PRIx32 "\n", this,
1500 static_cast<uint32_t>(code)));
1501
1502 MOZ_ASSERT(OnSocketThread());
1503
1504 if (mIsWebsocket && mDrivingTransaction) {
1505 mDrivingTransaction->SetH2WSTransaction(nullptr);
1506 if (!mConnRefTaken) {
1507 // This indicates that the websocket failed to set up, so just close down
1508 // the transaction as usual.
1509 mDrivingTransaction->Close(code);
1510 mDrivingTransaction = nullptr;
1511 }
1512 }
1513 NullHttpTransaction::Close(code);
1514 if (NS_FAILED(code) && (code != NS_BASE_STREAM_WOULD_BLOCK)) {
1515 CreateShimError(code);
1516 } else {
1517 CreateShimError(NS_BASE_STREAM_CLOSED);
1518 }
1519 }
1520
SetConnRefTaken()1521 void SpdyConnectTransaction::SetConnRefTaken() {
1522 MOZ_ASSERT(OnSocketThread());
1523
1524 mConnRefTaken = true;
1525 mDrivingTransaction = nullptr; // Just in case
1526 }
1527
TakeCallback()1528 already_AddRefed<nsIOutputStreamCallback> OutputStreamShim::TakeCallback() {
1529 mozilla::MutexAutoLock lock(mMutex);
1530 return mCallback.forget();
1531 }
1532
1533 class WebsocketHasDataToWrite final : public Runnable {
1534 public:
WebsocketHasDataToWrite(OutputStreamShim * shim)1535 explicit WebsocketHasDataToWrite(OutputStreamShim* shim)
1536 : Runnable("WebsocketHasDataToWrite"), mShim(shim) {}
1537
1538 ~WebsocketHasDataToWrite() = default;
1539
Run()1540 NS_IMETHOD Run() override { return mShim->CallTransactionHasDataToWrite(); }
1541
Dispatch()1542 [[nodiscard]] nsresult Dispatch() {
1543 if (OnSocketThread()) {
1544 return Run();
1545 }
1546
1547 nsCOMPtr<nsIEventTarget> sts =
1548 do_GetService("@mozilla.org/network/socket-transport-service;1");
1549 return sts->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
1550 }
1551
1552 private:
1553 RefPtr<OutputStreamShim> mShim;
1554 };
1555
1556 NS_IMETHODIMP
AsyncWait(nsIOutputStreamCallback * callback,unsigned int flags,unsigned int requestedCount,nsIEventTarget * target)1557 OutputStreamShim::AsyncWait(nsIOutputStreamCallback* callback,
1558 unsigned int flags, unsigned int requestedCount,
1559 nsIEventTarget* target) {
1560 if (mIsWebsocket) {
1561 // With websockets, AsyncWait may be called from the main thread, but the
1562 // target is on the socket thread. That's all we really care about.
1563 nsCOMPtr<nsIEventTarget> sts =
1564 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
1565 MOZ_ASSERT((!target && !callback) || (target == sts));
1566 if (target && (target != sts)) {
1567 return NS_ERROR_FAILURE;
1568 }
1569 } else {
1570 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1571 bool currentThread;
1572
1573 if (target && (NS_FAILED(target->IsOnCurrentThread(¤tThread)) ||
1574 !currentThread)) {
1575 return NS_ERROR_FAILURE;
1576 }
1577 }
1578
1579 LOG(("OutputStreamShim::AsyncWait %p callback %p\n", this, callback));
1580
1581 {
1582 mozilla::MutexAutoLock lock(mMutex);
1583 mCallback = callback;
1584 }
1585
1586 RefPtr<WebsocketHasDataToWrite> wsdw = new WebsocketHasDataToWrite(this);
1587 Unused << wsdw->Dispatch();
1588
1589 return NS_OK;
1590 }
1591
1592 class OutputCloseTransaction final : public Runnable {
1593 public:
OutputCloseTransaction(OutputStreamShim * shim,nsresult reason)1594 OutputCloseTransaction(OutputStreamShim* shim, nsresult reason)
1595 : Runnable("OutputCloseTransaction"), mShim(shim), mReason(reason) {}
1596
1597 ~OutputCloseTransaction() = default;
1598
Run()1599 NS_IMETHOD Run() override { return mShim->CloseTransaction(mReason); }
1600
1601 private:
1602 RefPtr<OutputStreamShim> mShim;
1603 nsresult mReason;
1604 };
1605
1606 NS_IMETHODIMP
CloseWithStatus(nsresult reason)1607 OutputStreamShim::CloseWithStatus(nsresult reason) {
1608 if (!OnSocketThread()) {
1609 RefPtr<OutputCloseTransaction> oct =
1610 new OutputCloseTransaction(this, reason);
1611 nsCOMPtr<nsIEventTarget> sts =
1612 do_GetService("@mozilla.org/network/socket-transport-service;1");
1613 return sts->Dispatch(oct, nsIEventTarget::DISPATCH_NORMAL);
1614 }
1615
1616 return CloseTransaction(reason);
1617 }
1618
CloseTransaction(nsresult reason)1619 nsresult OutputStreamShim::CloseTransaction(nsresult reason) {
1620 MOZ_ASSERT(OnSocketThread());
1621 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1622 if (!baseTrans) {
1623 return NS_ERROR_FAILURE;
1624 }
1625 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1626 MOZ_ASSERT(trans);
1627 if (!trans) {
1628 return NS_ERROR_UNEXPECTED;
1629 }
1630
1631 trans->mSession->CloseTransaction(trans, reason);
1632 return NS_OK;
1633 }
1634
1635 NS_IMETHODIMP
Close()1636 OutputStreamShim::Close() { return CloseWithStatus(NS_OK); }
1637
1638 NS_IMETHODIMP
Flush()1639 OutputStreamShim::Flush() {
1640 MOZ_ASSERT(OnSocketThread());
1641 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1642 if (!baseTrans) {
1643 return NS_ERROR_FAILURE;
1644 }
1645 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1646 MOZ_ASSERT(trans);
1647 if (!trans) {
1648 return NS_ERROR_UNEXPECTED;
1649 }
1650
1651 uint32_t count = trans->mOutputDataUsed - trans->mOutputDataOffset;
1652 if (!count) {
1653 return NS_OK;
1654 }
1655
1656 uint32_t countRead;
1657 nsresult rv = trans->Flush(count, &countRead);
1658 LOG(("OutputStreamShim::Flush %p before %d after %d\n", this, count,
1659 trans->mOutputDataUsed - trans->mOutputDataOffset));
1660 return rv;
1661 }
1662
CallTransactionHasDataToWrite()1663 nsresult OutputStreamShim::CallTransactionHasDataToWrite() {
1664 MOZ_ASSERT(OnSocketThread());
1665 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1666 if (!baseTrans) {
1667 return NS_ERROR_FAILURE;
1668 }
1669 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1670 MOZ_ASSERT(trans);
1671 if (!trans) {
1672 return NS_ERROR_UNEXPECTED;
1673 }
1674 trans->mSession->TransactionHasDataToWrite(trans);
1675 return NS_OK;
1676 }
1677
1678 NS_IMETHODIMP
Write(const char * aBuf,uint32_t aCount,uint32_t * _retval)1679 OutputStreamShim::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) {
1680 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1681
1682 if (NS_FAILED(mStatus)) {
1683 return mStatus;
1684 }
1685
1686 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1687 if (!baseTrans) {
1688 return NS_ERROR_FAILURE;
1689 }
1690 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1691 MOZ_ASSERT(trans);
1692 if (!trans) {
1693 return NS_ERROR_UNEXPECTED;
1694 }
1695
1696 if ((trans->mOutputDataUsed + aCount) >= 512000) {
1697 *_retval = 0;
1698 // time for some flow control;
1699 return NS_BASE_STREAM_WOULD_BLOCK;
1700 }
1701
1702 EnsureBuffer(trans->mOutputData, trans->mOutputDataUsed + aCount,
1703 trans->mOutputDataUsed, trans->mOutputDataSize);
1704 memcpy(&trans->mOutputData[trans->mOutputDataUsed], aBuf, aCount);
1705 trans->mOutputDataUsed += aCount;
1706 *_retval = aCount;
1707 LOG(("OutputStreamShim::Write %p new %d total %d\n", this, aCount,
1708 trans->mOutputDataUsed));
1709
1710 trans->mSession->TransactionHasDataToWrite(trans);
1711
1712 return NS_OK;
1713 }
1714
1715 NS_IMETHODIMP
WriteFrom(nsIInputStream * aFromStream,uint32_t aCount,uint32_t * _retval)1716 OutputStreamShim::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
1717 uint32_t* _retval) {
1718 if (mIsWebsocket) {
1719 LOG3(("WARNING: OutputStreamShim::WriteFrom %p", this));
1720 }
1721 return NS_ERROR_NOT_IMPLEMENTED;
1722 }
1723
1724 NS_IMETHODIMP
WriteSegments(nsReadSegmentFun aReader,void * aClosure,uint32_t aCount,uint32_t * _retval)1725 OutputStreamShim::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
1726 uint32_t aCount, uint32_t* _retval) {
1727 if (mIsWebsocket) {
1728 LOG3(("WARNING: OutputStreamShim::WriteSegments %p", this));
1729 }
1730 return NS_ERROR_NOT_IMPLEMENTED;
1731 }
1732
1733 NS_IMETHODIMP
IsNonBlocking(bool * _retval)1734 OutputStreamShim::IsNonBlocking(bool* _retval) {
1735 *_retval = true;
1736 return NS_OK;
1737 }
1738
TakeCallback()1739 already_AddRefed<nsIInputStreamCallback> InputStreamShim::TakeCallback() {
1740 mozilla::MutexAutoLock lock(mMutex);
1741 return mCallback.forget();
1742 }
1743
HasCallback()1744 bool InputStreamShim::HasCallback() {
1745 mozilla::MutexAutoLock lock(mMutex);
1746 return mCallback != nullptr;
1747 }
1748
1749 class CheckAvailData final : public Runnable {
1750 public:
CheckAvailData(InputStreamShim * shim)1751 explicit CheckAvailData(InputStreamShim* shim)
1752 : Runnable("CheckAvailData"), mShim(shim) {}
1753
1754 ~CheckAvailData() = default;
1755
Run()1756 NS_IMETHOD Run() override {
1757 uint64_t avail = 0;
1758 if (NS_SUCCEEDED(mShim->Available(&avail)) && avail) {
1759 nsCOMPtr<nsIInputStreamCallback> cb = mShim->TakeCallback();
1760 if (cb) {
1761 cb->OnInputStreamReady(mShim);
1762 }
1763 }
1764 return NS_OK;
1765 }
1766
Dispatch()1767 [[nodiscard]] nsresult Dispatch() {
1768 // Dispatch the event even if we're on socket thread to avoid closing and
1769 // destructing Http2Session in case this call is comming from
1770 // Http2Session::ReadSegments() and the callback closes the transaction in
1771 // OnInputStreamRead().
1772 nsCOMPtr<nsIEventTarget> sts =
1773 do_GetService("@mozilla.org/network/socket-transport-service;1");
1774 return sts->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
1775 }
1776
1777 private:
1778 RefPtr<InputStreamShim> mShim;
1779 };
1780
1781 NS_IMETHODIMP
AsyncWait(nsIInputStreamCallback * callback,unsigned int flags,unsigned int requestedCount,nsIEventTarget * target)1782 InputStreamShim::AsyncWait(nsIInputStreamCallback* callback, unsigned int flags,
1783 unsigned int requestedCount,
1784 nsIEventTarget* target) {
1785 if (mIsWebsocket) {
1786 // With websockets, AsyncWait may be called from the main thread, but the
1787 // target is on the socket thread. That's all we really care about.
1788 nsCOMPtr<nsIEventTarget> sts =
1789 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
1790 MOZ_ASSERT((!target && !callback) || (target == sts));
1791 if (target && (target != sts)) {
1792 return NS_ERROR_FAILURE;
1793 }
1794 } else {
1795 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1796 bool currentThread;
1797
1798 if (target && (NS_FAILED(target->IsOnCurrentThread(¤tThread)) ||
1799 !currentThread)) {
1800 return NS_ERROR_FAILURE;
1801 }
1802 }
1803
1804 LOG(("InputStreamShim::AsyncWait %p callback %p\n", this, callback));
1805
1806 {
1807 mozilla::MutexAutoLock lock(mMutex);
1808 mCallback = callback;
1809 }
1810
1811 if (callback) {
1812 RefPtr<CheckAvailData> cad = new CheckAvailData(this);
1813 Unused << cad->Dispatch();
1814 }
1815
1816 return NS_OK;
1817 }
1818
1819 class InputCloseTransaction final : public Runnable {
1820 public:
InputCloseTransaction(InputStreamShim * shim,nsresult reason)1821 InputCloseTransaction(InputStreamShim* shim, nsresult reason)
1822 : Runnable("InputCloseTransaction"), mShim(shim), mReason(reason) {}
1823
1824 ~InputCloseTransaction() = default;
1825
Run()1826 NS_IMETHOD Run() override { return mShim->CloseTransaction(mReason); }
1827
1828 private:
1829 RefPtr<InputStreamShim> mShim;
1830 nsresult mReason;
1831 };
1832
1833 NS_IMETHODIMP
CloseWithStatus(nsresult reason)1834 InputStreamShim::CloseWithStatus(nsresult reason) {
1835 if (!OnSocketThread()) {
1836 RefPtr<InputCloseTransaction> ict = new InputCloseTransaction(this, reason);
1837 nsCOMPtr<nsIEventTarget> sts =
1838 do_GetService("@mozilla.org/network/socket-transport-service;1");
1839 return sts->Dispatch(ict, nsIEventTarget::DISPATCH_NORMAL);
1840 }
1841
1842 return CloseTransaction(reason);
1843 }
1844
CloseTransaction(nsresult reason)1845 nsresult InputStreamShim::CloseTransaction(nsresult reason) {
1846 MOZ_ASSERT(OnSocketThread());
1847 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1848 if (!baseTrans) {
1849 return NS_ERROR_FAILURE;
1850 }
1851 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1852 MOZ_ASSERT(trans);
1853 if (!trans) {
1854 return NS_ERROR_UNEXPECTED;
1855 }
1856
1857 trans->mSession->CloseTransaction(trans, reason);
1858 return NS_OK;
1859 }
1860
1861 NS_IMETHODIMP
Close()1862 InputStreamShim::Close() { return CloseWithStatus(NS_OK); }
1863
1864 NS_IMETHODIMP
Available(uint64_t * _retval)1865 InputStreamShim::Available(uint64_t* _retval) {
1866 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1867 if (!baseTrans) {
1868 return NS_ERROR_FAILURE;
1869 }
1870 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1871 MOZ_ASSERT(trans);
1872 if (!trans) {
1873 return NS_ERROR_UNEXPECTED;
1874 }
1875
1876 *_retval = trans->mInputDataUsed - trans->mInputDataOffset;
1877 return NS_OK;
1878 }
1879
1880 NS_IMETHODIMP
Read(char * aBuf,uint32_t aCount,uint32_t * _retval)1881 InputStreamShim::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) {
1882 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1883
1884 if (NS_FAILED(mStatus)) {
1885 return mStatus;
1886 }
1887
1888 RefPtr<NullHttpTransaction> baseTrans = mWeakTrans->QueryTransaction();
1889 if (!baseTrans) {
1890 return NS_ERROR_FAILURE;
1891 }
1892 SpdyConnectTransaction* trans = baseTrans->QuerySpdyConnectTransaction();
1893 MOZ_ASSERT(trans);
1894 if (!trans) {
1895 return NS_ERROR_UNEXPECTED;
1896 }
1897
1898 uint32_t avail = trans->mInputDataUsed - trans->mInputDataOffset;
1899 uint32_t tocopy = std::min(aCount, avail);
1900 *_retval = tocopy;
1901 memcpy(aBuf, &trans->mInputData[trans->mInputDataOffset], tocopy);
1902 trans->mInputDataOffset += tocopy;
1903 if (trans->mInputDataOffset == trans->mInputDataUsed) {
1904 trans->mInputDataOffset = trans->mInputDataUsed = 0;
1905 }
1906
1907 return tocopy ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
1908 }
1909
1910 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun aWriter,void * aClosure,uint32_t aCount,uint32_t * _retval)1911 InputStreamShim::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
1912 uint32_t aCount, uint32_t* _retval) {
1913 if (mIsWebsocket) {
1914 LOG3(("WARNING: InputStreamShim::ReadSegments %p", this));
1915 }
1916 return NS_ERROR_NOT_IMPLEMENTED;
1917 }
1918
1919 NS_IMETHODIMP
IsNonBlocking(bool * _retval)1920 InputStreamShim::IsNonBlocking(bool* _retval) {
1921 *_retval = true;
1922 return NS_OK;
1923 }
1924
1925 NS_IMETHODIMP
SetKeepaliveEnabled(bool aKeepaliveEnabled)1926 SocketTransportShim::SetKeepaliveEnabled(bool aKeepaliveEnabled) {
1927 if (mIsWebsocket) {
1928 LOG3(("WARNING: SocketTransportShim::SetKeepaliveEnabled %p called", this));
1929 }
1930 return NS_ERROR_NOT_IMPLEMENTED;
1931 }
1932
1933 NS_IMETHODIMP
SetKeepaliveVals(int32_t keepaliveIdleTime,int32_t keepaliveRetryInterval)1934 SocketTransportShim::SetKeepaliveVals(int32_t keepaliveIdleTime,
1935 int32_t keepaliveRetryInterval) {
1936 if (mIsWebsocket) {
1937 LOG3(("WARNING: SocketTransportShim::SetKeepaliveVals %p called", this));
1938 }
1939 return NS_ERROR_NOT_IMPLEMENTED;
1940 }
1941
1942 NS_IMETHODIMP
GetSecurityCallbacks(nsIInterfaceRequestor ** aSecurityCallbacks)1943 SocketTransportShim::GetSecurityCallbacks(
1944 nsIInterfaceRequestor** aSecurityCallbacks) {
1945 if (mIsWebsocket) {
1946 nsCOMPtr<nsIInterfaceRequestor> out(mSecurityCallbacks);
1947 *aSecurityCallbacks = out.forget().take();
1948 return NS_OK;
1949 }
1950
1951 return mWrapped->GetSecurityCallbacks(aSecurityCallbacks);
1952 }
1953
1954 NS_IMETHODIMP
SetSecurityCallbacks(nsIInterfaceRequestor * aSecurityCallbacks)1955 SocketTransportShim::SetSecurityCallbacks(
1956 nsIInterfaceRequestor* aSecurityCallbacks) {
1957 if (mIsWebsocket) {
1958 mSecurityCallbacks = aSecurityCallbacks;
1959 return NS_OK;
1960 }
1961 return NS_ERROR_NOT_IMPLEMENTED;
1962 }
1963
1964 NS_IMETHODIMP
OpenInputStream(uint32_t aFlags,uint32_t aSegmentSize,uint32_t aSegmentCount,nsIInputStream ** _retval)1965 SocketTransportShim::OpenInputStream(uint32_t aFlags, uint32_t aSegmentSize,
1966 uint32_t aSegmentCount,
1967 nsIInputStream** _retval) {
1968 if (mIsWebsocket) {
1969 LOG3(("WARNING: SocketTransportShim::OpenInputStream %p", this));
1970 }
1971 return NS_ERROR_NOT_IMPLEMENTED;
1972 }
1973
1974 NS_IMETHODIMP
OpenOutputStream(uint32_t aFlags,uint32_t aSegmentSize,uint32_t aSegmentCount,nsIOutputStream ** _retval)1975 SocketTransportShim::OpenOutputStream(uint32_t aFlags, uint32_t aSegmentSize,
1976 uint32_t aSegmentCount,
1977 nsIOutputStream** _retval) {
1978 if (mIsWebsocket) {
1979 LOG3(("WARNING: SocketTransportShim::OpenOutputStream %p", this));
1980 }
1981 return NS_ERROR_NOT_IMPLEMENTED;
1982 }
1983
1984 NS_IMETHODIMP
Close(nsresult aReason)1985 SocketTransportShim::Close(nsresult aReason) {
1986 if (mIsWebsocket) {
1987 LOG3(("WARNING: SocketTransportShim::Close %p", this));
1988 } else {
1989 LOG(("SocketTransportShim::Close %p", this));
1990 }
1991
1992 if (gHttpHandler->Bug1563538()) {
1993 // Must always post, because mSession->CloseTransaction releases the
1994 // Http2Stream which is still on stack.
1995 RefPtr<SocketTransportShim> self(this);
1996
1997 nsCOMPtr<nsIEventTarget> sts =
1998 do_GetService("@mozilla.org/network/socket-transport-service;1");
1999 Unused << sts->Dispatch(NS_NewRunnableFunction(
2000 "SocketTransportShim::Close", [self = std::move(self), aReason]() {
2001 RefPtr<NullHttpTransaction> baseTrans =
2002 self->mWeakTrans->QueryTransaction();
2003 if (!baseTrans) {
2004 return;
2005 }
2006 SpdyConnectTransaction* trans =
2007 baseTrans->QuerySpdyConnectTransaction();
2008 MOZ_ASSERT(trans);
2009 if (!trans) {
2010 return;
2011 }
2012
2013 trans->mSession->CloseTransaction(trans, aReason);
2014 }));
2015 return NS_OK;
2016 }
2017
2018 return NS_ERROR_NOT_IMPLEMENTED;
2019 }
2020
2021 NS_IMETHODIMP
SetEventSink(nsITransportEventSink * aSink,nsIEventTarget * aEventTarget)2022 SocketTransportShim::SetEventSink(nsITransportEventSink* aSink,
2023 nsIEventTarget* aEventTarget) {
2024 if (mIsWebsocket) {
2025 // Need to pretend, since websockets expect this to work
2026 return NS_OK;
2027 }
2028 return NS_ERROR_NOT_IMPLEMENTED;
2029 }
2030
2031 NS_IMETHODIMP
Bind(NetAddr * aLocalAddr)2032 SocketTransportShim::Bind(NetAddr* aLocalAddr) {
2033 if (mIsWebsocket) {
2034 LOG3(("WARNING: SocketTransportShim::Bind %p", this));
2035 }
2036 return NS_ERROR_NOT_IMPLEMENTED;
2037 }
2038
2039 NS_IMETHODIMP
GetEchConfigUsed(bool * aEchConfigUsed)2040 SocketTransportShim::GetEchConfigUsed(bool* aEchConfigUsed) {
2041 if (mIsWebsocket) {
2042 LOG3(("WARNING: SocketTransportShim::GetEchConfigUsed %p", this));
2043 }
2044 return NS_ERROR_NOT_IMPLEMENTED;
2045 }
2046
2047 NS_IMETHODIMP
SetEchConfig(const nsACString & aEchConfig)2048 SocketTransportShim::SetEchConfig(const nsACString& aEchConfig) {
2049 if (mIsWebsocket) {
2050 LOG3(("WARNING: SocketTransportShim::SetEchConfig %p", this));
2051 }
2052 return NS_ERROR_NOT_IMPLEMENTED;
2053 }
2054
2055 NS_IMETHODIMP
ResolvedByTRR(bool * aResolvedByTRR)2056 SocketTransportShim::ResolvedByTRR(bool* aResolvedByTRR) {
2057 if (mIsWebsocket) {
2058 LOG3(("WARNING: SocketTransportShim::IsTRR %p", this));
2059 }
2060 return NS_ERROR_NOT_IMPLEMENTED;
2061 }
2062
2063 #define FWD_TS_PTR(fx, ts) \
2064 NS_IMETHODIMP \
2065 SocketTransportShim::fx(ts* arg) { return mWrapped->fx(arg); }
2066
2067 #define FWD_TS_ADDREF(fx, ts) \
2068 NS_IMETHODIMP \
2069 SocketTransportShim::fx(ts** arg) { return mWrapped->fx(arg); }
2070
2071 #define FWD_TS(fx, ts) \
2072 NS_IMETHODIMP \
2073 SocketTransportShim::fx(ts arg) { return mWrapped->fx(arg); }
2074
2075 FWD_TS_PTR(GetKeepaliveEnabled, bool);
2076 FWD_TS_PTR(GetSendBufferSize, uint32_t);
2077 FWD_TS(SetSendBufferSize, uint32_t);
2078 FWD_TS_PTR(GetPort, int32_t);
2079 FWD_TS_PTR(GetPeerAddr, mozilla::net::NetAddr);
2080 FWD_TS_PTR(GetSelfAddr, mozilla::net::NetAddr);
2081 FWD_TS_ADDREF(GetScriptablePeerAddr, nsINetAddr);
2082 FWD_TS_ADDREF(GetScriptableSelfAddr, nsINetAddr);
2083 FWD_TS_ADDREF(GetSecurityInfo, nsISupports);
2084 FWD_TS_PTR(IsAlive, bool);
2085 FWD_TS_PTR(GetConnectionFlags, uint32_t);
2086 FWD_TS(SetConnectionFlags, uint32_t);
2087 FWD_TS(SetIsPrivate, bool);
2088 FWD_TS_PTR(GetTlsFlags, uint32_t);
2089 FWD_TS(SetTlsFlags, uint32_t);
2090 FWD_TS_PTR(GetRecvBufferSize, uint32_t);
2091 FWD_TS(SetRecvBufferSize, uint32_t);
2092 FWD_TS_PTR(GetResetIPFamilyPreference, bool);
2093
GetOriginAttributes(mozilla::OriginAttributes * aOriginAttributes)2094 nsresult SocketTransportShim::GetOriginAttributes(
2095 mozilla::OriginAttributes* aOriginAttributes) {
2096 return mWrapped->GetOriginAttributes(aOriginAttributes);
2097 }
2098
SetOriginAttributes(const mozilla::OriginAttributes & aOriginAttributes)2099 nsresult SocketTransportShim::SetOriginAttributes(
2100 const mozilla::OriginAttributes& aOriginAttributes) {
2101 return mWrapped->SetOriginAttributes(aOriginAttributes);
2102 }
2103
2104 NS_IMETHODIMP
GetScriptableOriginAttributes(JSContext * aCx,JS::MutableHandle<JS::Value> aOriginAttributes)2105 SocketTransportShim::GetScriptableOriginAttributes(
2106 JSContext* aCx, JS::MutableHandle<JS::Value> aOriginAttributes) {
2107 return mWrapped->GetScriptableOriginAttributes(aCx, aOriginAttributes);
2108 }
2109
2110 NS_IMETHODIMP
SetScriptableOriginAttributes(JSContext * aCx,JS::Handle<JS::Value> aOriginAttributes)2111 SocketTransportShim::SetScriptableOriginAttributes(
2112 JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) {
2113 return mWrapped->SetScriptableOriginAttributes(aCx, aOriginAttributes);
2114 }
2115
2116 NS_IMETHODIMP
GetHost(nsACString & aHost)2117 SocketTransportShim::GetHost(nsACString& aHost) {
2118 return mWrapped->GetHost(aHost);
2119 }
2120
2121 NS_IMETHODIMP
GetTimeout(uint32_t aType,uint32_t * _retval)2122 SocketTransportShim::GetTimeout(uint32_t aType, uint32_t* _retval) {
2123 return mWrapped->GetTimeout(aType, _retval);
2124 }
2125
2126 NS_IMETHODIMP
SetTimeout(uint32_t aType,uint32_t aValue)2127 SocketTransportShim::SetTimeout(uint32_t aType, uint32_t aValue) {
2128 return mWrapped->SetTimeout(aType, aValue);
2129 }
2130
2131 NS_IMETHODIMP
SetReuseAddrPort(bool aReuseAddrPort)2132 SocketTransportShim::SetReuseAddrPort(bool aReuseAddrPort) {
2133 return mWrapped->SetReuseAddrPort(aReuseAddrPort);
2134 }
2135
2136 NS_IMETHODIMP
SetLinger(bool aPolarity,int16_t aTimeout)2137 SocketTransportShim::SetLinger(bool aPolarity, int16_t aTimeout) {
2138 return mWrapped->SetLinger(aPolarity, aTimeout);
2139 }
2140
2141 NS_IMETHODIMP
GetQoSBits(uint8_t * aQoSBits)2142 SocketTransportShim::GetQoSBits(uint8_t* aQoSBits) {
2143 return mWrapped->GetQoSBits(aQoSBits);
2144 }
2145
2146 NS_IMETHODIMP
SetQoSBits(uint8_t aQoSBits)2147 SocketTransportShim::SetQoSBits(uint8_t aQoSBits) {
2148 return mWrapped->SetQoSBits(aQoSBits);
2149 }
2150
2151 NS_IMETHODIMP
GetRetryDnsIfPossible(bool * aRetry)2152 SocketTransportShim::GetRetryDnsIfPossible(bool* aRetry) {
2153 return mWrapped->GetRetryDnsIfPossible(aRetry);
2154 }
2155
2156 NS_IMETHODIMP
GetStatus(nsresult * aStatus)2157 SocketTransportShim::GetStatus(nsresult* aStatus) {
2158 return mWrapped->GetStatus(aStatus);
2159 }
2160
2161 NS_IMPL_ISUPPORTS(TLSFilterTransaction, nsITimerCallback, nsINamed)
2162 NS_IMPL_ISUPPORTS(SocketTransportShim, nsISocketTransport, nsITransport)
2163 NS_IMPL_ISUPPORTS(InputStreamShim, nsIInputStream, nsIAsyncInputStream)
2164 NS_IMPL_ISUPPORTS(OutputStreamShim, nsIOutputStream, nsIAsyncOutputStream)
2165 NS_IMPL_ISUPPORTS(SocketInWrapper, nsIAsyncInputStream)
2166 NS_IMPL_ISUPPORTS(SocketOutWrapper, nsIAsyncOutputStream)
2167
2168 } // namespace net
2169 } // namespace mozilla
2170