Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : // Log on level :5, instead of default :4.
11 : #undef LOG
12 : #define LOG(args) LOG5(args)
13 : #undef LOG_ENABLED
14 : #define LOG_ENABLED() LOG5_ENABLED()
15 :
16 : #include <algorithm>
17 :
18 : #include "Http2Session.h"
19 : #include "Http2Stream.h"
20 : #include "Http2Push.h"
21 :
22 : #include "mozilla/EndianUtils.h"
23 : #include "mozilla/Telemetry.h"
24 : #include "mozilla/Preferences.h"
25 : #include "nsHttp.h"
26 : #include "nsHttpHandler.h"
27 : #include "nsHttpConnection.h"
28 : #include "nsIRequestContext.h"
29 : #include "nsISSLSocketControl.h"
30 : #include "nsISSLStatus.h"
31 : #include "nsISSLStatusProvider.h"
32 : #include "nsISupportsPriority.h"
33 : #include "nsStandardURL.h"
34 : #include "nsURLHelper.h"
35 : #include "prnetdb.h"
36 : #include "sslt.h"
37 : #include "mozilla/Sprintf.h"
38 : #include "nsSocketTransportService2.h"
39 : #include "nsNetUtil.h"
40 :
41 : namespace mozilla {
42 : namespace net {
43 :
44 : // Http2Session has multiple inheritance of things that implement nsISupports
45 0 : NS_IMPL_ADDREF(Http2Session)
46 0 : NS_IMPL_RELEASE(Http2Session)
47 0 : NS_INTERFACE_MAP_BEGIN(Http2Session)
48 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
49 0 : NS_INTERFACE_MAP_END
50 :
51 : // "magic" refers to the string that preceeds HTTP/2 on the wire
52 : // to help find any intermediaries speaking an older version of HTTP
53 : const uint8_t Http2Session::kMagicHello[] = {
54 : 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
55 : 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
56 : 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
57 : };
58 :
59 : #define RETURN_SESSION_ERROR(o,x) \
60 : do { \
61 : (o)->mGoAwayReason = (x); \
62 : return NS_ERROR_ILLEGAL_VALUE; \
63 : } while (0)
64 :
65 0 : Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t version, bool attemptingEarlyData)
66 : : mSocketTransport(aSocketTransport)
67 : , mSegmentReader(nullptr)
68 : , mSegmentWriter(nullptr)
69 : , mNextStreamID(3) // 1 is reserved for Updgrade handshakes
70 : , mLastPushedID(0)
71 : , mConcurrentHighWater(0)
72 : , mDownstreamState(BUFFERING_OPENING_SETTINGS)
73 : , mInputFrameBufferSize(kDefaultBufferSize)
74 : , mInputFrameBufferUsed(0)
75 : , mInputFrameDataSize(0)
76 : , mInputFrameDataRead(0)
77 : , mInputFrameFinal(false)
78 : , mInputFrameType(0)
79 : , mInputFrameFlags(0)
80 : , mInputFrameID(0)
81 : , mPaddingLength(0)
82 : , mInputFrameDataStream(nullptr)
83 : , mNeedsCleanup(nullptr)
84 : , mDownstreamRstReason(NO_HTTP_ERROR)
85 : , mExpectedHeaderID(0)
86 : , mExpectedPushPromiseID(0)
87 : , mContinuedPromiseStream(0)
88 : , mFlatHTTPResponseHeadersOut(0)
89 : , mShouldGoAway(false)
90 : , mClosed(false)
91 : , mCleanShutdown(false)
92 : , mReceivedSettings(false)
93 : , mTLSProfileConfirmed(false)
94 : , mGoAwayReason(NO_HTTP_ERROR)
95 : , mClientGoAwayReason(UNASSIGNED)
96 : , mPeerGoAwayReason(UNASSIGNED)
97 : , mGoAwayID(0)
98 : , mOutgoingGoAwayID(0)
99 : , mConcurrent(0)
100 : , mServerPushedResources(0)
101 : , mServerInitialStreamWindow(kDefaultRwin)
102 : , mLocalSessionWindow(kDefaultRwin)
103 : , mServerSessionWindow(kDefaultRwin)
104 : , mInitialRwin(ASpdySession::kInitialRwin)
105 : , mOutputQueueSize(kDefaultQueueSize)
106 : , mOutputQueueUsed(0)
107 : , mOutputQueueSent(0)
108 0 : , mLastReadEpoch(PR_IntervalNow())
109 : , mPingSentEpoch(0)
110 : , mPreviousUsed(false)
111 : , mWaitingForSettingsAck(false)
112 : , mGoAwayOnPush(false)
113 : , mUseH2Deps(false)
114 : , mAttemptingEarlyData(attemptingEarlyData)
115 0 : , mOriginFrameActivated(false)
116 : {
117 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
118 :
119 : static uint64_t sSerial;
120 0 : mSerial = ++sSerial;
121 :
122 0 : LOG3(("Http2Session::Http2Session %p serial=0x%" PRIX64 "\n", this, mSerial));
123 :
124 0 : mInputFrameBuffer = MakeUnique<char[]>(mInputFrameBufferSize);
125 0 : mOutputQueueBuffer = MakeUnique<char[]>(mOutputQueueSize);
126 0 : mDecompressBuffer.SetCapacity(kDefaultBufferSize);
127 :
128 0 : mPushAllowance = gHttpHandler->SpdyPushAllowance();
129 0 : mInitialRwin = std::max(gHttpHandler->SpdyPullAllowance(), mPushAllowance);
130 0 : mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
131 0 : mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
132 0 : SendHello();
133 :
134 0 : mLastDataReadEpoch = mLastReadEpoch;
135 :
136 0 : mPingThreshold = gHttpHandler->SpdyPingThreshold();
137 0 : mPreviousPingThreshold = mPingThreshold;
138 0 : }
139 :
140 : void
141 0 : Http2Session::Shutdown()
142 : {
143 0 : for (auto iter = mStreamTransactionHash.Iter(); !iter.Done(); iter.Next()) {
144 0 : nsAutoPtr<Http2Stream> &stream = iter.Data();
145 :
146 : // On a clean server hangup the server sets the GoAwayID to be the ID of
147 : // the last transaction it processed. If the ID of stream in the
148 : // local stream is greater than that it can safely be restarted because the
149 : // server guarantees it was not partially processed. Streams that have not
150 : // registered an ID haven't actually been sent yet so they can always be
151 : // restarted.
152 0 : if (mCleanShutdown &&
153 0 : (stream->StreamID() > mGoAwayID || !stream->HasRegisteredID())) {
154 0 : CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted
155 0 : } else if (stream->RecvdData()) {
156 0 : CloseStream(stream, NS_ERROR_NET_PARTIAL_TRANSFER);
157 0 : } else if (mGoAwayReason == INADEQUATE_SECURITY) {
158 0 : CloseStream(stream, NS_ERROR_NET_INADEQUATE_SECURITY);
159 : } else {
160 0 : CloseStream(stream, NS_ERROR_ABORT);
161 : }
162 : }
163 0 : }
164 :
165 0 : Http2Session::~Http2Session()
166 : {
167 0 : LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X",
168 : this, mDownstreamState));
169 :
170 0 : Shutdown();
171 :
172 0 : Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
173 0 : Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
174 0 : Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
175 0 : mServerPushedResources);
176 0 : Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_LOCAL, mClientGoAwayReason);
177 0 : Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_PEER, mPeerGoAwayReason);
178 0 : }
179 :
180 : void
181 0 : Http2Session::LogIO(Http2Session *self, Http2Stream *stream,
182 : const char *label,
183 : const char *data, uint32_t datalen)
184 : {
185 0 : if (!LOG5_ENABLED())
186 0 : return;
187 :
188 0 : LOG5(("Http2Session::LogIO %p stream=%p id=0x%X [%s]",
189 : self, stream, stream ? stream->StreamID() : 0, label));
190 :
191 : // Max line is (16 * 3) + 10(prefix) + newline + null
192 : char linebuf[128];
193 : uint32_t index;
194 0 : char *line = linebuf;
195 :
196 0 : linebuf[127] = 0;
197 :
198 0 : for (index = 0; index < datalen; ++index) {
199 0 : if (!(index % 16)) {
200 0 : if (index) {
201 0 : *line = 0;
202 0 : LOG5(("%s", linebuf));
203 : }
204 0 : line = linebuf;
205 0 : snprintf(line, 128, "%08X: ", index);
206 0 : line += 10;
207 : }
208 0 : snprintf(line, 128 - (line - linebuf), "%02X ", (reinterpret_cast<const uint8_t *>(data))[index]);
209 0 : line += 3;
210 : }
211 0 : if (index) {
212 0 : *line = 0;
213 0 : LOG5(("%s", linebuf));
214 : }
215 : }
216 :
217 : typedef nsresult (*Http2ControlFx) (Http2Session *self);
218 : static Http2ControlFx sControlFunctions[] = {
219 : nullptr, // type 0 data is not a control function
220 : Http2Session::RecvHeaders,
221 : Http2Session::RecvPriority,
222 : Http2Session::RecvRstStream,
223 : Http2Session::RecvSettings,
224 : Http2Session::RecvPushPromise,
225 : Http2Session::RecvPing,
226 : Http2Session::RecvGoAway,
227 : Http2Session::RecvWindowUpdate,
228 : Http2Session::RecvContinuation,
229 : Http2Session::RecvAltSvc, // extension for type 0x0A
230 : Http2Session::RecvUnused, // 0x0B was BLOCKED still radioactive
231 : Http2Session::RecvOrigin // extension for type 0x0C
232 : };
233 :
234 : bool
235 0 : Http2Session::RoomForMoreConcurrent()
236 : {
237 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
238 0 : return (mConcurrent < mMaxConcurrent);
239 : }
240 :
241 : bool
242 0 : Http2Session::RoomForMoreStreams()
243 : {
244 0 : if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
245 0 : return false;
246 :
247 0 : return !mShouldGoAway;
248 : }
249 :
250 : PRIntervalTime
251 0 : Http2Session::IdleTime()
252 : {
253 0 : return PR_IntervalNow() - mLastDataReadEpoch;
254 : }
255 :
256 : uint32_t
257 0 : Http2Session::ReadTimeoutTick(PRIntervalTime now)
258 : {
259 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
260 :
261 0 : LOG3(("Http2Session::ReadTimeoutTick %p delta since last read %ds\n",
262 : this, PR_IntervalToSeconds(now - mLastReadEpoch)));
263 :
264 0 : if (!mPingThreshold)
265 0 : return UINT32_MAX;
266 :
267 0 : if ((now - mLastReadEpoch) < mPingThreshold) {
268 : // recent activity means ping is not an issue
269 0 : if (mPingSentEpoch) {
270 0 : mPingSentEpoch = 0;
271 0 : if (mPreviousUsed) {
272 : // restore the former value
273 0 : mPingThreshold = mPreviousPingThreshold;
274 0 : mPreviousUsed = false;
275 : }
276 : }
277 :
278 0 : return PR_IntervalToSeconds(mPingThreshold) -
279 0 : PR_IntervalToSeconds(now - mLastReadEpoch);
280 : }
281 :
282 0 : if (mPingSentEpoch) {
283 0 : LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n", this));
284 0 : if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
285 0 : LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n", this));
286 0 : mPingSentEpoch = 0;
287 0 : Close(NS_ERROR_NET_TIMEOUT);
288 0 : return UINT32_MAX;
289 : }
290 0 : return 1; // run the tick aggressively while ping is outstanding
291 : }
292 :
293 0 : LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this));
294 :
295 0 : mPingSentEpoch = PR_IntervalNow();
296 0 : if (!mPingSentEpoch) {
297 0 : mPingSentEpoch = 1; // avoid the 0 sentinel value
298 : }
299 0 : GeneratePing(false);
300 0 : Unused << ResumeRecv(); // read the ping reply
301 :
302 : // Check for orphaned push streams. This looks expensive, but generally the
303 : // list is empty.
304 : Http2PushedStream *deleteMe;
305 0 : TimeStamp timestampNow;
306 0 : do {
307 0 : deleteMe = nullptr;
308 :
309 0 : for (uint32_t index = mPushedStreams.Length();
310 0 : index > 0 ; --index) {
311 0 : Http2PushedStream *pushedStream = mPushedStreams[index - 1];
312 :
313 0 : if (timestampNow.IsNull())
314 0 : timestampNow = TimeStamp::Now(); // lazy initializer
315 :
316 : // if stream finished, but is not connected, and its been like that for
317 : // long then cleanup the stream.
318 0 : if (pushedStream->IsOrphaned(timestampNow))
319 : {
320 0 : LOG3(("Http2Session Timeout Pushed Stream %p 0x%X\n",
321 : this, pushedStream->StreamID()));
322 0 : deleteMe = pushedStream;
323 0 : break; // don't CleanupStream() while iterating this vector
324 : }
325 : }
326 0 : if (deleteMe)
327 0 : CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR);
328 :
329 0 : } while (deleteMe);
330 :
331 0 : return 1; // run the tick aggressively while ping is outstanding
332 : }
333 :
334 : uint32_t
335 0 : Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID)
336 : {
337 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
338 0 : MOZ_ASSERT(mNextStreamID < 0xfffffff0,
339 : "should have stopped admitting streams");
340 0 : MOZ_ASSERT(!(aNewID & 1),
341 : "0 for autoassign pull, otherwise explicit even push assignment");
342 :
343 0 : if (!aNewID) {
344 : // auto generate a new pull stream ID
345 0 : aNewID = mNextStreamID;
346 0 : MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
347 0 : mNextStreamID += 2;
348 : }
349 :
350 0 : LOG3(("Http2Session::RegisterStreamID session=%p stream=%p id=0x%X "
351 : "concurrent=%d",this, stream, aNewID, mConcurrent));
352 :
353 : // We've used up plenty of ID's on this session. Start
354 : // moving to a new one before there is a crunch involving
355 : // server push streams or concurrent non-registered submits
356 0 : if (aNewID >= kMaxStreamID)
357 0 : mShouldGoAway = true;
358 :
359 : // integrity check
360 0 : if (mStreamIDHash.Get(aNewID)) {
361 0 : LOG3((" New ID already present\n"));
362 0 : MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
363 : mShouldGoAway = true;
364 : return kDeadStreamID;
365 : }
366 :
367 0 : mStreamIDHash.Put(aNewID, stream);
368 0 : return aNewID;
369 : }
370 :
371 : bool
372 0 : Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
373 : int32_t aPriority,
374 : bool aUseTunnel,
375 : nsIInterfaceRequestor *aCallbacks)
376 : {
377 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
378 :
379 : // integrity check
380 0 : if (mStreamTransactionHash.Get(aHttpTransaction)) {
381 0 : LOG3((" New transaction already present\n"));
382 0 : MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
383 : return false;
384 : }
385 :
386 0 : if (!mConnection) {
387 0 : mConnection = aHttpTransaction->Connection();
388 : }
389 :
390 0 : if (mClosed || mShouldGoAway) {
391 0 : nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
392 0 : if (trans && !trans->GetPushedStream()) {
393 0 : LOG3(("Http2Session::AddStream %p atrans=%p trans=%p session unusable - resched.\n",
394 : this, aHttpTransaction, trans));
395 0 : aHttpTransaction->SetConnection(nullptr);
396 0 : nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
397 0 : if (NS_FAILED(rv)) {
398 0 : LOG3(("Http2Session::AddStream %p atrans=%p trans=%p failed to initiate "
399 : "transaction (%08x).\n", this, aHttpTransaction, trans,
400 : static_cast<uint32_t>(rv)));
401 : }
402 0 : return true;
403 : }
404 : }
405 :
406 0 : aHttpTransaction->SetConnection(this);
407 0 : aHttpTransaction->OnActivated(true);
408 :
409 0 : if (aUseTunnel) {
410 0 : LOG3(("Http2Session::AddStream session=%p trans=%p OnTunnel",
411 : this, aHttpTransaction));
412 0 : DispatchOnTunnel(aHttpTransaction, aCallbacks);
413 0 : return true;
414 : }
415 :
416 0 : Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
417 :
418 0 : LOG3(("Http2Session::AddStream session=%p stream=%p serial=%" PRIu64 " "
419 : "NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
420 :
421 0 : mStreamTransactionHash.Put(aHttpTransaction, stream);
422 :
423 0 : mReadyForWrite.Push(stream);
424 0 : SetWriteCallbacks();
425 :
426 : // Kick off the SYN transmit without waiting for the poll loop
427 : // This won't work for the first stream because there is no segment reader
428 : // yet.
429 0 : if (mSegmentReader) {
430 : uint32_t countRead;
431 0 : Unused << ReadSegments(nullptr, kDefaultBufferSize, &countRead);
432 : }
433 :
434 0 : if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
435 0 : !aHttpTransaction->IsNullTransaction()) {
436 0 : LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n",
437 : this, aHttpTransaction));
438 0 : DontReuse();
439 : }
440 :
441 0 : return true;
442 : }
443 :
444 : void
445 0 : Http2Session::QueueStream(Http2Stream *stream)
446 : {
447 : // will be removed via processpending or a shutdown path
448 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
449 0 : MOZ_ASSERT(!stream->CountAsActive());
450 0 : MOZ_ASSERT(!stream->Queued());
451 :
452 0 : LOG3(("Http2Session::QueueStream %p stream %p queued.", this, stream));
453 :
454 : #ifdef DEBUG
455 0 : int32_t qsize = mQueuedStreams.GetSize();
456 0 : for (int32_t i = 0; i < qsize; i++) {
457 0 : Http2Stream *qStream = static_cast<Http2Stream *>(mQueuedStreams.ObjectAt(i));
458 0 : MOZ_ASSERT(qStream != stream);
459 0 : MOZ_ASSERT(qStream->Queued());
460 : }
461 : #endif
462 :
463 0 : stream->SetQueued(true);
464 0 : mQueuedStreams.Push(stream);
465 0 : }
466 :
467 : void
468 0 : Http2Session::ProcessPending()
469 : {
470 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
471 :
472 : Http2Stream*stream;
473 0 : while (RoomForMoreConcurrent() &&
474 0 : (stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront()))) {
475 :
476 0 : LOG3(("Http2Session::ProcessPending %p stream %p woken from queue.",
477 : this, stream));
478 0 : MOZ_ASSERT(!stream->CountAsActive());
479 0 : MOZ_ASSERT(stream->Queued());
480 0 : stream->SetQueued(false);
481 0 : mReadyForWrite.Push(stream);
482 0 : SetWriteCallbacks();
483 : }
484 0 : }
485 :
486 : nsresult
487 0 : Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
488 : uint32_t count, uint32_t *countWritten)
489 : {
490 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
491 :
492 0 : if (!count) {
493 0 : *countWritten = 0;
494 0 : return NS_OK;
495 : }
496 :
497 0 : nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
498 0 : if (NS_SUCCEEDED(rv) && *countWritten > 0)
499 0 : mLastReadEpoch = PR_IntervalNow();
500 0 : return rv;
501 : }
502 :
503 : void
504 0 : Http2Session::SetWriteCallbacks()
505 : {
506 0 : if (mConnection &&
507 0 : (GetWriteQueueSize() || (mOutputQueueUsed > mOutputQueueSent))) {
508 0 : Unused << mConnection->ResumeSend();
509 : }
510 0 : }
511 :
512 : void
513 0 : Http2Session::RealignOutputQueue()
514 : {
515 0 : if (mAttemptingEarlyData) {
516 : // We can't realign right now, because we may need what's in there if early
517 : // data fails.
518 0 : return;
519 : }
520 :
521 0 : mOutputQueueUsed -= mOutputQueueSent;
522 0 : memmove(mOutputQueueBuffer.get(),
523 0 : mOutputQueueBuffer.get() + mOutputQueueSent,
524 0 : mOutputQueueUsed);
525 0 : mOutputQueueSent = 0;
526 : }
527 :
528 : void
529 0 : Http2Session::FlushOutputQueue()
530 : {
531 0 : if (!mSegmentReader || !mOutputQueueUsed)
532 0 : return;
533 :
534 : nsresult rv;
535 : uint32_t countRead;
536 0 : uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
537 :
538 0 : if (!avail && mAttemptingEarlyData) {
539 : // This is kind of a hack, but there are cases where we'll have already
540 : // written the data we want whlie doing early data, but we get called again
541 : // with a reader, and we need to avoid calling the reader when there's
542 : // nothing for it to read.
543 0 : return;
544 : }
545 :
546 0 : rv = mSegmentReader->
547 0 : OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
548 0 : &countRead);
549 0 : LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%" PRIx32 " actual=%d",
550 : this, avail, static_cast<uint32_t>(rv), countRead));
551 :
552 : // Dont worry about errors on write, we will pick this up as a read error too
553 0 : if (NS_FAILED(rv))
554 0 : return;
555 :
556 0 : mOutputQueueSent += countRead;
557 :
558 0 : if (mAttemptingEarlyData) {
559 0 : return;
560 : }
561 :
562 0 : if (countRead == avail) {
563 0 : mOutputQueueUsed = 0;
564 0 : mOutputQueueSent = 0;
565 0 : return;
566 : }
567 :
568 : // If the output queue is close to filling up and we have sent out a good
569 : // chunk of data from the beginning then realign it.
570 :
571 0 : if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
572 0 : ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
573 0 : RealignOutputQueue();
574 : }
575 : }
576 :
577 : void
578 0 : Http2Session::DontReuse()
579 : {
580 0 : LOG3(("Http2Session::DontReuse %p\n", this));
581 0 : mShouldGoAway = true;
582 0 : if (!mStreamTransactionHash.Count())
583 0 : Close(NS_OK);
584 0 : }
585 :
586 : uint32_t
587 0 : Http2Session::SpdyVersion()
588 : {
589 0 : return HTTP_VERSION_2;
590 : }
591 :
592 : uint32_t
593 0 : Http2Session::GetWriteQueueSize()
594 : {
595 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
596 :
597 0 : return mReadyForWrite.GetSize();
598 : }
599 :
600 : void
601 0 : Http2Session::ChangeDownstreamState(enum internalStateType newState)
602 : {
603 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
604 :
605 0 : LOG3(("Http2Session::ChangeDownstreamState() %p from %X to %X",
606 : this, mDownstreamState, newState));
607 0 : mDownstreamState = newState;
608 0 : }
609 :
610 : void
611 0 : Http2Session::ResetDownstreamState()
612 : {
613 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
614 :
615 0 : LOG3(("Http2Session::ResetDownstreamState() %p", this));
616 0 : ChangeDownstreamState(BUFFERING_FRAME_HEADER);
617 :
618 0 : if (mInputFrameFinal && mInputFrameDataStream) {
619 0 : mInputFrameFinal = false;
620 0 : LOG3((" SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
621 0 : mInputFrameDataStream->SetRecvdFin(true);
622 0 : MaybeDecrementConcurrent(mInputFrameDataStream);
623 : }
624 0 : mInputFrameFinal = false;
625 0 : mInputFrameBufferUsed = 0;
626 0 : mInputFrameDataStream = nullptr;
627 0 : }
628 :
629 : // return true if activated (and counted against max)
630 : // otherwise return false and queue
631 : bool
632 0 : Http2Session::TryToActivate(Http2Stream *aStream)
633 : {
634 0 : if (aStream->Queued()) {
635 0 : LOG3(("Http2Session::TryToActivate %p stream=%p already queued.\n", this, aStream));
636 0 : return false;
637 : }
638 :
639 0 : if (!RoomForMoreConcurrent()) {
640 0 : LOG3(("Http2Session::TryToActivate %p stream=%p no room for more concurrent "
641 : "streams\n", this, aStream));
642 0 : QueueStream(aStream);
643 0 : return false;
644 : }
645 :
646 0 : LOG3(("Http2Session::TryToActivate %p stream=%p\n", this, aStream));
647 0 : IncrementConcurrent(aStream);
648 0 : return true;
649 : }
650 :
651 : void
652 0 : Http2Session::IncrementConcurrent(Http2Stream *stream)
653 : {
654 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
655 0 : MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
656 : "Do not activate pushed streams");
657 :
658 0 : nsAHttpTransaction *trans = stream->Transaction();
659 0 : if (!trans || !trans->IsNullTransaction() || trans->QuerySpdyConnectTransaction()) {
660 :
661 0 : MOZ_ASSERT(!stream->CountAsActive());
662 0 : stream->SetCountAsActive(true);
663 0 : ++mConcurrent;
664 :
665 0 : if (mConcurrent > mConcurrentHighWater) {
666 0 : mConcurrentHighWater = mConcurrent;
667 : }
668 0 : LOG3(("Http2Session::IncrementCounter %p counting stream %p Currently %d "
669 : "streams in session, high water mark is %d\n",
670 : this, stream, mConcurrent, mConcurrentHighWater));
671 : }
672 0 : }
673 :
674 : // call with data length (i.e. 0 for 0 data bytes - ignore 9 byte header)
675 : // dest must have 9 bytes of allocated space
676 : template<typename charType> void
677 0 : Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength,
678 : uint8_t frameType, uint8_t frameFlags,
679 : uint32_t streamID)
680 : {
681 0 : MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large");
682 0 : MOZ_ASSERT(!(streamID & 0x80000000));
683 0 : MOZ_ASSERT(!frameFlags ||
684 : (frameType != FRAME_TYPE_PRIORITY &&
685 : frameType != FRAME_TYPE_RST_STREAM &&
686 : frameType != FRAME_TYPE_GOAWAY &&
687 : frameType != FRAME_TYPE_WINDOW_UPDATE));
688 :
689 0 : dest[0] = 0x00;
690 0 : NetworkEndian::writeUint16(dest + 1, frameLength);
691 0 : dest[3] = frameType;
692 0 : dest[4] = frameFlags;
693 0 : NetworkEndian::writeUint32(dest + 5, streamID);
694 0 : }
695 :
696 : char *
697 0 : Http2Session::EnsureOutputBuffer(uint32_t spaceNeeded)
698 : {
699 : // this is an infallible allocation (if an allocation is
700 : // needed, which is probably isn't)
701 0 : EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + spaceNeeded,
702 0 : mOutputQueueUsed, mOutputQueueSize);
703 0 : return mOutputQueueBuffer.get() + mOutputQueueUsed;
704 : }
705 :
706 : template void
707 : Http2Session::CreateFrameHeader(char *dest, uint16_t frameLength,
708 : uint8_t frameType, uint8_t frameFlags,
709 : uint32_t streamID);
710 :
711 : template void
712 : Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength,
713 : uint8_t frameType, uint8_t frameFlags,
714 : uint32_t streamID);
715 :
716 : void
717 0 : Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream)
718 : {
719 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
720 0 : LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n",
721 : this, aStream->StreamID(), mConcurrent, aStream->CountAsActive()));
722 :
723 0 : if (!aStream->CountAsActive())
724 0 : return;
725 :
726 0 : MOZ_ASSERT(mConcurrent);
727 0 : aStream->SetCountAsActive(false);
728 0 : --mConcurrent;
729 0 : ProcessPending();
730 : }
731 :
732 : // Need to decompress some data in order to keep the compression
733 : // context correct, but we really don't care what the result is
734 : nsresult
735 0 : Http2Session::UncompressAndDiscard(bool isPush)
736 : {
737 : nsresult rv;
738 0 : nsAutoCString trash;
739 :
740 0 : rv = mDecompressor.DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(mDecompressBuffer.BeginReading()),
741 0 : mDecompressBuffer.Length(), trash, isPush);
742 0 : mDecompressBuffer.Truncate();
743 0 : if (NS_FAILED(rv)) {
744 0 : LOG3(("Http2Session::UncompressAndDiscard %p Compression Error\n",
745 : this));
746 0 : mGoAwayReason = COMPRESSION_ERROR;
747 0 : return rv;
748 : }
749 0 : return NS_OK;
750 : }
751 :
752 : void
753 0 : Http2Session::GeneratePing(bool isAck)
754 : {
755 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
756 0 : LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck));
757 :
758 0 : char *packet = EnsureOutputBuffer(kFrameHeaderBytes + 8);
759 0 : mOutputQueueUsed += kFrameHeaderBytes + 8;
760 :
761 0 : if (isAck) {
762 0 : CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0);
763 0 : memcpy(packet + kFrameHeaderBytes,
764 0 : mInputFrameBuffer.get() + kFrameHeaderBytes, 8);
765 : } else {
766 0 : CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0);
767 0 : memset(packet + kFrameHeaderBytes, 0, 8);
768 : }
769 :
770 0 : LogIO(this, nullptr, "Generate Ping", packet, kFrameHeaderBytes + 8);
771 0 : FlushOutputQueue();
772 0 : }
773 :
774 : void
775 0 : Http2Session::GenerateSettingsAck()
776 : {
777 : // need to generate ack of this settings frame
778 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
779 0 : LOG3(("Http2Session::GenerateSettingsAck %p\n", this));
780 :
781 0 : char *packet = EnsureOutputBuffer(kFrameHeaderBytes);
782 0 : mOutputQueueUsed += kFrameHeaderBytes;
783 0 : CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0);
784 0 : LogIO(this, nullptr, "Generate Settings ACK", packet, kFrameHeaderBytes);
785 0 : FlushOutputQueue();
786 0 : }
787 :
788 : void
789 0 : Http2Session::GeneratePriority(uint32_t aID, uint8_t aPriorityWeight)
790 : {
791 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
792 0 : LOG3(("Http2Session::GeneratePriority %p %X %X\n",
793 : this, aID, aPriorityWeight));
794 :
795 0 : uint32_t frameSize = kFrameHeaderBytes + 5;
796 0 : char *packet = EnsureOutputBuffer(frameSize);
797 0 : mOutputQueueUsed += frameSize;
798 :
799 0 : CreateFrameHeader(packet, 5, FRAME_TYPE_PRIORITY, 0, aID);
800 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, 0);
801 0 : memcpy(packet + frameSize - 1, &aPriorityWeight, 1);
802 0 : LogIO(this, nullptr, "Generate Priority", packet, frameSize);
803 0 : FlushOutputQueue();
804 0 : }
805 :
806 : void
807 0 : Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
808 : {
809 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
810 :
811 : // make sure we don't do this twice for the same stream (at least if we
812 : // have a stream entry for it)
813 0 : Http2Stream *stream = mStreamIDHash.Get(aID);
814 0 : if (stream) {
815 0 : if (stream->SentReset())
816 0 : return;
817 0 : stream->SetSentReset(true);
818 : }
819 :
820 0 : LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
821 :
822 0 : uint32_t frameSize = kFrameHeaderBytes + 4;
823 0 : char *packet = EnsureOutputBuffer(frameSize);
824 0 : mOutputQueueUsed += frameSize;
825 0 : CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID);
826 :
827 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, aStatusCode);
828 :
829 0 : LogIO(this, nullptr, "Generate Reset", packet, frameSize);
830 0 : FlushOutputQueue();
831 : }
832 :
833 : void
834 0 : Http2Session::GenerateGoAway(uint32_t aStatusCode)
835 : {
836 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
837 0 : LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
838 :
839 0 : mClientGoAwayReason = aStatusCode;
840 0 : uint32_t frameSize = kFrameHeaderBytes + 8;
841 0 : char *packet = EnsureOutputBuffer(frameSize);
842 0 : mOutputQueueUsed += frameSize;
843 :
844 0 : CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
845 :
846 : // last-good-stream-id are bytes 9-12 reflecting pushes
847 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, mOutgoingGoAwayID);
848 :
849 : // bytes 13-16 are the status code.
850 0 : NetworkEndian::writeUint32(packet + frameSize - 4, aStatusCode);
851 :
852 0 : LogIO(this, nullptr, "Generate GoAway", packet, frameSize);
853 0 : FlushOutputQueue();
854 0 : }
855 :
856 : // The Hello is comprised of
857 : // 1] 24 octets of magic, which are designed to
858 : // flush out silent but broken intermediaries
859 : // 2] a settings frame which sets a small flow control window for pushes
860 : // 3] a window update frame which creates a large session flow control window
861 : // 4] 5 priority frames for streams which will never be opened with headers
862 : // these streams (3, 5, 7, 9, b) build a dependency tree that all other
863 : // streams will be direct leaves of.
864 : void
865 0 : Http2Session::SendHello()
866 : {
867 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
868 0 : LOG3(("Http2Session::SendHello %p\n", this));
869 :
870 : // sized for magic + 5 settings and a session window update and 5 priority frames
871 : // 24 magic, 33 for settings (9 header + 4 settings @6), 13 for window update,
872 : // 5 priority frames at 14 (9 + 5) each
873 : static const uint32_t maxSettings = 5;
874 : static const uint32_t prioritySize = 5 * (kFrameHeaderBytes + 5);
875 : static const uint32_t maxDataLen = 24 + kFrameHeaderBytes + maxSettings * 6 + 13 + prioritySize;
876 0 : char *packet = EnsureOutputBuffer(maxDataLen);
877 0 : memcpy(packet, kMagicHello, 24);
878 0 : mOutputQueueUsed += 24;
879 0 : LogIO(this, nullptr, "Magic Connection Header", packet, 24);
880 :
881 0 : packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
882 0 : memset(packet, 0, maxDataLen - 24);
883 :
884 : // frame header will be filled in after we know how long the frame is
885 0 : uint8_t numberOfEntries = 0;
886 :
887 : // entries need to be listed in order by ID
888 : // 1st entry is bytes 9 to 14
889 : // 2nd entry is bytes 15 to 20
890 : // 3rd entry is bytes 21 to 26
891 : // 4th entry is bytes 27 to 32
892 : // 5th entry is bytes 33 to 38
893 :
894 : // Let the other endpoint know about our default HPACK decompress table size
895 0 : uint32_t maxHpackBufferSize = gHttpHandler->DefaultHpackBuffer();
896 0 : mDecompressor.SetInitialMaxBufferSize(maxHpackBufferSize);
897 0 : NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_HEADER_TABLE_SIZE);
898 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, maxHpackBufferSize);
899 0 : numberOfEntries++;
900 :
901 0 : if (!gHttpHandler->AllowPush()) {
902 : // If we don't support push then set MAX_CONCURRENT to 0 and also
903 : // set ENABLE_PUSH to 0
904 0 : NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_ENABLE_PUSH);
905 : // The value portion of the setting pair is already initialized to 0
906 0 : numberOfEntries++;
907 :
908 0 : NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_CONCURRENT);
909 : // The value portion of the setting pair is already initialized to 0
910 0 : numberOfEntries++;
911 :
912 0 : mWaitingForSettingsAck = true;
913 : }
914 :
915 : // Advertise the Push RWIN for the session, and on each new pull stream
916 : // send a window update
917 0 : NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_INITIAL_WINDOW);
918 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, mPushAllowance);
919 0 : numberOfEntries++;
920 :
921 : // Make sure the other endpoint knows that we're sticking to the default max
922 : // frame size
923 0 : NetworkEndian::writeUint16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_MAX_FRAME_SIZE);
924 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, kMaxFrameData);
925 0 : numberOfEntries++;
926 :
927 0 : MOZ_ASSERT(numberOfEntries <= maxSettings);
928 0 : uint32_t dataLen = 6 * numberOfEntries;
929 0 : CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0);
930 0 : mOutputQueueUsed += kFrameHeaderBytes + dataLen;
931 :
932 0 : LogIO(this, nullptr, "Generate Settings", packet, kFrameHeaderBytes + dataLen);
933 :
934 : // now bump the local session window from 64KB
935 0 : uint32_t sessionWindowBump = mInitialRwin - kDefaultRwin;
936 0 : if (kDefaultRwin < mInitialRwin) {
937 : // send a window update for the session (Stream 0) for something large
938 0 : mLocalSessionWindow = mInitialRwin;
939 :
940 0 : packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
941 0 : CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
942 0 : mOutputQueueUsed += kFrameHeaderBytes + 4;
943 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, sessionWindowBump);
944 :
945 0 : LOG3(("Session Window increase at start of session %p %u\n",
946 : this, sessionWindowBump));
947 0 : LogIO(this, nullptr, "Session Window Bump ", packet, kFrameHeaderBytes + 4);
948 : }
949 :
950 0 : if (gHttpHandler->UseH2Deps() && gHttpHandler->CriticalRequestPrioritization()) {
951 0 : mUseH2Deps = true;
952 0 : MOZ_ASSERT(mNextStreamID == kLeaderGroupID);
953 0 : CreatePriorityNode(kLeaderGroupID, 0, 200, "leader");
954 0 : mNextStreamID += 2;
955 0 : MOZ_ASSERT(mNextStreamID == kOtherGroupID);
956 0 : CreatePriorityNode(kOtherGroupID, 0, 100, "other");
957 0 : mNextStreamID += 2;
958 0 : MOZ_ASSERT(mNextStreamID == kBackgroundGroupID);
959 0 : CreatePriorityNode(kBackgroundGroupID, 0, 0, "background");
960 0 : mNextStreamID += 2;
961 0 : MOZ_ASSERT(mNextStreamID == kSpeculativeGroupID);
962 0 : CreatePriorityNode(kSpeculativeGroupID, kBackgroundGroupID, 0, "speculative");
963 0 : mNextStreamID += 2;
964 0 : MOZ_ASSERT(mNextStreamID == kFollowerGroupID);
965 0 : CreatePriorityNode(kFollowerGroupID, kLeaderGroupID, 0, "follower");
966 0 : mNextStreamID += 2;
967 0 : MOZ_ASSERT(mNextStreamID == kUrgentStartGroupID);
968 0 : CreatePriorityNode(kUrgentStartGroupID, 0, 240, "urgentStart");
969 0 : mNextStreamID += 2;
970 : // Hey, you! YES YOU! If you add/remove any groups here, you almost
971 : // certainly need to change the lookup of the stream/ID hash in
972 : // Http2Session::OnTransportStatus. Yeah, that's right. YOU!
973 : }
974 :
975 0 : FlushOutputQueue();
976 0 : }
977 :
978 : void
979 0 : Http2Session::CreatePriorityNode(uint32_t streamID, uint32_t dependsOn, uint8_t weight,
980 : const char *label)
981 : {
982 0 : char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
983 0 : CreateFrameHeader(packet, 5, FRAME_TYPE_PRIORITY, 0, streamID);
984 0 : mOutputQueueUsed += kFrameHeaderBytes + 5;
985 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, dependsOn); // depends on
986 0 : packet[kFrameHeaderBytes + 4] = weight; // weight
987 :
988 0 : LOG3(("Http2Session %p generate Priority Frame 0x%X depends on 0x%X "
989 : "weight %d for %s class\n", this, streamID, dependsOn, weight, label));
990 0 : LogIO(this, nullptr, "Priority dep node", packet, kFrameHeaderBytes + 5);
991 0 : }
992 :
993 : // perform a bunch of integrity checks on the stream.
994 : // returns true if passed, false (plus LOG and ABORT) if failed.
995 : bool
996 0 : Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0)
997 : {
998 : // This is annoying, but at least it is O(1)
999 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1000 :
1001 : #ifndef DEBUG
1002 : // Only do the real verification in debug builds
1003 : return true;
1004 : #else //DEBUG
1005 :
1006 0 : if (!aStream)
1007 0 : return true;
1008 :
1009 0 : uint32_t test = 0;
1010 :
1011 : do {
1012 0 : if (aStream->StreamID() == kDeadStreamID)
1013 0 : break;
1014 :
1015 0 : nsAHttpTransaction *trans = aStream->Transaction();
1016 :
1017 0 : test++;
1018 0 : if (!trans)
1019 0 : break;
1020 :
1021 0 : test++;
1022 0 : if (mStreamTransactionHash.Get(trans) != aStream)
1023 0 : break;
1024 :
1025 0 : if (aStream->StreamID()) {
1026 0 : Http2Stream *idStream = mStreamIDHash.Get(aStream->StreamID());
1027 :
1028 0 : test++;
1029 0 : if (idStream != aStream)
1030 0 : break;
1031 :
1032 0 : if (aOptionalID) {
1033 0 : test++;
1034 0 : if (idStream->StreamID() != aOptionalID)
1035 0 : break;
1036 : }
1037 : }
1038 :
1039 : // tests passed
1040 0 : return true;
1041 : } while (0);
1042 :
1043 0 : LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X "
1044 : "optionalID=0x%X trans=%p test=%d\n",
1045 : this, aStream, aStream->StreamID(),
1046 : aOptionalID, aStream->Transaction(), test));
1047 :
1048 0 : MOZ_ASSERT(false, "VerifyStream");
1049 : return false;
1050 : #endif //DEBUG
1051 : }
1052 :
1053 : void
1054 0 : Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
1055 : errorType aResetCode)
1056 : {
1057 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1058 0 : LOG3(("Http2Session::CleanupStream %p %p 0x%X %" PRIX32 "\n",
1059 : this, aStream, aStream ? aStream->StreamID() : 0, static_cast<uint32_t>(aResult)));
1060 0 : if (!aStream) {
1061 0 : return;
1062 : }
1063 :
1064 0 : if (aStream->DeferCleanup(aResult)) {
1065 0 : LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
1066 0 : return;
1067 : }
1068 :
1069 0 : if (!VerifyStream(aStream)) {
1070 0 : LOG3(("Http2Session::CleanupStream failed to verify stream\n"));
1071 0 : return;
1072 : }
1073 :
1074 0 : Http2PushedStream *pushSource = aStream->PushSource();
1075 0 : if (pushSource) {
1076 : // aStream is a synthetic attached to an even push
1077 0 : MOZ_ASSERT(pushSource->GetConsumerStream() == aStream);
1078 0 : MOZ_ASSERT(!aStream->StreamID());
1079 0 : MOZ_ASSERT(!(pushSource->StreamID() & 0x1));
1080 0 : pushSource->SetConsumerStream(nullptr);
1081 : }
1082 :
1083 : // don't reset a stream that has recevied a fin or rst
1084 0 : if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID() &&
1085 0 : !(mInputFrameFinal && (aStream == mInputFrameDataStream))) { // !(recvdfin with mark pending)
1086 0 : LOG3(("Stream 0x%X had not processed recv FIN, sending RST code %X\n", aStream->StreamID(), aResetCode));
1087 0 : GenerateRstStream(aResetCode, aStream->StreamID());
1088 : }
1089 :
1090 0 : CloseStream(aStream, aResult);
1091 :
1092 : // Remove the stream from the ID hash table and, if an even id, the pushed
1093 : // table too.
1094 0 : uint32_t id = aStream->StreamID();
1095 0 : if (id > 0) {
1096 0 : mStreamIDHash.Remove(id);
1097 0 : if (!(id & 1)) {
1098 0 : mPushedStreams.RemoveElement(aStream);
1099 0 : Http2PushedStream *pushStream = static_cast<Http2PushedStream *>(aStream);
1100 0 : nsAutoCString hashKey;
1101 0 : DebugOnly<bool> rv = pushStream->GetHashKey(hashKey);
1102 0 : MOZ_ASSERT(rv);
1103 0 : nsIRequestContext *requestContext = aStream->RequestContext();
1104 0 : if (requestContext) {
1105 0 : SpdyPushCache *cache = nullptr;
1106 0 : requestContext->GetSpdyPushCache(&cache);
1107 0 : if (cache) {
1108 : // Make sure the id of the stream in the push cache is the same
1109 : // as the id of the stream we're cleaning up! See bug 1368080.
1110 0 : Http2PushedStream *trash = cache->RemovePushedStreamHttp2ByID(hashKey, aStream->StreamID());
1111 0 : LOG3(("Http2Session::CleanupStream %p aStream=%p pushStream=%p trash=%p",
1112 : this, aStream, pushStream, trash));
1113 : }
1114 : }
1115 : }
1116 : }
1117 :
1118 0 : RemoveStreamFromQueues(aStream);
1119 :
1120 : // removing from the stream transaction hash will
1121 : // delete the Http2Stream and drop the reference to
1122 : // its transaction
1123 0 : mStreamTransactionHash.Remove(aStream->Transaction());
1124 :
1125 0 : if (mShouldGoAway && !mStreamTransactionHash.Count())
1126 0 : Close(NS_OK);
1127 :
1128 0 : if (pushSource) {
1129 0 : pushSource->SetDeferCleanupOnSuccess(false);
1130 0 : CleanupStream(pushSource, aResult, aResetCode);
1131 : }
1132 : }
1133 :
1134 : void
1135 0 : Http2Session::CleanupStream(uint32_t aID, nsresult aResult, errorType aResetCode)
1136 : {
1137 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1138 0 : Http2Stream *stream = mStreamIDHash.Get(aID);
1139 0 : LOG3(("Http2Session::CleanupStream %p by ID 0x%X to stream %p\n",
1140 : this, aID, stream));
1141 0 : if (!stream) {
1142 0 : return;
1143 : }
1144 0 : CleanupStream(stream, aResult, aResetCode);
1145 : }
1146 :
1147 0 : static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue)
1148 : {
1149 0 : size_t size = queue.GetSize();
1150 0 : for (size_t count = 0; count < size; ++count) {
1151 0 : Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront());
1152 0 : if (stream != aStream)
1153 0 : queue.Push(stream);
1154 : }
1155 0 : }
1156 :
1157 : void
1158 0 : Http2Session::RemoveStreamFromQueues(Http2Stream *aStream)
1159 : {
1160 0 : RemoveStreamFromQueue(aStream, mReadyForWrite);
1161 0 : RemoveStreamFromQueue(aStream, mQueuedStreams);
1162 0 : RemoveStreamFromQueue(aStream, mPushesReadyForRead);
1163 0 : RemoveStreamFromQueue(aStream, mSlowConsumersReadyForRead);
1164 0 : }
1165 :
1166 : void
1167 0 : Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult)
1168 : {
1169 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1170 0 : LOG3(("Http2Session::CloseStream %p %p 0x%x %" PRIX32 "\n",
1171 : this, aStream, aStream->StreamID(), static_cast<uint32_t>(aResult)));
1172 :
1173 0 : MaybeDecrementConcurrent(aStream);
1174 :
1175 : // Check if partial frame reader
1176 0 : if (aStream == mInputFrameDataStream) {
1177 0 : LOG3(("Stream had active partial read frame on close"));
1178 0 : ChangeDownstreamState(DISCARDING_DATA_FRAME);
1179 0 : mInputFrameDataStream = nullptr;
1180 : }
1181 :
1182 0 : RemoveStreamFromQueues(aStream);
1183 :
1184 0 : if (aStream->IsTunnel()) {
1185 0 : UnRegisterTunnel(aStream);
1186 : }
1187 :
1188 : // Send the stream the close() indication
1189 0 : aStream->Close(aResult);
1190 0 : }
1191 :
1192 : nsresult
1193 0 : Http2Session::SetInputFrameDataStream(uint32_t streamID)
1194 : {
1195 0 : mInputFrameDataStream = mStreamIDHash.Get(streamID);
1196 0 : if (VerifyStream(mInputFrameDataStream, streamID))
1197 0 : return NS_OK;
1198 :
1199 0 : LOG3(("Http2Session::SetInputFrameDataStream failed to verify 0x%X\n",
1200 : streamID));
1201 0 : mInputFrameDataStream = nullptr;
1202 0 : return NS_ERROR_UNEXPECTED;
1203 : }
1204 :
1205 : nsresult
1206 0 : Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength)
1207 : {
1208 0 : if (mInputFrameFlags & kFlag_PADDED) {
1209 0 : paddingLength = *reinterpret_cast<uint8_t *>(&mInputFrameBuffer[kFrameHeaderBytes]);
1210 0 : paddingControlBytes = 1;
1211 : } else {
1212 0 : paddingLength = 0;
1213 0 : paddingControlBytes = 0;
1214 : }
1215 :
1216 0 : if (static_cast<uint32_t>(paddingLength + paddingControlBytes) > mInputFrameDataSize) {
1217 : // This is fatal to the session
1218 0 : LOG3(("Http2Session::ParsePadding %p stream 0x%x PROTOCOL_ERROR "
1219 : "paddingLength %d > frame size %d\n",
1220 : this, mInputFrameID, paddingLength, mInputFrameDataSize));
1221 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
1222 : }
1223 :
1224 0 : return NS_OK;
1225 : }
1226 :
1227 : nsresult
1228 0 : Http2Session::RecvHeaders(Http2Session *self)
1229 : {
1230 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_HEADERS ||
1231 : self->mInputFrameType == FRAME_TYPE_CONTINUATION);
1232 :
1233 0 : bool isContinuation = self->mExpectedHeaderID != 0;
1234 :
1235 : // If this doesn't have END_HEADERS set on it then require the next
1236 : // frame to be HEADERS of the same ID
1237 0 : bool endHeadersFlag = self->mInputFrameFlags & kFlag_END_HEADERS;
1238 :
1239 0 : if (endHeadersFlag)
1240 0 : self->mExpectedHeaderID = 0;
1241 : else
1242 0 : self->mExpectedHeaderID = self->mInputFrameID;
1243 :
1244 0 : uint32_t priorityLen = 0;
1245 0 : if (self->mInputFrameFlags & kFlag_PRIORITY) {
1246 0 : priorityLen = 5;
1247 : }
1248 0 : nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
1249 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1250 :
1251 : // Find out how much padding this frame has, so we can only extract the real
1252 : // header data from the frame.
1253 0 : uint16_t paddingLength = 0;
1254 0 : uint8_t paddingControlBytes = 0;
1255 :
1256 0 : if (!isContinuation) {
1257 0 : self->mDecompressBuffer.Truncate();
1258 0 : rv = self->ParsePadding(paddingControlBytes, paddingLength);
1259 0 : if (NS_FAILED(rv)) {
1260 0 : return rv;
1261 : }
1262 : }
1263 :
1264 0 : LOG3(("Http2Session::RecvHeaders %p stream 0x%X priorityLen=%d stream=%p "
1265 : "end_stream=%d end_headers=%d priority_group=%d "
1266 : "paddingLength=%d padded=%d\n",
1267 : self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream,
1268 : self->mInputFrameFlags & kFlag_END_STREAM,
1269 : self->mInputFrameFlags & kFlag_END_HEADERS,
1270 : self->mInputFrameFlags & kFlag_PRIORITY,
1271 : paddingLength,
1272 : self->mInputFrameFlags & kFlag_PADDED));
1273 :
1274 0 : if ((paddingControlBytes + priorityLen + paddingLength) > self->mInputFrameDataSize) {
1275 : // This is fatal to the session
1276 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1277 : }
1278 :
1279 0 : if (!self->mInputFrameDataStream) {
1280 : // Cannot find stream. We can continue the session, but we need to
1281 : // uncompress the header block to maintain the correct compression context
1282 :
1283 0 : LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream "
1284 : "0x%X failed. NextStreamID = 0x%X\n",
1285 : self, self->mInputFrameID, self->mNextStreamID));
1286 :
1287 0 : if (self->mInputFrameID >= self->mNextStreamID)
1288 0 : self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
1289 :
1290 0 : self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen],
1291 0 : self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
1292 :
1293 0 : if (self->mInputFrameFlags & kFlag_END_HEADERS) {
1294 0 : rv = self->UncompressAndDiscard(false);
1295 0 : if (NS_FAILED(rv)) {
1296 0 : LOG3(("Http2Session::RecvHeaders uncompress failed\n"));
1297 : // this is fatal to the session
1298 0 : self->mGoAwayReason = COMPRESSION_ERROR;
1299 0 : return rv;
1300 : }
1301 : }
1302 :
1303 0 : self->ResetDownstreamState();
1304 0 : return NS_OK;
1305 : }
1306 :
1307 : // make sure this is either the first headers or a trailer
1308 0 : if (self->mInputFrameDataStream->AllHeadersReceived() &&
1309 0 : !(self->mInputFrameFlags & kFlag_END_STREAM)) {
1310 : // Any header block after the first that does *not* end the stream is
1311 : // illegal.
1312 0 : LOG3(("Http2Session::Illegal Extra HeaderBlock %p 0x%X\n", self, self->mInputFrameID));
1313 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1314 : }
1315 :
1316 : // queue up any compression bytes
1317 0 : self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen],
1318 0 : self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength);
1319 :
1320 0 : self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
1321 0 : self->mLastDataReadEpoch = self->mLastReadEpoch;
1322 :
1323 0 : if (!isContinuation) {
1324 0 : self->mAggregatedHeaderSize = self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength;
1325 : } else {
1326 0 : self->mAggregatedHeaderSize += self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength;
1327 : }
1328 :
1329 0 : if (!endHeadersFlag) { // more are coming - don't process yet
1330 0 : self->ResetDownstreamState();
1331 0 : return NS_OK;
1332 : }
1333 :
1334 0 : if (isContinuation) {
1335 0 : Telemetry::Accumulate(Telemetry::SPDY_CONTINUED_HEADERS, self->mAggregatedHeaderSize);
1336 : }
1337 :
1338 0 : rv = self->ResponseHeadersComplete();
1339 0 : if (rv == NS_ERROR_ILLEGAL_VALUE) {
1340 0 : LOG3(("Http2Session::RecvHeaders %p PROTOCOL_ERROR detected stream 0x%X\n",
1341 : self, self->mInputFrameID));
1342 0 : self->CleanupStream(self->mInputFrameDataStream, rv, PROTOCOL_ERROR);
1343 0 : self->ResetDownstreamState();
1344 0 : rv = NS_OK;
1345 0 : } else if (NS_FAILED(rv)) {
1346 : // This is fatal to the session.
1347 0 : self->mGoAwayReason = COMPRESSION_ERROR;
1348 : }
1349 0 : return rv;
1350 : }
1351 :
1352 : // ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
1353 : // should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were
1354 : // fine, and any other error is fatal to the session.
1355 : nsresult
1356 0 : Http2Session::ResponseHeadersComplete()
1357 : {
1358 0 : LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d",
1359 : this, mInputFrameDataStream->StreamID(), mInputFrameFinal));
1360 :
1361 : // only interpret headers once, afterwards ignore as trailers
1362 0 : if (mInputFrameDataStream->AllHeadersReceived()) {
1363 0 : LOG3(("Http2Session::ResponseHeadersComplete extra headers"));
1364 0 : MOZ_ASSERT(mInputFrameFlags & kFlag_END_STREAM);
1365 0 : nsresult rv = UncompressAndDiscard(false);
1366 0 : if (NS_FAILED(rv)) {
1367 0 : LOG3(("Http2Session::ResponseHeadersComplete extra uncompress failed\n"));
1368 0 : return rv;
1369 : }
1370 0 : mFlatHTTPResponseHeadersOut = 0;
1371 0 : mFlatHTTPResponseHeaders.Truncate();
1372 0 : if (mInputFrameFinal) {
1373 : // need to process the fin
1374 0 : ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
1375 : } else {
1376 0 : ResetDownstreamState();
1377 : }
1378 :
1379 0 : return NS_OK;
1380 : }
1381 :
1382 : // if this turns out to be a 1xx response code we have to
1383 : // undo the headers received bit that we are setting here.
1384 0 : bool didFirstSetAllRecvd = !mInputFrameDataStream->AllHeadersReceived();
1385 0 : mInputFrameDataStream->SetAllHeadersReceived();
1386 :
1387 : // The stream needs to see flattened http headers
1388 : // Uncompressed http/2 format headers currently live in
1389 : // Http2Stream::mDecompressBuffer - convert that to HTTP format in
1390 : // mFlatHTTPResponseHeaders via ConvertHeaders()
1391 :
1392 : nsresult rv;
1393 : int32_t httpResponseCode; // out param to ConvertResponseHeaders
1394 0 : mFlatHTTPResponseHeadersOut = 0;
1395 0 : rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
1396 : mDecompressBuffer,
1397 : mFlatHTTPResponseHeaders,
1398 0 : httpResponseCode);
1399 0 : if (rv == NS_ERROR_NET_RESET) {
1400 0 : LOG(("Http2Session::ResponseHeadersComplete %p ConvertResponseHeaders reset\n", this));
1401 : // This means the stream found connection-oriented auth. Treat this like we
1402 : // got a reset with HTTP_1_1_REQUIRED.
1403 0 : mInputFrameDataStream->Transaction()->DisableSpdy();
1404 0 : CleanupStream(mInputFrameDataStream, NS_ERROR_NET_RESET, CANCEL_ERROR);
1405 0 : ResetDownstreamState();
1406 0 : return NS_OK;
1407 0 : } else if (NS_FAILED(rv)) {
1408 0 : return rv;
1409 : }
1410 :
1411 : // allow more headers in the case of 1xx
1412 0 : if (((httpResponseCode / 100) == 1) && didFirstSetAllRecvd) {
1413 0 : mInputFrameDataStream->UnsetAllHeadersReceived();
1414 : }
1415 :
1416 0 : ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
1417 0 : return NS_OK;
1418 : }
1419 :
1420 : nsresult
1421 0 : Http2Session::RecvPriority(Http2Session *self)
1422 : {
1423 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY);
1424 :
1425 0 : if (self->mInputFrameDataSize != 5) {
1426 0 : LOG3(("Http2Session::RecvPriority %p wrong length data=%d\n",
1427 : self, self->mInputFrameDataSize));
1428 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1429 : }
1430 :
1431 0 : if (!self->mInputFrameID) {
1432 0 : LOG3(("Http2Session::RecvPriority %p stream ID of 0.\n", self));
1433 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1434 : }
1435 :
1436 0 : nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
1437 0 : if (NS_FAILED(rv))
1438 0 : return rv;
1439 :
1440 : uint32_t newPriorityDependency = NetworkEndian::readUint32(
1441 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes);
1442 0 : bool exclusive = !!(newPriorityDependency & 0x80000000);
1443 0 : newPriorityDependency &= 0x7fffffff;
1444 0 : uint8_t newPriorityWeight = *(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
1445 0 : if (self->mInputFrameDataStream) {
1446 0 : self->mInputFrameDataStream->SetPriorityDependency(newPriorityDependency,
1447 : newPriorityWeight,
1448 0 : exclusive);
1449 : }
1450 :
1451 0 : self->ResetDownstreamState();
1452 0 : return NS_OK;
1453 : }
1454 :
1455 : nsresult
1456 0 : Http2Session::RecvRstStream(Http2Session *self)
1457 : {
1458 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_RST_STREAM);
1459 :
1460 0 : if (self->mInputFrameDataSize != 4) {
1461 0 : LOG3(("Http2Session::RecvRstStream %p RST_STREAM wrong length data=%d",
1462 : self, self->mInputFrameDataSize));
1463 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1464 : }
1465 :
1466 0 : if (!self->mInputFrameID) {
1467 0 : LOG3(("Http2Session::RecvRstStream %p stream ID of 0.\n", self));
1468 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1469 : }
1470 :
1471 0 : self->mDownstreamRstReason = NetworkEndian::readUint32(
1472 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes);
1473 :
1474 0 : LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n",
1475 : self, self->mDownstreamRstReason, self->mInputFrameID));
1476 :
1477 0 : DebugOnly<nsresult> rv = self->SetInputFrameDataStream(self->mInputFrameID);
1478 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1479 0 : if (!self->mInputFrameDataStream) {
1480 : // if we can't find the stream just ignore it (4.2 closed)
1481 0 : self->ResetDownstreamState();
1482 0 : return NS_OK;
1483 : }
1484 :
1485 0 : self->mInputFrameDataStream->SetRecvdReset(true);
1486 0 : self->MaybeDecrementConcurrent(self->mInputFrameDataStream);
1487 0 : self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
1488 0 : return NS_OK;
1489 : }
1490 :
1491 : nsresult
1492 0 : Http2Session::RecvSettings(Http2Session *self)
1493 : {
1494 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_SETTINGS);
1495 :
1496 0 : if (self->mInputFrameID) {
1497 0 : LOG3(("Http2Session::RecvSettings %p needs stream ID of 0. 0x%X\n",
1498 : self, self->mInputFrameID));
1499 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1500 : }
1501 :
1502 0 : if (self->mInputFrameDataSize % 6) {
1503 : // Number of Settings is determined by dividing by each 6 byte setting
1504 : // entry. So the payload must be a multiple of 6.
1505 0 : LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d",
1506 : self, self->mInputFrameDataSize));
1507 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1508 : }
1509 :
1510 0 : self->mReceivedSettings = true;
1511 :
1512 0 : uint32_t numEntries = self->mInputFrameDataSize / 6;
1513 0 : LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame "
1514 : "with %d entries ack=%X", self, numEntries,
1515 : self->mInputFrameFlags & kFlag_ACK));
1516 :
1517 0 : if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) {
1518 0 : LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n", self));
1519 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1520 : }
1521 :
1522 0 : for (uint32_t index = 0; index < numEntries; ++index) {
1523 : uint8_t *setting = reinterpret_cast<uint8_t *>
1524 0 : (self->mInputFrameBuffer.get()) + kFrameHeaderBytes + index * 6;
1525 :
1526 0 : uint16_t id = NetworkEndian::readUint16(setting);
1527 0 : uint32_t value = NetworkEndian::readUint32(setting + 2);
1528 0 : LOG3(("Settings ID %u, Value %u", id, value));
1529 :
1530 0 : switch (id)
1531 : {
1532 : case SETTINGS_TYPE_HEADER_TABLE_SIZE:
1533 0 : LOG3(("Compression header table setting received: %d\n", value));
1534 0 : self->mCompressor.SetMaxBufferSize(value);
1535 0 : break;
1536 :
1537 : case SETTINGS_TYPE_ENABLE_PUSH:
1538 0 : LOG3(("Client received an ENABLE Push SETTING. Odd.\n"));
1539 : // nop
1540 0 : break;
1541 :
1542 : case SETTINGS_TYPE_MAX_CONCURRENT:
1543 0 : self->mMaxConcurrent = value;
1544 0 : Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
1545 0 : self->ProcessPending();
1546 0 : break;
1547 :
1548 : case SETTINGS_TYPE_INITIAL_WINDOW:
1549 : {
1550 0 : Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
1551 0 : int32_t delta = value - self->mServerInitialStreamWindow;
1552 0 : self->mServerInitialStreamWindow = value;
1553 :
1554 : // SETTINGS only adjusts stream windows. Leave the session window alone.
1555 : // We need to add the delta to all open streams (delta can be negative)
1556 0 : for (auto iter = self->mStreamTransactionHash.Iter();
1557 0 : !iter.Done();
1558 0 : iter.Next()) {
1559 0 : iter.Data()->UpdateServerReceiveWindow(delta);
1560 : }
1561 : }
1562 0 : break;
1563 :
1564 : case SETTINGS_TYPE_MAX_FRAME_SIZE:
1565 : {
1566 0 : if ((value < kMaxFrameData) || (value >= 0x01000000)) {
1567 0 : LOG3(("Received invalid max frame size 0x%X", value));
1568 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1569 : }
1570 : // We stick to the default for simplicity's sake, so nothing to change
1571 : }
1572 0 : break;
1573 :
1574 : default:
1575 0 : break;
1576 : }
1577 : }
1578 :
1579 0 : self->ResetDownstreamState();
1580 :
1581 0 : if (!(self->mInputFrameFlags & kFlag_ACK)) {
1582 0 : self->GenerateSettingsAck();
1583 0 : } else if (self->mWaitingForSettingsAck) {
1584 0 : self->mGoAwayOnPush = true;
1585 : }
1586 :
1587 0 : return NS_OK;
1588 : }
1589 :
1590 : nsresult
1591 0 : Http2Session::RecvPushPromise(Http2Session *self)
1592 : {
1593 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PUSH_PROMISE ||
1594 : self->mInputFrameType == FRAME_TYPE_CONTINUATION);
1595 :
1596 : // Find out how much padding this frame has, so we can only extract the real
1597 : // header data from the frame.
1598 0 : uint16_t paddingLength = 0;
1599 0 : uint8_t paddingControlBytes = 0;
1600 :
1601 : // If this doesn't have END_PUSH_PROMISE set on it then require the next
1602 : // frame to be PUSH_PROMISE of the same ID
1603 : uint32_t promiseLen;
1604 : uint32_t promisedID;
1605 :
1606 0 : if (self->mExpectedPushPromiseID) {
1607 0 : promiseLen = 0; // really a continuation frame
1608 0 : promisedID = self->mContinuedPromiseStream;
1609 : } else {
1610 0 : self->mDecompressBuffer.Truncate();
1611 0 : nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength);
1612 0 : if (NS_FAILED(rv)) {
1613 0 : return rv;
1614 : }
1615 0 : promiseLen = 4;
1616 : promisedID = NetworkEndian::readUint32(
1617 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes + paddingControlBytes);
1618 0 : promisedID &= 0x7fffffff;
1619 0 : if (promisedID <= self->mLastPushedID) {
1620 0 : LOG3(("Http2Session::RecvPushPromise %p ID too low %u expected > %u.\n",
1621 : self, promisedID, self->mLastPushedID));
1622 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1623 : }
1624 0 : self->mLastPushedID = promisedID;
1625 : }
1626 :
1627 0 : uint32_t associatedID = self->mInputFrameID;
1628 :
1629 0 : if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
1630 0 : self->mExpectedPushPromiseID = 0;
1631 0 : self->mContinuedPromiseStream = 0;
1632 : } else {
1633 0 : self->mExpectedPushPromiseID = self->mInputFrameID;
1634 0 : self->mContinuedPromiseStream = promisedID;
1635 : }
1636 :
1637 0 : if ((paddingControlBytes + promiseLen + paddingLength) > self->mInputFrameDataSize) {
1638 : // This is fatal to the session
1639 0 : LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
1640 : "PROTOCOL_ERROR extra %d > frame size %d\n",
1641 : self, promisedID, associatedID, (paddingControlBytes + promiseLen + paddingLength),
1642 : self->mInputFrameDataSize));
1643 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1644 : }
1645 :
1646 0 : LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X "
1647 : "paddingLength %d padded %d\n",
1648 : self, promisedID, associatedID, paddingLength,
1649 : self->mInputFrameFlags & kFlag_PADDED));
1650 :
1651 0 : if (!associatedID || !promisedID || (promisedID & 1)) {
1652 0 : LOG3(("Http2Session::RecvPushPromise %p ID invalid.\n", self));
1653 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1654 : }
1655 :
1656 : // confirm associated-to
1657 0 : nsresult rv = self->SetInputFrameDataStream(associatedID);
1658 0 : if (NS_FAILED(rv))
1659 0 : return rv;
1660 :
1661 0 : Http2Stream *associatedStream = self->mInputFrameDataStream;
1662 0 : ++(self->mServerPushedResources);
1663 :
1664 : // Anytime we start using the high bit of stream ID (either client or server)
1665 : // begin to migrate to a new session.
1666 0 : if (promisedID >= kMaxStreamID)
1667 0 : self->mShouldGoAway = true;
1668 :
1669 0 : bool resetStream = true;
1670 0 : SpdyPushCache *cache = nullptr;
1671 :
1672 0 : if (self->mShouldGoAway && !Http2PushedStream::TestOnPush(associatedStream)) {
1673 0 : LOG3(("Http2Session::RecvPushPromise %p cache push while in GoAway "
1674 : "mode refused.\n", self));
1675 0 : self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
1676 0 : } else if (!gHttpHandler->AllowPush()) {
1677 : // ENABLE_PUSH and MAX_CONCURRENT_STREAMS of 0 in settings disabled push
1678 0 : LOG3(("Http2Session::RecvPushPromise Push Recevied when Disabled\n"));
1679 0 : if (self->mGoAwayOnPush) {
1680 0 : LOG3(("Http2Session::RecvPushPromise sending GOAWAY"));
1681 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1682 : }
1683 0 : self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
1684 0 : } else if (!(associatedID & 1)) {
1685 0 : LOG3(("Http2Session::RecvPushPromise %p assocated=0x%X on pushed (even) stream not allowed\n",
1686 : self, associatedID));
1687 0 : self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
1688 0 : } else if (!associatedStream) {
1689 0 : LOG3(("Http2Session::RecvPushPromise %p lookup associated ID failed.\n", self));
1690 0 : self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
1691 : } else {
1692 0 : nsIRequestContext *requestContext = associatedStream->RequestContext();
1693 0 : if (requestContext) {
1694 0 : requestContext->GetSpdyPushCache(&cache);
1695 0 : if (!cache) {
1696 0 : cache = new SpdyPushCache();
1697 0 : if (!cache || NS_FAILED(requestContext->SetSpdyPushCache(cache))) {
1698 0 : delete cache;
1699 0 : cache = nullptr;
1700 : }
1701 : }
1702 : }
1703 0 : if (!cache) {
1704 : // this is unexpected, but we can handle it just by refusing the push
1705 0 : LOG3(("Http2Session::RecvPushPromise Push Recevied without push cache\n"));
1706 0 : self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
1707 : } else {
1708 0 : resetStream = false;
1709 : }
1710 : }
1711 :
1712 0 : if (resetStream) {
1713 : // Need to decompress the headers even though we aren't using them yet in
1714 : // order to keep the compression context consistent for other frames
1715 0 : self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen],
1716 0 : self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
1717 0 : if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) {
1718 0 : rv = self->UncompressAndDiscard(true);
1719 0 : if (NS_FAILED(rv)) {
1720 0 : LOG3(("Http2Session::RecvPushPromise uncompress failed\n"));
1721 0 : self->mGoAwayReason = COMPRESSION_ERROR;
1722 0 : return rv;
1723 : }
1724 : }
1725 0 : self->ResetDownstreamState();
1726 0 : return NS_OK;
1727 : }
1728 :
1729 0 : self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen],
1730 0 : self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
1731 :
1732 0 : if (self->mInputFrameType != FRAME_TYPE_CONTINUATION) {
1733 0 : self->mAggregatedHeaderSize = self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength;
1734 : } else {
1735 0 : self->mAggregatedHeaderSize += self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength;
1736 : }
1737 :
1738 0 : if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) {
1739 0 : LOG3(("Http2Session::RecvPushPromise not finishing processing for multi-frame push\n"));
1740 0 : self->ResetDownstreamState();
1741 0 : return NS_OK;
1742 : }
1743 :
1744 0 : if (self->mInputFrameType == FRAME_TYPE_CONTINUATION) {
1745 0 : Telemetry::Accumulate(Telemetry::SPDY_CONTINUED_HEADERS, self->mAggregatedHeaderSize);
1746 : }
1747 :
1748 : // Create the buffering transaction and push stream
1749 : RefPtr<Http2PushTransactionBuffer> transactionBuffer =
1750 0 : new Http2PushTransactionBuffer();
1751 0 : transactionBuffer->SetConnection(self);
1752 : Http2PushedStream *pushedStream =
1753 0 : new Http2PushedStream(transactionBuffer, self, associatedStream, promisedID);
1754 :
1755 0 : rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
1756 : self->mDecompressBuffer,
1757 0 : pushedStream->GetRequestString());
1758 :
1759 0 : if (rv == NS_ERROR_NOT_IMPLEMENTED) {
1760 0 : LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
1761 0 : self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
1762 0 : delete pushedStream;
1763 0 : self->ResetDownstreamState();
1764 0 : return NS_OK;
1765 : }
1766 :
1767 0 : if (rv == NS_ERROR_ILLEGAL_VALUE) {
1768 : // This means the decompression completed ok, but there was a problem with
1769 : // the decoded headers. Reset the stream and go away.
1770 0 : self->GenerateRstStream(PROTOCOL_ERROR, promisedID);
1771 0 : delete pushedStream;
1772 0 : self->ResetDownstreamState();
1773 0 : return NS_OK;
1774 0 : } else if (NS_FAILED(rv)) {
1775 : // This is fatal to the session.
1776 0 : self->mGoAwayReason = COMPRESSION_ERROR;
1777 0 : return rv;
1778 : }
1779 :
1780 : // Ownership of the pushed stream is by the transaction hash, just as it
1781 : // is for a client initiated stream. Errors that aren't fatal to the
1782 : // whole session must call cleanupStream() after this point in order
1783 : // to remove the stream from that hash.
1784 0 : self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
1785 0 : self->mPushedStreams.AppendElement(pushedStream);
1786 :
1787 0 : if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) {
1788 0 : LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n"));
1789 0 : self->mGoAwayReason = INTERNAL_ERROR;
1790 0 : return NS_ERROR_FAILURE;
1791 : }
1792 :
1793 0 : if (promisedID > self->mOutgoingGoAwayID)
1794 0 : self->mOutgoingGoAwayID = promisedID;
1795 :
1796 : // Fake the request side of the pushed HTTP transaction. Sets up hash
1797 : // key and origin
1798 : uint32_t notUsed;
1799 0 : Unused << pushedStream->ReadSegments(nullptr, 1, ¬Used);
1800 :
1801 0 : nsAutoCString key;
1802 0 : if (!pushedStream->GetHashKey(key)) {
1803 0 : LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n"));
1804 0 : self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR);
1805 0 : self->ResetDownstreamState();
1806 0 : return NS_OK;
1807 : }
1808 :
1809 : // does the pushed origin belong on this connection?
1810 0 : LOG3(("Http2Session::RecvPushPromise %p origin check %s", self,
1811 : pushedStream->Origin().get()));
1812 0 : RefPtr<nsStandardURL> pushedURL;
1813 0 : rv = Http2Stream::MakeOriginURL(pushedStream->Origin(), pushedURL);
1814 0 : nsAutoCString pushedHostName;
1815 0 : int32_t pushedPort = -1;
1816 0 : if (NS_SUCCEEDED(rv)) {
1817 0 : rv = pushedURL->GetHost(pushedHostName);
1818 : }
1819 0 : if (NS_SUCCEEDED(rv)) {
1820 0 : rv = pushedURL->GetPort(&pushedPort);
1821 : }
1822 0 : if (NS_FAILED(rv) ||
1823 0 : !self->TestJoinConnection(pushedHostName, pushedPort)) {
1824 0 : LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin %s\n",
1825 : self, pushedStream->Origin().get()));
1826 0 : self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
1827 0 : self->ResetDownstreamState();
1828 0 : return NS_OK;
1829 : }
1830 :
1831 0 : if (pushedStream->TryOnPush()) {
1832 0 : LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener "
1833 : "stream %p will not be placed into session cache.\n", self, pushedStream));
1834 : } else {
1835 0 : LOG3(("Http2Session::RecvPushPromise %p place stream into session cache\n", self));
1836 0 : if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
1837 : // This only happens if they've already pushed us this item.
1838 0 : LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
1839 0 : self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
1840 0 : self->ResetDownstreamState();
1841 0 : return NS_OK;
1842 : }
1843 : }
1844 :
1845 0 : pushedStream->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE);
1846 : static_assert(Http2Stream::kWorstPriority >= 0,
1847 : "kWorstPriority out of range");
1848 : uint8_t priorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) -
1849 0 : (Http2Stream::kWorstPriority - Http2Stream::kNormalPriority);
1850 0 : pushedStream->SetPriority(Http2Stream::kWorstPriority);
1851 0 : self->GeneratePriority(promisedID, priorityWeight);
1852 0 : self->ResetDownstreamState();
1853 0 : return NS_OK;
1854 : }
1855 :
1856 : nsresult
1857 0 : Http2Session::RecvPing(Http2Session *self)
1858 : {
1859 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING);
1860 :
1861 0 : LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self,
1862 : self->mInputFrameFlags));
1863 :
1864 0 : if (self->mInputFrameDataSize != 8) {
1865 0 : LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d",
1866 : self, self->mInputFrameDataSize));
1867 0 : RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR);
1868 : }
1869 :
1870 0 : if (self->mInputFrameID) {
1871 0 : LOG3(("Http2Session::RecvPing %p PING needs stream ID of 0. 0x%X\n",
1872 : self, self->mInputFrameID));
1873 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1874 : }
1875 :
1876 0 : if (self->mInputFrameFlags & kFlag_ACK) {
1877 : // presumably a reply to our timeout ping.. don't reply to it
1878 0 : self->mPingSentEpoch = 0;
1879 : } else {
1880 : // reply with a ack'd ping
1881 0 : self->GeneratePing(true);
1882 : }
1883 :
1884 0 : self->ResetDownstreamState();
1885 0 : return NS_OK;
1886 : }
1887 :
1888 : nsresult
1889 0 : Http2Session::RecvGoAway(Http2Session *self)
1890 : {
1891 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_GOAWAY);
1892 :
1893 0 : if (self->mInputFrameDataSize < 8) {
1894 : // data > 8 is an opaque token that we can't interpret. NSPR Logs will
1895 : // have the hex of all packets so there is no point in separately logging.
1896 0 : LOG3(("Http2Session::RecvGoAway %p GOAWAY had wrong amount of data %d",
1897 : self, self->mInputFrameDataSize));
1898 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1899 : }
1900 :
1901 0 : if (self->mInputFrameID) {
1902 0 : LOG3(("Http2Session::RecvGoAway %p GOAWAY had non zero stream ID 0x%X\n",
1903 : self, self->mInputFrameID));
1904 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1905 : }
1906 :
1907 0 : self->mShouldGoAway = true;
1908 0 : self->mGoAwayID = NetworkEndian::readUint32(
1909 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes);
1910 0 : self->mGoAwayID &= 0x7fffffff;
1911 0 : self->mCleanShutdown = true;
1912 0 : self->mPeerGoAwayReason = NetworkEndian::readUint32(
1913 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes + 4);
1914 :
1915 : // Find streams greater than the last-good ID and mark them for deletion
1916 : // in the mGoAwayStreamsToRestart queue. The underlying transaction can be
1917 : // restarted.
1918 0 : for (auto iter = self->mStreamTransactionHash.Iter();
1919 0 : !iter.Done();
1920 0 : iter.Next()) {
1921 : // these streams were not processed by the server and can be restarted.
1922 : // Do that after the enumerator completes to avoid the risk of
1923 : // a restart event re-entrantly modifying this hash. Be sure not to restart
1924 : // a pushed (even numbered) stream
1925 0 : nsAutoPtr<Http2Stream>& stream = iter.Data();
1926 0 : if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
1927 0 : !stream->HasRegisteredID()) {
1928 0 : self->mGoAwayStreamsToRestart.Push(stream);
1929 : }
1930 : }
1931 :
1932 : // Process the streams marked for deletion and restart.
1933 0 : size_t size = self->mGoAwayStreamsToRestart.GetSize();
1934 0 : for (size_t count = 0; count < size; ++count) {
1935 : Http2Stream *stream =
1936 0 : static_cast<Http2Stream *>(self->mGoAwayStreamsToRestart.PopFront());
1937 :
1938 0 : if (self->mPeerGoAwayReason == HTTP_1_1_REQUIRED) {
1939 0 : stream->Transaction()->DisableSpdy();
1940 : }
1941 0 : self->CloseStream(stream, NS_ERROR_NET_RESET);
1942 0 : if (stream->HasRegisteredID())
1943 0 : self->mStreamIDHash.Remove(stream->StreamID());
1944 0 : self->mStreamTransactionHash.Remove(stream->Transaction());
1945 : }
1946 :
1947 : // Queued streams can also be deleted from this session and restarted
1948 : // in another one. (they were never sent on the network so they implicitly
1949 : // are not covered by the last-good id.
1950 0 : size = self->mQueuedStreams.GetSize();
1951 0 : for (size_t count = 0; count < size; ++count) {
1952 : Http2Stream *stream =
1953 0 : static_cast<Http2Stream *>(self->mQueuedStreams.PopFront());
1954 0 : MOZ_ASSERT(stream->Queued());
1955 0 : stream->SetQueued(false);
1956 0 : if (self->mPeerGoAwayReason == HTTP_1_1_REQUIRED) {
1957 0 : stream->Transaction()->DisableSpdy();
1958 : }
1959 0 : self->CloseStream(stream, NS_ERROR_NET_RESET);
1960 0 : self->mStreamTransactionHash.Remove(stream->Transaction());
1961 : }
1962 :
1963 0 : LOG3(("Http2Session::RecvGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
1964 : "live streams=%d\n", self, self->mGoAwayID, self->mPeerGoAwayReason,
1965 : self->mStreamTransactionHash.Count()));
1966 :
1967 0 : self->ResetDownstreamState();
1968 0 : return NS_OK;
1969 : }
1970 :
1971 : nsresult
1972 0 : Http2Session::RecvWindowUpdate(Http2Session *self)
1973 : {
1974 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_WINDOW_UPDATE);
1975 :
1976 0 : if (self->mInputFrameDataSize != 4) {
1977 0 : LOG3(("Http2Session::RecvWindowUpdate %p Window Update wrong length %d\n",
1978 : self, self->mInputFrameDataSize));
1979 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
1980 : }
1981 :
1982 : uint32_t delta = NetworkEndian::readUint32(
1983 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes);
1984 0 : delta &= 0x7fffffff;
1985 :
1986 0 : LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n",
1987 : self, delta, self->mInputFrameID));
1988 :
1989 0 : if (self->mInputFrameID) { // stream window
1990 0 : nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID);
1991 0 : if (NS_FAILED(rv))
1992 0 : return rv;
1993 :
1994 0 : if (!self->mInputFrameDataStream) {
1995 0 : LOG3(("Http2Session::RecvWindowUpdate %p lookup streamID 0x%X failed.\n",
1996 : self, self->mInputFrameID));
1997 : // only resest the session if the ID is one we haven't ever opened
1998 0 : if (self->mInputFrameID >= self->mNextStreamID)
1999 0 : self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID);
2000 0 : self->ResetDownstreamState();
2001 0 : return NS_OK;
2002 : }
2003 :
2004 0 : if (delta == 0) {
2005 0 : LOG3(("Http2Session::RecvWindowUpdate %p received 0 stream window update",
2006 : self));
2007 0 : self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
2008 0 : PROTOCOL_ERROR);
2009 0 : self->ResetDownstreamState();
2010 0 : return NS_OK;
2011 : }
2012 :
2013 0 : int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow();
2014 0 : self->mInputFrameDataStream->UpdateServerReceiveWindow(delta);
2015 0 : if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) {
2016 : // a window cannot reach 2^31 and be in compliance. Our calculations
2017 : // are 64 bit safe though.
2018 0 : LOG3(("Http2Session::RecvWindowUpdate %p stream window "
2019 : "exceeds 2^31 - 1\n", self));
2020 0 : self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
2021 0 : FLOW_CONTROL_ERROR);
2022 0 : self->ResetDownstreamState();
2023 0 : return NS_OK;
2024 : }
2025 :
2026 0 : LOG3(("Http2Session::RecvWindowUpdate %p stream 0x%X window "
2027 : "%" PRId64 " increased by %" PRIu32 " now %" PRId64 ".\n",
2028 : self, self->mInputFrameID, oldRemoteWindow, delta, oldRemoteWindow + delta));
2029 :
2030 : } else { // session window update
2031 0 : if (delta == 0) {
2032 0 : LOG3(("Http2Session::RecvWindowUpdate %p received 0 session window update",
2033 : self));
2034 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
2035 : }
2036 :
2037 0 : int64_t oldRemoteWindow = self->mServerSessionWindow;
2038 0 : self->mServerSessionWindow += delta;
2039 :
2040 0 : if (self->mServerSessionWindow >= 0x80000000) {
2041 : // a window cannot reach 2^31 and be in compliance. Our calculations
2042 : // are 64 bit safe though.
2043 0 : LOG3(("Http2Session::RecvWindowUpdate %p session window "
2044 : "exceeds 2^31 - 1\n", self));
2045 0 : RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR);
2046 : }
2047 :
2048 0 : if ((oldRemoteWindow <= 0) && (self->mServerSessionWindow > 0)) {
2049 0 : LOG3(("Http2Session::RecvWindowUpdate %p restart session window\n",
2050 : self));
2051 0 : for (auto iter = self->mStreamTransactionHash.Iter();
2052 0 : !iter.Done();
2053 0 : iter.Next()) {
2054 0 : MOZ_ASSERT(self->mServerSessionWindow > 0);
2055 :
2056 0 : nsAutoPtr<Http2Stream>& stream = iter.Data();
2057 0 : if (!stream->BlockedOnRwin() || stream->ServerReceiveWindow() <= 0) {
2058 0 : continue;
2059 : }
2060 :
2061 0 : self->mReadyForWrite.Push(stream);
2062 0 : self->SetWriteCallbacks();
2063 : }
2064 : }
2065 0 : LOG3(("Http2Session::RecvWindowUpdate %p session window "
2066 : "%" PRId64 " increased by %d now %" PRId64 ".\n", self,
2067 : oldRemoteWindow, delta, oldRemoteWindow + delta));
2068 : }
2069 :
2070 0 : self->ResetDownstreamState();
2071 0 : return NS_OK;
2072 : }
2073 :
2074 : nsresult
2075 0 : Http2Session::RecvContinuation(Http2Session *self)
2076 : {
2077 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_CONTINUATION);
2078 0 : MOZ_ASSERT(self->mInputFrameID);
2079 0 : MOZ_ASSERT(self->mExpectedPushPromiseID || self->mExpectedHeaderID);
2080 0 : MOZ_ASSERT(!(self->mExpectedPushPromiseID && self->mExpectedHeaderID));
2081 :
2082 0 : LOG3(("Http2Session::RecvContinuation %p Flags 0x%X id 0x%X "
2083 : "promise id 0x%X header id 0x%X\n",
2084 : self, self->mInputFrameFlags, self->mInputFrameID,
2085 : self->mExpectedPushPromiseID, self->mExpectedHeaderID));
2086 :
2087 0 : DebugOnly<nsresult> rv = self->SetInputFrameDataStream(self->mInputFrameID);
2088 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2089 :
2090 0 : if (!self->mInputFrameDataStream) {
2091 0 : LOG3(("Http2Session::RecvContination stream ID 0x%X not found.",
2092 : self->mInputFrameID));
2093 0 : RETURN_SESSION_ERROR(self, PROTOCOL_ERROR);
2094 : }
2095 :
2096 : // continued headers
2097 0 : if (self->mExpectedHeaderID) {
2098 0 : self->mInputFrameFlags &= ~kFlag_PRIORITY;
2099 0 : return RecvHeaders(self);
2100 : }
2101 :
2102 : // continued push promise
2103 0 : if (self->mInputFrameFlags & kFlag_END_HEADERS) {
2104 0 : self->mInputFrameFlags &= ~kFlag_END_HEADERS;
2105 0 : self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE;
2106 : }
2107 0 : return RecvPushPromise(self);
2108 : }
2109 :
2110 0 : class UpdateAltSvcEvent : public Runnable
2111 : {
2112 : public:
2113 0 : UpdateAltSvcEvent(const nsCString& header,
2114 : const nsCString& aOrigin,
2115 : nsHttpConnectionInfo* aCI,
2116 : nsIInterfaceRequestor* callbacks)
2117 0 : : Runnable("net::UpdateAltSvcEvent")
2118 : , mHeader(header)
2119 : , mOrigin(aOrigin)
2120 : , mCI(aCI)
2121 0 : , mCallbacks(callbacks)
2122 : {
2123 0 : }
2124 :
2125 0 : NS_IMETHOD Run() override
2126 : {
2127 0 : MOZ_ASSERT(NS_IsMainThread());
2128 :
2129 0 : nsCString originScheme;
2130 0 : nsCString originHost;
2131 0 : int32_t originPort = -1;
2132 :
2133 0 : nsCOMPtr<nsIURI> uri;
2134 0 : if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mOrigin))) {
2135 0 : LOG(("UpdateAltSvcEvent origin does not parse %s\n",
2136 : mOrigin.get()));
2137 0 : return NS_OK;
2138 : }
2139 0 : uri->GetScheme(originScheme);
2140 0 : uri->GetHost(originHost);
2141 0 : uri->GetPort(&originPort);
2142 :
2143 0 : AltSvcMapping::ProcessHeader(mHeader, originScheme, originHost, originPort,
2144 0 : mCI->GetUsername(), mCI->GetPrivate(), mCallbacks,
2145 0 : mCI->ProxyInfo(), 0, mCI->GetOriginAttributes());
2146 0 : return NS_OK;
2147 : }
2148 :
2149 : private:
2150 : nsCString mHeader;
2151 : nsCString mOrigin;
2152 : RefPtr<nsHttpConnectionInfo> mCI;
2153 : nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
2154 : };
2155 :
2156 : // defined as an http2 extension - alt-svc
2157 : // defines receipt of frame type 0x0A.. See AlternateSevices.h at least draft -06 sec 4
2158 : // as this is an extension, never generate protocol error - just ignore problems
2159 : nsresult
2160 0 : Http2Session::RecvAltSvc(Http2Session *self)
2161 : {
2162 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ALTSVC);
2163 0 : LOG3(("Http2Session::RecvAltSvc %p Flags 0x%X id 0x%X\n", self,
2164 : self->mInputFrameFlags, self->mInputFrameID));
2165 :
2166 0 : if (self->mInputFrameDataSize < 2) {
2167 0 : LOG3(("Http2Session::RecvAltSvc %p frame too small", self));
2168 0 : self->ResetDownstreamState();
2169 0 : return NS_OK;
2170 : }
2171 :
2172 : uint16_t originLen = NetworkEndian::readUint16(
2173 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes);
2174 0 : if (originLen + 2U > self->mInputFrameDataSize) {
2175 0 : LOG3(("Http2Session::RecvAltSvc %p origin len too big for frame", self));
2176 0 : self->ResetDownstreamState();
2177 0 : return NS_OK;
2178 : }
2179 :
2180 0 : if (!gHttpHandler->AllowAltSvc()) {
2181 0 : LOG3(("Http2Session::RecvAltSvc %p frame alt service pref'd off", self));
2182 0 : self->ResetDownstreamState();
2183 0 : return NS_OK;
2184 : }
2185 :
2186 0 : uint16_t altSvcFieldValueLen = static_cast<uint16_t>(self->mInputFrameDataSize) - 2U - originLen;
2187 0 : LOG3(("Http2Session::RecvAltSvc %p frame originLen=%u altSvcFieldValueLen=%u\n",
2188 : self, originLen, altSvcFieldValueLen));
2189 :
2190 0 : if (self->mInputFrameDataSize > 2000) {
2191 0 : LOG3(("Http2Session::RecvAltSvc %p frame too large to parse sensibly", self));
2192 0 : self->ResetDownstreamState();
2193 0 : return NS_OK;
2194 : }
2195 :
2196 0 : nsAutoCString origin;
2197 0 : bool impliedOrigin = true;
2198 0 : if (originLen) {
2199 0 : origin.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 2, originLen);
2200 0 : impliedOrigin = false;
2201 : }
2202 :
2203 0 : nsAutoCString altSvcFieldValue;
2204 0 : if (altSvcFieldValueLen) {
2205 0 : altSvcFieldValue.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + 2 + originLen,
2206 0 : altSvcFieldValueLen);
2207 : }
2208 :
2209 0 : if (altSvcFieldValue.IsEmpty() || !nsHttp::IsReasonableHeaderValue(altSvcFieldValue)) {
2210 0 : LOG(("Http2Session %p Alt-Svc Response Header seems unreasonable - skipping\n", self));
2211 0 : self->ResetDownstreamState();
2212 0 : return NS_OK;
2213 : }
2214 :
2215 0 : if (self->mInputFrameID & 1) {
2216 : // pulled streams apply to the origin of the pulled stream.
2217 : // If the origin field is filled in the frame, the frame should be ignored
2218 0 : if (!origin.IsEmpty()) {
2219 0 : LOG(("Http2Session %p Alt-Svc pulled stream has non empty origin\n", self));
2220 0 : self->ResetDownstreamState();
2221 0 : return NS_OK;
2222 : }
2223 :
2224 0 : if (NS_FAILED(self->SetInputFrameDataStream(self->mInputFrameID)) ||
2225 0 : !self->mInputFrameDataStream->Transaction() ||
2226 0 : !self->mInputFrameDataStream->Transaction()->RequestHead()) {
2227 0 : LOG3(("Http2Session::RecvAltSvc %p got frame w/o origin on invalid stream", self));
2228 0 : self->ResetDownstreamState();
2229 0 : return NS_OK;
2230 : }
2231 :
2232 0 : self->mInputFrameDataStream->Transaction()->RequestHead()->Origin(origin);
2233 0 : } else if (!self->mInputFrameID) {
2234 : // ID 0 streams must supply their own origin
2235 0 : if (origin.IsEmpty()) {
2236 0 : LOG(("Http2Session %p Alt-Svc Stream 0 has empty origin\n", self));
2237 0 : self->ResetDownstreamState();
2238 0 : return NS_OK;
2239 : }
2240 : } else {
2241 : // handling of push streams is not defined. Let's ignore it
2242 0 : LOG(("Http2Session %p Alt-Svc received on pushed stream - ignoring\n", self));
2243 0 : self->ResetDownstreamState();
2244 0 : return NS_OK;
2245 : }
2246 :
2247 0 : RefPtr<nsHttpConnectionInfo> ci(self->ConnectionInfo());
2248 0 : if (!self->mConnection || !ci) {
2249 0 : LOG3(("Http2Session::RecvAltSvc %p no connection or conninfo for %d", self,
2250 : self->mInputFrameID));
2251 0 : self->ResetDownstreamState();
2252 0 : return NS_OK;
2253 : }
2254 :
2255 0 : if (!impliedOrigin) {
2256 0 : bool okToReroute = true;
2257 0 : nsCOMPtr<nsISupports> securityInfo;
2258 0 : self->mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
2259 0 : nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
2260 0 : if (!ssl) {
2261 0 : okToReroute = false;
2262 : }
2263 :
2264 : // a little off main thread origin parser. This is a non critical function because
2265 : // any alternate route created has to be verified anyhow
2266 0 : nsAutoCString specifiedOriginHost;
2267 0 : if (origin.EqualsIgnoreCase("https://", 8)) {
2268 0 : specifiedOriginHost.Assign(origin.get() + 8, origin.Length() - 8);
2269 0 : } else if (origin.EqualsIgnoreCase("http://", 7)) {
2270 0 : specifiedOriginHost.Assign(origin.get() + 7, origin.Length() - 7);
2271 : }
2272 :
2273 0 : int32_t colonOffset = specifiedOriginHost.FindCharInSet(":", 0);
2274 0 : if (colonOffset != kNotFound) {
2275 0 : specifiedOriginHost.Truncate(colonOffset);
2276 : }
2277 :
2278 0 : if (okToReroute) {
2279 0 : ssl->IsAcceptableForHost(specifiedOriginHost, &okToReroute);
2280 : }
2281 :
2282 0 : if (!okToReroute) {
2283 0 : LOG3(("Http2Session::RecvAltSvc %p can't reroute non-authoritative origin %s",
2284 : self, origin.BeginReading()));
2285 0 : self->ResetDownstreamState();
2286 0 : return NS_OK;
2287 : }
2288 : }
2289 :
2290 0 : nsCOMPtr<nsISupports> callbacks;
2291 0 : self->mConnection->GetSecurityInfo(getter_AddRefs(callbacks));
2292 0 : nsCOMPtr<nsIInterfaceRequestor> irCallbacks = do_QueryInterface(callbacks);
2293 :
2294 : RefPtr<UpdateAltSvcEvent> event =
2295 0 : new UpdateAltSvcEvent(altSvcFieldValue, origin, ci, irCallbacks);
2296 0 : NS_DispatchToMainThread(event);
2297 0 : self->ResetDownstreamState();
2298 0 : return NS_OK;
2299 : }
2300 :
2301 : void
2302 0 : Http2Session::Received421(nsHttpConnectionInfo *ci)
2303 : {
2304 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2305 0 : LOG3(("Http2Session::Recevied421 %p %d\n", this, mOriginFrameActivated));
2306 0 : if (!mOriginFrameActivated || !ci) {
2307 0 : return;
2308 : }
2309 :
2310 0 : nsAutoCString key(ci->GetOrigin());
2311 0 : key.Append(':');
2312 0 : key.AppendInt(ci->OriginPort());
2313 0 : mOriginFrame.Remove(key);
2314 0 : LOG3(("Http2Session::Received421 %p key %s removed\n", this, key.get()));
2315 : }
2316 :
2317 : nsresult
2318 0 : Http2Session::RecvUnused(Http2Session *self)
2319 : {
2320 0 : LOG3(("Http2Session %p unknown frame type %x ignored\n",
2321 : self, self->mInputFrameType));
2322 0 : self->ResetDownstreamState();
2323 0 : return NS_OK;
2324 : }
2325 :
2326 : // defined as an http2 extension - origin
2327 : // defines receipt of frame type 0x0b.. http://httpwg.org/http-extensions/origin-frame.html
2328 : // as this is an extension, never generate protocol error - just ignore problems
2329 : nsresult
2330 0 : Http2Session::RecvOrigin(Http2Session *self)
2331 : {
2332 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2333 0 : MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_ORIGIN);
2334 0 : LOG3(("Http2Session::RecvOrigin %p Flags 0x%X id 0x%X\n", self,
2335 : self->mInputFrameFlags, self->mInputFrameID));
2336 :
2337 0 : if (self->mInputFrameFlags & 0x0F) {
2338 0 : LOG3(("Http2Session::RecvOrigin %p leading flags must be 0", self));
2339 0 : self->ResetDownstreamState();
2340 0 : return NS_OK;
2341 : }
2342 :
2343 0 : if (self->mInputFrameID) {
2344 0 : LOG3(("Http2Session::RecvOrigin %p not stream 0", self));
2345 0 : self->ResetDownstreamState();
2346 0 : return NS_OK;
2347 : }
2348 :
2349 0 : if (self->ConnectionInfo()->UsingProxy()) {
2350 0 : LOG3(("Http2Session::RecvOrigin %p must not use proxy", self));
2351 0 : self->ResetDownstreamState();
2352 0 : return NS_OK;
2353 : }
2354 :
2355 0 : if (!gHttpHandler->AllowOriginExtension()) {
2356 0 : LOG3(("Http2Session::RecvOrigin %p origin extension pref'd off", self));
2357 0 : self->ResetDownstreamState();
2358 0 : return NS_OK;
2359 : }
2360 :
2361 0 : uint32_t offset = 0;
2362 0 : self->mOriginFrameActivated = true;
2363 :
2364 0 : while (self->mInputFrameDataSize >= (offset + 2U)) {
2365 :
2366 : uint16_t originLen = NetworkEndian::readUint16(
2367 0 : self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset);
2368 0 : LOG3(("Http2Session::RecvOrigin %p origin extension defined as %d bytes\n", self, originLen));
2369 0 : if (originLen + 2U + offset > self->mInputFrameDataSize) {
2370 0 : LOG3(("Http2Session::RecvOrigin %p origin len too big for frame", self));
2371 0 : break;
2372 : }
2373 :
2374 0 : nsAutoCString originString;
2375 0 : RefPtr<nsStandardURL> originURL;
2376 0 : originString.Assign(self->mInputFrameBuffer.get() + kFrameHeaderBytes + offset + 2, originLen);
2377 0 : offset += originLen + 2;
2378 0 : if (NS_FAILED(Http2Stream::MakeOriginURL(originString, originURL))){
2379 0 : LOG3(("Http2Session::RecvOrigin %p origin frame string %s failed to parse\n", self, originString.get()));
2380 0 : continue;
2381 : }
2382 :
2383 0 : LOG3(("Http2Session::RecvOrigin %p origin frame string %s parsed OK\n", self, originString.get()));
2384 0 : bool isHttps = false;
2385 0 : if (NS_FAILED(originURL->SchemeIs("https", &isHttps)) || !isHttps) {
2386 0 : LOG3(("Http2Session::RecvOrigin %p origin frame not https\n", self));
2387 0 : continue;
2388 : }
2389 :
2390 0 : int32_t port = -1;
2391 0 : originURL->GetPort(&port);
2392 0 : if (port == -1) {
2393 0 : port = 443;
2394 : }
2395 : // dont use ->GetHostPort because we want explicit 443
2396 0 : nsAutoCString host;
2397 0 : originURL->GetHost(host);
2398 0 : nsAutoCString key(host);
2399 0 : key.Append(':');
2400 0 : key.AppendInt(port);
2401 0 : if (!self->mOriginFrame.Get(key)) {
2402 0 : self->mOriginFrame.Put(key, true);
2403 0 : RefPtr<nsHttpConnection> conn(self->HttpConnection());
2404 0 : MOZ_ASSERT(conn.get());
2405 0 : gHttpHandler->ConnMgr()->RegisterOriginCoalescingKey(conn, host, port);
2406 : } else {
2407 0 : LOG3(("Http2Session::RecvOrigin %p origin frame already in set\n", self));
2408 : }
2409 : }
2410 :
2411 0 : self->ResetDownstreamState();
2412 0 : return NS_OK;
2413 : }
2414 :
2415 : //-----------------------------------------------------------------------------
2416 : // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
2417 : // of these methods
2418 : //-----------------------------------------------------------------------------
2419 :
2420 : void
2421 0 : Http2Session::OnTransportStatus(nsITransport* aTransport,
2422 : nsresult aStatus, int64_t aProgress)
2423 : {
2424 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2425 :
2426 0 : switch (aStatus) {
2427 : // These should appear only once, deliver to the first
2428 : // transaction on the session.
2429 : case NS_NET_STATUS_RESOLVING_HOST:
2430 : case NS_NET_STATUS_RESOLVED_HOST:
2431 : case NS_NET_STATUS_CONNECTING_TO:
2432 : case NS_NET_STATUS_CONNECTED_TO:
2433 : case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
2434 : case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
2435 : {
2436 0 : Http2Stream *target = mStreamIDHash.Get(mUseH2Deps ? 0xF : 0x3);
2437 0 : nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
2438 0 : if (transaction)
2439 0 : transaction->OnTransportStatus(aTransport, aStatus, aProgress);
2440 0 : break;
2441 : }
2442 :
2443 : default:
2444 : // The other transport events are ignored here because there is no good
2445 : // way to map them to the right transaction in http/2. Instead, the events
2446 : // are generated again from the http/2 code and passed directly to the
2447 : // correct transaction.
2448 :
2449 : // NS_NET_STATUS_SENDING_TO:
2450 : // This is generated by the socket transport when (part) of
2451 : // a transaction is written out
2452 : //
2453 : // There is no good way to map it to the right transaction in http/2,
2454 : // so it is ignored here and generated separately when the request
2455 : // is sent from Http2Stream::TransmitFrame
2456 :
2457 : // NS_NET_STATUS_WAITING_FOR:
2458 : // Created by nsHttpConnection when the request has been totally sent.
2459 : // There is no good way to map it to the right transaction in http/2,
2460 : // so it is ignored here and generated separately when the same
2461 : // condition is complete in Http2Stream when there is no more
2462 : // request body left to be transmitted.
2463 :
2464 : // NS_NET_STATUS_RECEIVING_FROM
2465 : // Generated in session whenever we read a data frame or a HEADERS
2466 : // that can be attributed to a particular stream/transaction
2467 :
2468 0 : break;
2469 : }
2470 0 : }
2471 :
2472 : // ReadSegments() is used to write data to the network. Generally, HTTP
2473 : // request data is pulled from the approriate transaction and
2474 : // converted to http/2 data. Sometimes control data like window-update are
2475 : // generated instead.
2476 :
2477 : nsresult
2478 0 : Http2Session::ReadSegmentsAgain(nsAHttpSegmentReader *reader,
2479 : uint32_t count, uint32_t *countRead, bool *again)
2480 : {
2481 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2482 :
2483 0 : MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
2484 : "Inconsistent Write Function Callback");
2485 :
2486 0 : nsresult rv = ConfirmTLSProfile();
2487 0 : if (NS_FAILED(rv)) {
2488 0 : if (mGoAwayReason == INADEQUATE_SECURITY) {
2489 0 : LOG3(("Http2Session::ReadSegments %p returning INADEQUATE_SECURITY %" PRIx32,
2490 : this, static_cast<uint32_t>(NS_ERROR_NET_INADEQUATE_SECURITY)));
2491 0 : rv = NS_ERROR_NET_INADEQUATE_SECURITY;
2492 : }
2493 0 : return rv;
2494 : }
2495 :
2496 0 : if (reader)
2497 0 : mSegmentReader = reader;
2498 :
2499 0 : *countRead = 0;
2500 :
2501 0 : LOG3(("Http2Session::ReadSegments %p", this));
2502 :
2503 0 : Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront());
2504 0 : if (!stream) {
2505 0 : LOG3(("Http2Session %p could not identify a stream to write; suspending.",
2506 : this));
2507 0 : uint32_t availBeforeFlush = mOutputQueueUsed - mOutputQueueSent;
2508 0 : FlushOutputQueue();
2509 0 : uint32_t availAfterFlush = mOutputQueueUsed - mOutputQueueSent;
2510 0 : if (availBeforeFlush != availAfterFlush) {
2511 0 : LOG3(("Http2Session %p ResumeRecv After early flush in ReadSegments", this));
2512 0 : Unused << ResumeRecv();
2513 : }
2514 0 : SetWriteCallbacks();
2515 0 : if (mAttemptingEarlyData) {
2516 : // We can still try to send our preamble as early-data
2517 0 : *countRead = mOutputQueueUsed - mOutputQueueSent;
2518 : }
2519 0 : return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
2520 : }
2521 :
2522 0 : uint32_t earlyDataUsed = 0;
2523 0 : if (mAttemptingEarlyData) {
2524 0 : if (!stream->Do0RTT()) {
2525 0 : LOG3(("Http2Session %p will not get early data from Http2Stream %p 0x%X",
2526 : this, stream, stream->StreamID()));
2527 0 : FlushOutputQueue();
2528 0 : SetWriteCallbacks();
2529 : // We can still send our preamble
2530 0 : *countRead = mOutputQueueUsed - mOutputQueueSent;
2531 0 : return *countRead ? NS_OK : NS_BASE_STREAM_WOULD_BLOCK;
2532 : }
2533 :
2534 : // Need to adjust this to only take as much as we can fit in with the
2535 : // preamble/settings/priority stuff
2536 0 : count -= (mOutputQueueUsed - mOutputQueueSent);
2537 :
2538 : // Keep track of this to add it into countRead later, as
2539 : // stream->ReadSegments will likely change the value of mOutputQueueUsed.
2540 0 : earlyDataUsed = mOutputQueueUsed - mOutputQueueSent;
2541 : }
2542 :
2543 0 : LOG3(("Http2Session %p will write from Http2Stream %p 0x%X "
2544 : "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
2545 : stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
2546 :
2547 0 : rv = stream->ReadSegments(this, count, countRead);
2548 :
2549 0 : if (earlyDataUsed) {
2550 : // Do this here because countRead could get reset somewhere down the rabbit
2551 : // hole of stream->ReadSegments, and we want to make sure we return the
2552 : // proper value to our caller.
2553 0 : *countRead += earlyDataUsed;
2554 : }
2555 :
2556 0 : if (mAttemptingEarlyData && !m0RTTStreams.Contains(stream)) {
2557 0 : LOG3(("Http2Session::ReadSegmentsAgain adding stream %d to m0RTTStreams\n",
2558 : stream->StreamID()));
2559 0 : m0RTTStreams.AppendElement(stream);
2560 : }
2561 :
2562 : // Not every permutation of stream->ReadSegents produces data (and therefore
2563 : // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
2564 : // of that. But we might still have old data buffered that would be good
2565 : // to flush.
2566 0 : FlushOutputQueue();
2567 :
2568 : // Allow new server reads - that might be data or control information
2569 : // (e.g. window updates or http replies) that are responses to these writes
2570 0 : Unused << ResumeRecv();
2571 :
2572 0 : if (stream->RequestBlockedOnRead()) {
2573 :
2574 : // We are blocked waiting for input - either more http headers or
2575 : // any request body data. When more data from the request stream
2576 : // becomes available the httptransaction will call conn->ResumeSend().
2577 :
2578 0 : LOG3(("Http2Session::ReadSegments %p dealing with block on read", this));
2579 :
2580 : // call readsegments again if there are other streams ready
2581 : // to run in this session
2582 0 : if (GetWriteQueueSize()) {
2583 0 : rv = NS_OK;
2584 : } else {
2585 0 : rv = NS_BASE_STREAM_WOULD_BLOCK;
2586 : }
2587 0 : SetWriteCallbacks();
2588 0 : return rv;
2589 : }
2590 :
2591 0 : if (NS_FAILED(rv)) {
2592 0 : LOG3(("Http2Session::ReadSegments %p may return FAIL code %" PRIX32,
2593 : this, static_cast<uint32_t>(rv)));
2594 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2595 0 : return rv;
2596 : }
2597 :
2598 0 : CleanupStream(stream, rv, CANCEL_ERROR);
2599 0 : if (SoftStreamError(rv)) {
2600 0 : LOG3(("Http2Session::ReadSegments %p soft error override\n", this));
2601 0 : *again = false;
2602 0 : SetWriteCallbacks();
2603 0 : rv = NS_OK;
2604 : }
2605 0 : return rv;
2606 : }
2607 :
2608 0 : if (*countRead > 0) {
2609 0 : LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d",
2610 : this, stream, *countRead));
2611 0 : mReadyForWrite.Push(stream);
2612 0 : SetWriteCallbacks();
2613 0 : return rv;
2614 : }
2615 :
2616 0 : if (stream->BlockedOnRwin()) {
2617 0 : LOG3(("Http2Session %p will stream %p 0x%X suspended for flow control\n",
2618 : this, stream, stream->StreamID()));
2619 0 : return NS_BASE_STREAM_WOULD_BLOCK;
2620 : }
2621 :
2622 0 : LOG3(("Http2Session::ReadSegments %p stream=%p stream send complete",
2623 : this, stream));
2624 :
2625 : // call readsegments again if there are other streams ready
2626 : // to go in this session
2627 0 : SetWriteCallbacks();
2628 :
2629 0 : return rv;
2630 : }
2631 :
2632 : nsresult
2633 0 : Http2Session::ReadSegments(nsAHttpSegmentReader *reader,
2634 : uint32_t count, uint32_t *countRead)
2635 : {
2636 0 : bool again = false;
2637 0 : return ReadSegmentsAgain(reader, count, countRead, &again);
2638 : }
2639 :
2640 : nsresult
2641 0 : Http2Session::ReadyToProcessDataFrame(enum internalStateType newState)
2642 : {
2643 0 : MOZ_ASSERT(newState == PROCESSING_DATA_FRAME ||
2644 : newState == DISCARDING_DATA_FRAME_PADDING);
2645 0 : ChangeDownstreamState(newState);
2646 :
2647 0 : Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
2648 0 : mInputFrameDataSize >> 10);
2649 0 : mLastDataReadEpoch = mLastReadEpoch;
2650 :
2651 0 : if (!mInputFrameID) {
2652 0 : LOG3(("Http2Session::ReadyToProcessDataFrame %p data frame stream 0\n",
2653 : this));
2654 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2655 : }
2656 :
2657 0 : nsresult rv = SetInputFrameDataStream(mInputFrameID);
2658 0 : if (NS_FAILED(rv)) {
2659 0 : LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
2660 : "failed. probably due to verification.\n", this, mInputFrameID));
2661 0 : return rv;
2662 : }
2663 0 : if (!mInputFrameDataStream) {
2664 0 : LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X "
2665 : "failed. Next = 0x%X", this, mInputFrameID, mNextStreamID));
2666 0 : if (mInputFrameID >= mNextStreamID)
2667 0 : GenerateRstStream(PROTOCOL_ERROR, mInputFrameID);
2668 0 : ChangeDownstreamState(DISCARDING_DATA_FRAME);
2669 0 : } else if (mInputFrameDataStream->RecvdFin() ||
2670 0 : mInputFrameDataStream->RecvdReset() ||
2671 0 : mInputFrameDataStream->SentReset()) {
2672 0 : LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X "
2673 : "Data arrived for already server closed stream.\n",
2674 : this, mInputFrameID));
2675 0 : if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset())
2676 0 : GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID);
2677 0 : ChangeDownstreamState(DISCARDING_DATA_FRAME);
2678 0 : } else if (mInputFrameDataSize == 0 && !mInputFrameFinal) {
2679 : // Only if non-final because the stream properly handles final frames of any
2680 : // size, and we want the stream to be able to notice its own end flag.
2681 0 : LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X "
2682 : "Ignoring 0-length non-terminal data frame.", this, mInputFrameID));
2683 0 : ChangeDownstreamState(DISCARDING_DATA_FRAME);
2684 : }
2685 :
2686 0 : LOG3(("Start Processing Data Frame. "
2687 : "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
2688 : this, mInputFrameID, mInputFrameDataStream, mInputFrameFinal,
2689 : mInputFrameDataSize));
2690 0 : UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
2691 :
2692 0 : if (mInputFrameDataStream) {
2693 0 : mInputFrameDataStream->SetRecvdData(true);
2694 : }
2695 :
2696 0 : return NS_OK;
2697 : }
2698 :
2699 : // WriteSegments() is used to read data off the socket. Generally this is
2700 : // just the http2 frame header and from there the appropriate *Stream
2701 : // is identified from the Stream-ID. The http transaction associated with
2702 : // that read then pulls in the data directly, which it will feed to
2703 : // OnWriteSegment(). That function will gateway it into http and feed
2704 : // it to the appropriate transaction.
2705 :
2706 : // we call writer->OnWriteSegment via NetworkRead() to get a http2 header..
2707 : // and decide if it is data or control.. if it is control, just deal with it.
2708 : // if it is data, identify the stream
2709 : // call stream->WriteSegments which can call this::OnWriteSegment to get the
2710 : // data. It always gets full frames if they are part of the stream
2711 :
2712 : nsresult
2713 0 : Http2Session::WriteSegmentsAgain(nsAHttpSegmentWriter *writer,
2714 : uint32_t count, uint32_t *countWritten,
2715 : bool *again)
2716 : {
2717 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2718 :
2719 0 : LOG3(("Http2Session::WriteSegments %p InternalState %X\n",
2720 : this, mDownstreamState));
2721 :
2722 0 : *countWritten = 0;
2723 :
2724 0 : if (mClosed)
2725 0 : return NS_ERROR_FAILURE;
2726 :
2727 0 : nsresult rv = ConfirmTLSProfile();
2728 0 : if (NS_FAILED(rv))
2729 0 : return rv;
2730 :
2731 0 : SetWriteCallbacks();
2732 :
2733 : // If there are http transactions attached to a push stream with filled buffers
2734 : // trigger that data pump here. This only reads from buffers (not the network)
2735 : // so mDownstreamState doesn't matter.
2736 : Http2Stream *pushConnectedStream =
2737 0 : static_cast<Http2Stream *>(mPushesReadyForRead.PopFront());
2738 0 : if (pushConnectedStream) {
2739 0 : return ProcessConnectedPush(pushConnectedStream, writer, count, countWritten);
2740 : }
2741 :
2742 : // feed gecko channels that previously stopped consuming data
2743 : // only take data from stored buffers
2744 : Http2Stream *slowConsumer =
2745 0 : static_cast<Http2Stream *>(mSlowConsumersReadyForRead.PopFront());
2746 0 : if (slowConsumer) {
2747 0 : internalStateType savedState = mDownstreamState;
2748 0 : mDownstreamState = NOT_USING_NETWORK;
2749 0 : rv = ProcessSlowConsumer(slowConsumer, writer, count, countWritten);
2750 0 : mDownstreamState = savedState;
2751 0 : return rv;
2752 : }
2753 :
2754 : // The BUFFERING_OPENING_SETTINGS state is just like any BUFFERING_FRAME_HEADER
2755 : // except the only frame type it will allow is SETTINGS
2756 :
2757 : // The session layer buffers the leading 8 byte header of every frame.
2758 : // Non-Data frames are then buffered for their full length, but data
2759 : // frames (type 0) are passed through to the http stack unprocessed
2760 :
2761 0 : if (mDownstreamState == BUFFERING_OPENING_SETTINGS ||
2762 0 : mDownstreamState == BUFFERING_FRAME_HEADER) {
2763 : // The first 9 bytes of every frame is header information that
2764 : // we are going to want to strip before passing to http. That is
2765 : // true of both control and data packets.
2766 :
2767 0 : MOZ_ASSERT(mInputFrameBufferUsed < kFrameHeaderBytes,
2768 : "Frame Buffer Used Too Large for State");
2769 :
2770 0 : rv = NetworkRead(writer, &mInputFrameBuffer[mInputFrameBufferUsed],
2771 0 : kFrameHeaderBytes - mInputFrameBufferUsed, countWritten);
2772 :
2773 0 : if (NS_FAILED(rv)) {
2774 0 : LOG3(("Http2Session %p buffering frame header read failure %" PRIx32 "\n",
2775 : this, static_cast<uint32_t>(rv)));
2776 : // maybe just blocked reading from network
2777 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
2778 0 : rv = NS_OK;
2779 0 : return rv;
2780 : }
2781 :
2782 0 : LogIO(this, nullptr, "Reading Frame Header",
2783 0 : &mInputFrameBuffer[mInputFrameBufferUsed], *countWritten);
2784 :
2785 0 : mInputFrameBufferUsed += *countWritten;
2786 :
2787 0 : if (mInputFrameBufferUsed < kFrameHeaderBytes)
2788 : {
2789 0 : LOG3(("Http2Session::WriteSegments %p "
2790 : "BUFFERING FRAME HEADER incomplete size=%d",
2791 : this, mInputFrameBufferUsed));
2792 0 : return rv;
2793 : }
2794 :
2795 : // 3 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID
2796 0 : uint8_t totallyWastedByte = mInputFrameBuffer.get()[0];
2797 0 : mInputFrameDataSize = NetworkEndian::readUint16(
2798 0 : mInputFrameBuffer.get() + 1);
2799 0 : if (totallyWastedByte || (mInputFrameDataSize > kMaxFrameData)) {
2800 0 : LOG3(("Got frame too large 0x%02X%04X", totallyWastedByte, mInputFrameDataSize));
2801 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2802 : }
2803 0 : mInputFrameType = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes);
2804 0 : mInputFrameFlags = *reinterpret_cast<uint8_t *>(mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes);
2805 0 : mInputFrameID = NetworkEndian::readUint32(
2806 0 : mInputFrameBuffer.get() + kFrameLengthBytes + kFrameTypeBytes + kFrameFlagBytes);
2807 0 : mInputFrameID &= 0x7fffffff;
2808 0 : mInputFrameDataRead = 0;
2809 :
2810 0 : if (mInputFrameType == FRAME_TYPE_DATA || mInputFrameType == FRAME_TYPE_HEADERS) {
2811 0 : mInputFrameFinal = mInputFrameFlags & kFlag_END_STREAM;
2812 : } else {
2813 0 : mInputFrameFinal = 0;
2814 : }
2815 :
2816 0 : mPaddingLength = 0;
2817 :
2818 0 : LOG3(("Http2Session::WriteSegments[%p::%" PRIu64 "] Frame Header Read "
2819 : "type %X data len %u flags %x id 0x%X",
2820 : this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags,
2821 : mInputFrameID));
2822 :
2823 : // if mExpectedHeaderID is non 0, it means this frame must be a CONTINUATION of
2824 : // a HEADERS frame with a matching ID (section 6.2)
2825 0 : if (mExpectedHeaderID &&
2826 0 : ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
2827 0 : (mExpectedHeaderID != mInputFrameID))) {
2828 0 : LOG3(("Expected CONINUATION OF HEADERS for ID 0x%X\n", mExpectedHeaderID));
2829 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2830 : }
2831 :
2832 : // if mExpectedPushPromiseID is non 0, it means this frame must be a
2833 : // CONTINUATION of a PUSH_PROMISE with a matching ID (section 6.2)
2834 0 : if (mExpectedPushPromiseID &&
2835 0 : ((mInputFrameType != FRAME_TYPE_CONTINUATION) ||
2836 0 : (mExpectedPushPromiseID != mInputFrameID))) {
2837 0 : LOG3(("Expected CONTINUATION of PUSH PROMISE for ID 0x%X\n",
2838 : mExpectedPushPromiseID));
2839 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2840 : }
2841 :
2842 0 : if (mDownstreamState == BUFFERING_OPENING_SETTINGS &&
2843 0 : mInputFrameType != FRAME_TYPE_SETTINGS) {
2844 0 : LOG3(("First Frame Type Must Be Settings\n"));
2845 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2846 : }
2847 :
2848 0 : if (mInputFrameType != FRAME_TYPE_DATA) { // control frame
2849 0 : EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + kFrameHeaderBytes,
2850 0 : kFrameHeaderBytes, mInputFrameBufferSize);
2851 0 : ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
2852 0 : } else if (mInputFrameFlags & kFlag_PADDED) {
2853 0 : ChangeDownstreamState(PROCESSING_DATA_FRAME_PADDING_CONTROL);
2854 : } else {
2855 0 : rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
2856 0 : if (NS_FAILED(rv)) {
2857 0 : return rv;
2858 : }
2859 : }
2860 : }
2861 :
2862 0 : if (mDownstreamState == PROCESSING_DATA_FRAME_PADDING_CONTROL) {
2863 0 : MOZ_ASSERT(mInputFrameFlags & kFlag_PADDED,
2864 : "Processing padding control on unpadded frame");
2865 :
2866 0 : MOZ_ASSERT(mInputFrameBufferUsed < (kFrameHeaderBytes + 1),
2867 : "Frame buffer used too large for state");
2868 :
2869 0 : rv = NetworkRead(writer, &mInputFrameBuffer[mInputFrameBufferUsed],
2870 0 : (kFrameHeaderBytes + 1) - mInputFrameBufferUsed,
2871 0 : countWritten);
2872 :
2873 0 : if (NS_FAILED(rv)) {
2874 0 : LOG3(("Http2Session %p buffering data frame padding control read failure %" PRIx32 "\n",
2875 : this, static_cast<uint32_t>(rv)));
2876 : // maybe just blocked reading from network
2877 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
2878 0 : rv = NS_OK;
2879 0 : return rv;
2880 : }
2881 :
2882 0 : LogIO(this, nullptr, "Reading Data Frame Padding Control",
2883 0 : &mInputFrameBuffer[mInputFrameBufferUsed], *countWritten);
2884 :
2885 0 : mInputFrameBufferUsed += *countWritten;
2886 :
2887 0 : if (mInputFrameBufferUsed - kFrameHeaderBytes < 1) {
2888 0 : LOG3(("Http2Session::WriteSegments %p "
2889 : "BUFFERING DATA FRAME CONTROL PADDING incomplete size=%d",
2890 : this, mInputFrameBufferUsed - 8));
2891 0 : return rv;
2892 : }
2893 :
2894 0 : ++mInputFrameDataRead;
2895 :
2896 0 : char *control = &mInputFrameBuffer[kFrameHeaderBytes];
2897 0 : mPaddingLength = static_cast<uint8_t>(*control);
2898 :
2899 0 : LOG3(("Http2Session::WriteSegments %p stream 0x%X mPaddingLength=%d", this,
2900 : mInputFrameID, mPaddingLength));
2901 :
2902 0 : if (1U + mPaddingLength > mInputFrameDataSize) {
2903 0 : LOG3(("Http2Session::WriteSegments %p stream 0x%X padding too large for "
2904 : "frame", this, mInputFrameID));
2905 0 : RETURN_SESSION_ERROR(this, PROTOCOL_ERROR);
2906 0 : } else if (1U + mPaddingLength == mInputFrameDataSize) {
2907 : // This frame consists entirely of padding, we can just discard it
2908 0 : LOG3(("Http2Session::WriteSegments %p stream 0x%X frame with only padding",
2909 : this, mInputFrameID));
2910 0 : rv = ReadyToProcessDataFrame(DISCARDING_DATA_FRAME_PADDING);
2911 0 : if (NS_FAILED(rv)) {
2912 0 : return rv;
2913 : }
2914 : } else {
2915 0 : LOG3(("Http2Session::WriteSegments %p stream 0x%X ready to read HTTP data",
2916 : this, mInputFrameID));
2917 0 : rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME);
2918 0 : if (NS_FAILED(rv)) {
2919 0 : return rv;
2920 : }
2921 : }
2922 : }
2923 :
2924 0 : if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
2925 : nsresult streamCleanupCode;
2926 :
2927 : // There is no bounds checking on the error code.. we provide special
2928 : // handling for a couple of cases and all others (including unknown) are
2929 : // equivalent to cancel.
2930 0 : if (mDownstreamRstReason == REFUSED_STREAM_ERROR) {
2931 0 : streamCleanupCode = NS_ERROR_NET_RESET; // can retry this 100% safely
2932 0 : mInputFrameDataStream->Transaction()->ReuseConnectionOnRestartOK(true);
2933 0 : } else if (mDownstreamRstReason == HTTP_1_1_REQUIRED) {
2934 0 : streamCleanupCode = NS_ERROR_NET_RESET;
2935 0 : mInputFrameDataStream->Transaction()->ReuseConnectionOnRestartOK(true);
2936 0 : mInputFrameDataStream->Transaction()->DisableSpdy();
2937 : } else {
2938 0 : streamCleanupCode = mInputFrameDataStream->RecvdData() ?
2939 : NS_ERROR_NET_PARTIAL_TRANSFER :
2940 : NS_ERROR_NET_INTERRUPT;
2941 : }
2942 :
2943 0 : if (mDownstreamRstReason == COMPRESSION_ERROR) {
2944 0 : mShouldGoAway = true;
2945 : }
2946 :
2947 : // mInputFrameDataStream is reset by ChangeDownstreamState
2948 0 : Http2Stream *stream = mInputFrameDataStream;
2949 0 : ResetDownstreamState();
2950 0 : LOG3(("Http2Session::WriteSegments cleanup stream on recv of rst "
2951 : "session=%p stream=%p 0x%X\n", this, stream,
2952 : stream ? stream->StreamID() : 0));
2953 0 : CleanupStream(stream, streamCleanupCode, CANCEL_ERROR);
2954 0 : return NS_OK;
2955 : }
2956 :
2957 0 : if (mDownstreamState == PROCESSING_DATA_FRAME ||
2958 0 : mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
2959 :
2960 : // The cleanup stream should only be set while stream->WriteSegments is
2961 : // on the stack and then cleaned up in this code block afterwards.
2962 0 : MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
2963 0 : mNeedsCleanup = nullptr; /* just in case */
2964 :
2965 0 : uint32_t streamID = mInputFrameDataStream->StreamID();
2966 0 : mSegmentWriter = writer;
2967 0 : rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
2968 0 : mSegmentWriter = nullptr;
2969 :
2970 0 : mLastDataReadEpoch = mLastReadEpoch;
2971 :
2972 0 : if (SoftStreamError(rv)) {
2973 : // This will happen when the transaction figures out it is EOF, generally
2974 : // due to a content-length match being made. Return OK from this function
2975 : // otherwise the whole session would be torn down.
2976 :
2977 : // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
2978 : // back to PROCESSING_DATA_FRAME where we came from
2979 0 : mDownstreamState = PROCESSING_DATA_FRAME;
2980 :
2981 0 : if (mInputFrameDataRead == mInputFrameDataSize)
2982 0 : ResetDownstreamState();
2983 0 : LOG3(("Http2Session::WriteSegments session=%p id 0x%X "
2984 : "needscleanup=%p. cleanup stream based on "
2985 : "stream->writeSegments returning code %" PRIx32 "\n",
2986 : this, streamID, mNeedsCleanup, static_cast<uint32_t>(rv)));
2987 0 : MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup->StreamID() == streamID);
2988 0 : CleanupStream(streamID, NS_OK, CANCEL_ERROR);
2989 0 : mNeedsCleanup = nullptr;
2990 0 : *again = false;
2991 0 : rv = ResumeRecv();
2992 0 : if (NS_FAILED(rv)) {
2993 0 : LOG3(("ResumeRecv returned code %x", static_cast<uint32_t>(rv)));
2994 : }
2995 0 : return NS_OK;
2996 : }
2997 :
2998 0 : if (mNeedsCleanup) {
2999 0 : LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X "
3000 : "cleanup stream based on mNeedsCleanup.\n",
3001 : this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
3002 0 : CleanupStream(mNeedsCleanup, NS_OK, CANCEL_ERROR);
3003 0 : mNeedsCleanup = nullptr;
3004 : }
3005 :
3006 0 : if (NS_FAILED(rv)) {
3007 0 : LOG3(("Http2Session %p data frame read failure %" PRIx32 "\n", this,
3008 : static_cast<uint32_t>(rv)));
3009 : // maybe just blocked reading from network
3010 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
3011 0 : rv = NS_OK;
3012 : }
3013 :
3014 0 : return rv;
3015 : }
3016 :
3017 0 : if (mDownstreamState == DISCARDING_DATA_FRAME ||
3018 0 : mDownstreamState == DISCARDING_DATA_FRAME_PADDING) {
3019 : char trash[4096];
3020 0 : uint32_t discardCount = std::min(mInputFrameDataSize - mInputFrameDataRead,
3021 0 : 4096U);
3022 0 : LOG3(("Http2Session::WriteSegments %p trying to discard %d bytes of data",
3023 : this, discardCount));
3024 :
3025 0 : if (!discardCount) {
3026 0 : ResetDownstreamState();
3027 0 : Unused << ResumeRecv();
3028 0 : return NS_BASE_STREAM_WOULD_BLOCK;
3029 : }
3030 :
3031 0 : rv = NetworkRead(writer, trash, discardCount, countWritten);
3032 :
3033 0 : if (NS_FAILED(rv)) {
3034 0 : LOG3(("Http2Session %p discard frame read failure %" PRIx32 "\n", this,
3035 : static_cast<uint32_t>(rv)));
3036 : // maybe just blocked reading from network
3037 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
3038 0 : rv = NS_OK;
3039 0 : return rv;
3040 : }
3041 :
3042 0 : LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
3043 :
3044 0 : mInputFrameDataRead += *countWritten;
3045 :
3046 0 : if (mInputFrameDataRead == mInputFrameDataSize) {
3047 0 : Http2Stream *streamToCleanup = nullptr;
3048 0 : if (mInputFrameFinal) {
3049 0 : streamToCleanup = mInputFrameDataStream;
3050 : }
3051 :
3052 0 : ResetDownstreamState();
3053 :
3054 0 : if (streamToCleanup) {
3055 0 : CleanupStream(streamToCleanup, NS_OK, CANCEL_ERROR);
3056 : }
3057 : }
3058 0 : return rv;
3059 : }
3060 :
3061 0 : if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
3062 0 : MOZ_ASSERT(false); // this cannot happen
3063 : return NS_ERROR_UNEXPECTED;
3064 : }
3065 :
3066 0 : MOZ_ASSERT(mInputFrameBufferUsed == kFrameHeaderBytes, "Frame Buffer Header Not Present");
3067 0 : MOZ_ASSERT(mInputFrameDataSize + kFrameHeaderBytes <= mInputFrameBufferSize,
3068 : "allocation for control frame insufficient");
3069 :
3070 0 : rv = NetworkRead(writer, &mInputFrameBuffer[kFrameHeaderBytes + mInputFrameDataRead],
3071 0 : mInputFrameDataSize - mInputFrameDataRead, countWritten);
3072 :
3073 0 : if (NS_FAILED(rv)) {
3074 0 : LOG3(("Http2Session %p buffering control frame read failure %" PRIx32 "\n",
3075 : this, static_cast<uint32_t>(rv)));
3076 : // maybe just blocked reading from network
3077 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK)
3078 0 : rv = NS_OK;
3079 0 : return rv;
3080 : }
3081 :
3082 0 : LogIO(this, nullptr, "Reading Control Frame",
3083 0 : &mInputFrameBuffer[kFrameHeaderBytes + mInputFrameDataRead], *countWritten);
3084 :
3085 0 : mInputFrameDataRead += *countWritten;
3086 :
3087 0 : if (mInputFrameDataRead != mInputFrameDataSize)
3088 0 : return NS_OK;
3089 :
3090 0 : MOZ_ASSERT(mInputFrameType != FRAME_TYPE_DATA);
3091 0 : if (mInputFrameType < FRAME_TYPE_LAST) {
3092 0 : rv = sControlFunctions[mInputFrameType](this);
3093 : } else {
3094 : // Section 4.1 requires this to be ignored; though protocol_error would
3095 : // be better
3096 0 : LOG3(("Http2Session %p unknown frame type %x ignored\n",
3097 : this, mInputFrameType));
3098 0 : ResetDownstreamState();
3099 0 : rv = NS_OK;
3100 : }
3101 :
3102 0 : MOZ_ASSERT(NS_FAILED(rv) ||
3103 : mDownstreamState != BUFFERING_CONTROL_FRAME,
3104 : "Control Handler returned OK but did not change state");
3105 :
3106 0 : if (mShouldGoAway && !mStreamTransactionHash.Count())
3107 0 : Close(NS_OK);
3108 0 : return rv;
3109 : }
3110 :
3111 : nsresult
3112 0 : Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
3113 : uint32_t count, uint32_t *countWritten)
3114 : {
3115 0 : bool again = false;
3116 0 : return WriteSegmentsAgain(writer, count, countWritten, &again);
3117 : }
3118 :
3119 : nsresult
3120 0 : Http2Session::Finish0RTT(bool aRestart, bool aAlpnChanged)
3121 : {
3122 0 : MOZ_ASSERT(mAttemptingEarlyData);
3123 0 : LOG3(("Http2Session::Finish0RTT %p aRestart=%d aAlpnChanged=%d", this,
3124 : aRestart, aAlpnChanged));
3125 :
3126 0 : for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
3127 0 : if (m0RTTStreams[i]) {
3128 0 : m0RTTStreams[i]->Finish0RTT(aRestart, aAlpnChanged);
3129 : }
3130 : }
3131 :
3132 0 : if (aRestart) {
3133 : // 0RTT failed
3134 0 : if (aAlpnChanged) {
3135 : // This is a slightly more involved case - we need to get all our streams/
3136 : // transactions back in the queue so they can restart as http/1
3137 :
3138 : // These must be set this way to ensure we gracefully restart all streams
3139 0 : mGoAwayID = 0;
3140 0 : mCleanShutdown = true;
3141 :
3142 : // Close takes care of the rest of our work for us. The reason code here
3143 : // doesn't matter, as we aren't actually going to send a GOAWAY frame, but
3144 : // we use NS_ERROR_NET_RESET as it's closest to the truth.
3145 0 : Close(NS_ERROR_NET_RESET);
3146 : } else {
3147 : // This is the easy case - early data failed, but we're speaking h2, so
3148 : // we just need to rewind to the beginning of the preamble and try again.
3149 0 : mOutputQueueSent = 0;
3150 : }
3151 : } else {
3152 : // 0RTT succeeded
3153 : // Make sure we look for any incoming data in repsonse to our early data.
3154 0 : Unused << ResumeRecv();
3155 : }
3156 :
3157 0 : mAttemptingEarlyData = false;
3158 0 : m0RTTStreams.Clear();
3159 0 : RealignOutputQueue();
3160 :
3161 0 : return NS_OK;
3162 : }
3163 :
3164 : void
3165 0 : Http2Session::SetFastOpenStatus(uint8_t aStatus)
3166 : {
3167 0 : LOG3(("Http2Session::SetFastOpenStatus %d [this=%p]",
3168 : aStatus, this));
3169 :
3170 0 : for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
3171 0 : if (m0RTTStreams[i]) {
3172 0 : m0RTTStreams[i]->Transaction()->SetFastOpenStatus(aStatus);
3173 : }
3174 : }
3175 0 : }
3176 :
3177 : nsresult
3178 0 : Http2Session::ProcessConnectedPush(Http2Stream *pushConnectedStream,
3179 : nsAHttpSegmentWriter * writer,
3180 : uint32_t count, uint32_t *countWritten)
3181 : {
3182 0 : LOG3(("Http2Session::ProcessConnectedPush %p 0x%X\n",
3183 : this, pushConnectedStream->StreamID()));
3184 0 : mSegmentWriter = writer;
3185 0 : nsresult rv = pushConnectedStream->WriteSegments(this, count, countWritten);
3186 0 : mSegmentWriter = nullptr;
3187 :
3188 : // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
3189 : // so we need this check to determine the truth.
3190 0 : if (NS_SUCCEEDED(rv) && !*countWritten &&
3191 0 : pushConnectedStream->PushSource() &&
3192 0 : pushConnectedStream->PushSource()->GetPushComplete()) {
3193 0 : rv = NS_BASE_STREAM_CLOSED;
3194 : }
3195 :
3196 0 : if (rv == NS_BASE_STREAM_CLOSED) {
3197 0 : CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
3198 0 : rv = NS_OK;
3199 : }
3200 :
3201 : // if we return OK to nsHttpConnection it will use mSocketInCondition
3202 : // to determine whether to schedule more reads, incorrectly
3203 : // assuming that nsHttpConnection::OnSocketWrite() was called.
3204 0 : if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
3205 0 : rv = NS_BASE_STREAM_WOULD_BLOCK;
3206 0 : Unused << ResumeRecv();
3207 : }
3208 0 : return rv;
3209 : }
3210 :
3211 : nsresult
3212 0 : Http2Session::ProcessSlowConsumer(Http2Stream *slowConsumer,
3213 : nsAHttpSegmentWriter * writer,
3214 : uint32_t count, uint32_t *countWritten)
3215 : {
3216 0 : LOG3(("Http2Session::ProcessSlowConsumer %p 0x%X\n",
3217 : this, slowConsumer->StreamID()));
3218 0 : mSegmentWriter = writer;
3219 0 : nsresult rv = slowConsumer->WriteSegments(this, count, countWritten);
3220 0 : mSegmentWriter = nullptr;
3221 0 : LOG3(("Http2Session::ProcessSlowConsumer Writesegments %p 0x%X rv %" PRIX32 " %d\n",
3222 : this, slowConsumer->StreamID(), static_cast<uint32_t>(rv), *countWritten));
3223 0 : if (NS_SUCCEEDED(rv) && !*countWritten && slowConsumer->RecvdFin()) {
3224 0 : rv = NS_BASE_STREAM_CLOSED;
3225 : }
3226 :
3227 0 : if (NS_SUCCEEDED(rv) && (*countWritten > 0)) {
3228 : // There have been buffered bytes successfully fed into the
3229 : // formerly blocked consumer. Repeat until buffer empty or
3230 : // consumer is blocked again.
3231 0 : UpdateLocalRwin(slowConsumer, 0);
3232 0 : ConnectSlowConsumer(slowConsumer);
3233 : }
3234 :
3235 0 : if (rv == NS_BASE_STREAM_CLOSED) {
3236 0 : CleanupStream(slowConsumer, NS_OK, CANCEL_ERROR);
3237 0 : rv = NS_OK;
3238 : }
3239 :
3240 0 : return rv;
3241 : }
3242 :
3243 : void
3244 0 : Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
3245 : {
3246 0 : if (!stream) // this is ok - it means there was a data frame for a rst stream
3247 0 : return;
3248 :
3249 : // If this data packet was not for a valid or live stream then there
3250 : // is no reason to mess with the flow control
3251 0 : if (!stream || stream->RecvdFin() || stream->RecvdReset() ||
3252 0 : mInputFrameFinal) {
3253 0 : return;
3254 : }
3255 :
3256 0 : stream->DecrementClientReceiveWindow(bytes);
3257 :
3258 : // Don't necessarily ack every data packet. Only do it
3259 : // after a significant amount of data.
3260 0 : uint64_t unacked = stream->LocalUnAcked();
3261 0 : int64_t localWindow = stream->ClientReceiveWindow();
3262 :
3263 0 : LOG3(("Http2Session::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u "
3264 : "unacked=%" PRIu64 " localWindow=%" PRId64 "\n",
3265 : this, stream->StreamID(), bytes, unacked, localWindow));
3266 :
3267 0 : if (!unacked)
3268 0 : return;
3269 :
3270 0 : if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
3271 0 : return;
3272 :
3273 0 : if (!stream->HasSink()) {
3274 0 : LOG3(("Http2Session::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n",
3275 : this, stream->StreamID()));
3276 0 : return;
3277 : }
3278 :
3279 : // Generate window updates directly out of session instead of the stream
3280 : // in order to avoid queue delays in getting the 'ACK' out.
3281 0 : uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
3282 :
3283 0 : LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
3284 : this, stream->StreamID(), toack));
3285 0 : stream->IncrementClientReceiveWindow(toack);
3286 0 : if (toack == 0) {
3287 : // Ensure we never send an illegal 0 window update
3288 0 : return;
3289 : }
3290 :
3291 : // room for this packet needs to be ensured before calling this function
3292 0 : char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
3293 0 : mOutputQueueUsed += kFrameHeaderBytes + 4;
3294 0 : MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
3295 :
3296 0 : CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID());
3297 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, toack);
3298 :
3299 0 : LogIO(this, stream, "Stream Window Update", packet, kFrameHeaderBytes + 4);
3300 : // dont flush here, this write can commonly be coalesced with a
3301 : // session window update to immediately follow.
3302 : }
3303 :
3304 : void
3305 0 : Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
3306 : {
3307 0 : if (!bytes)
3308 0 : return;
3309 :
3310 0 : mLocalSessionWindow -= bytes;
3311 :
3312 0 : LOG3(("Http2Session::UpdateLocalSessionWindow this=%p newbytes=%u "
3313 : "localWindow=%" PRId64 "\n", this, bytes, mLocalSessionWindow));
3314 :
3315 : // Don't necessarily ack every data packet. Only do it
3316 : // after a significant amount of data.
3317 0 : if ((mLocalSessionWindow > (mInitialRwin - kMinimumToAck)) &&
3318 0 : (mLocalSessionWindow > kEmergencyWindowThreshold))
3319 0 : return;
3320 :
3321 : // Only send max bits of window updates at a time.
3322 0 : uint64_t toack64 = mInitialRwin - mLocalSessionWindow;
3323 0 : uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
3324 :
3325 0 : LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
3326 : this, toack));
3327 0 : mLocalSessionWindow += toack;
3328 :
3329 0 : if (toack == 0) {
3330 : // Ensure we never send an illegal 0 window update
3331 0 : return;
3332 : }
3333 :
3334 : // room for this packet needs to be ensured before calling this function
3335 0 : char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
3336 0 : mOutputQueueUsed += kFrameHeaderBytes + 4;
3337 0 : MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
3338 :
3339 0 : CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
3340 0 : NetworkEndian::writeUint32(packet + kFrameHeaderBytes, toack);
3341 :
3342 0 : LogIO(this, nullptr, "Session Window Update", packet, kFrameHeaderBytes + 4);
3343 : // dont flush here, this write can commonly be coalesced with others
3344 : }
3345 :
3346 : void
3347 0 : Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes)
3348 : {
3349 : // make sure there is room for 2 window updates even though
3350 : // we may not generate any.
3351 0 : EnsureOutputBuffer(2 * (kFrameHeaderBytes + 4));
3352 :
3353 0 : UpdateLocalStreamWindow(stream, bytes);
3354 0 : UpdateLocalSessionWindow(bytes);
3355 0 : FlushOutputQueue();
3356 0 : }
3357 :
3358 : void
3359 0 : Http2Session::Close(nsresult aReason)
3360 : {
3361 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3362 :
3363 0 : if (mClosed)
3364 0 : return;
3365 :
3366 0 : LOG3(("Http2Session::Close %p %" PRIX32, this, static_cast<uint32_t>(aReason)));
3367 :
3368 0 : mClosed = true;
3369 :
3370 0 : Shutdown();
3371 :
3372 0 : mStreamIDHash.Clear();
3373 0 : mStreamTransactionHash.Clear();
3374 :
3375 : uint32_t goAwayReason;
3376 0 : if (mGoAwayReason != NO_HTTP_ERROR) {
3377 0 : goAwayReason = mGoAwayReason;
3378 0 : } else if (NS_SUCCEEDED(aReason)) {
3379 0 : goAwayReason = NO_HTTP_ERROR;
3380 0 : } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
3381 0 : goAwayReason = PROTOCOL_ERROR;
3382 : } else {
3383 0 : goAwayReason = INTERNAL_ERROR;
3384 : }
3385 0 : if (!mAttemptingEarlyData) {
3386 0 : GenerateGoAway(goAwayReason);
3387 : }
3388 0 : mConnection = nullptr;
3389 0 : mSegmentReader = nullptr;
3390 0 : mSegmentWriter = nullptr;
3391 : }
3392 :
3393 : nsHttpConnectionInfo *
3394 0 : Http2Session::ConnectionInfo()
3395 : {
3396 0 : RefPtr<nsHttpConnectionInfo> ci;
3397 0 : GetConnectionInfo(getter_AddRefs(ci));
3398 0 : return ci.get();
3399 : }
3400 :
3401 : void
3402 0 : Http2Session::CloseTransaction(nsAHttpTransaction *aTransaction,
3403 : nsresult aResult)
3404 : {
3405 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3406 0 : LOG3(("Http2Session::CloseTransaction %p %p %" PRIx32, this, aTransaction,
3407 : static_cast<uint32_t>(aResult)));
3408 :
3409 : // Generally this arrives as a cancel event from the connection manager.
3410 :
3411 : // need to find the stream and call CleanupStream() on it.
3412 0 : Http2Stream *stream = mStreamTransactionHash.Get(aTransaction);
3413 0 : if (!stream) {
3414 0 : LOG3(("Http2Session::CloseTransaction %p %p %" PRIx32 " - not found.",
3415 : this, aTransaction, static_cast<uint32_t>(aResult)));
3416 0 : return;
3417 : }
3418 0 : LOG3(("Http2Session::CloseTransaction probably a cancel. "
3419 : "this=%p, trans=%p, result=%" PRIx32 ", streamID=0x%X stream=%p",
3420 : this, aTransaction, static_cast<uint32_t>(aResult), stream->StreamID(), stream));
3421 0 : CleanupStream(stream, aResult, CANCEL_ERROR);
3422 0 : nsresult rv = ResumeRecv();
3423 0 : if (NS_FAILED(rv)) {
3424 0 : LOG3(("Http2Session::CloseTransaction %p %p %x ResumeRecv returned %x",
3425 : this, aTransaction, static_cast<uint32_t>(aResult),
3426 : static_cast<uint32_t>(rv)));
3427 : }
3428 : }
3429 :
3430 : //-----------------------------------------------------------------------------
3431 : // nsAHttpSegmentReader
3432 : //-----------------------------------------------------------------------------
3433 :
3434 : nsresult
3435 0 : Http2Session::OnReadSegment(const char *buf,
3436 : uint32_t count, uint32_t *countRead)
3437 : {
3438 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3439 : nsresult rv;
3440 :
3441 : // If we can release old queued data then we can try and write the new
3442 : // data directly to the network without using the output queue at all
3443 0 : if (mOutputQueueUsed)
3444 0 : FlushOutputQueue();
3445 :
3446 0 : if (!mOutputQueueUsed && mSegmentReader) {
3447 : // try and write directly without output queue
3448 0 : rv = mSegmentReader->OnReadSegment(buf, count, countRead);
3449 :
3450 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
3451 0 : *countRead = 0;
3452 0 : } else if (NS_FAILED(rv)) {
3453 0 : return rv;
3454 : }
3455 :
3456 0 : if (*countRead < count) {
3457 0 : uint32_t required = count - *countRead;
3458 : // assuming a commitment() happened, this ensurebuffer is a nop
3459 : // but just in case the queuesize is too small for the required data
3460 : // call ensurebuffer().
3461 0 : EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
3462 0 : memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
3463 0 : mOutputQueueUsed = required;
3464 : }
3465 :
3466 0 : *countRead = count;
3467 0 : return NS_OK;
3468 : }
3469 :
3470 : // At this point we are going to buffer the new data in the output
3471 : // queue if it fits. By coalescing multiple small submissions into one larger
3472 : // buffer we can get larger writes out to the network later on.
3473 :
3474 : // This routine should not be allowed to fill up the output queue
3475 : // all on its own - at least kQueueReserved bytes are always left
3476 : // for other routines to use - but this is an all-or-nothing function,
3477 : // so if it will not all fit just return WOULD_BLOCK
3478 :
3479 0 : if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
3480 0 : return NS_BASE_STREAM_WOULD_BLOCK;
3481 :
3482 0 : memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
3483 0 : mOutputQueueUsed += count;
3484 0 : *countRead = count;
3485 :
3486 0 : FlushOutputQueue();
3487 :
3488 0 : return NS_OK;
3489 : }
3490 :
3491 : nsresult
3492 0 : Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment)
3493 : {
3494 0 : if (mOutputQueueUsed && !mAttemptingEarlyData)
3495 0 : FlushOutputQueue();
3496 :
3497 : // would there be enough room to buffer this if needed?
3498 0 : if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
3499 0 : return NS_OK;
3500 :
3501 : // if we are using part of our buffers already, try again later unless
3502 : // forceCommitment is set.
3503 0 : if (mOutputQueueUsed && !forceCommitment)
3504 0 : return NS_BASE_STREAM_WOULD_BLOCK;
3505 :
3506 0 : if (mOutputQueueUsed) {
3507 : // normally we avoid the memmove of RealignOutputQueue, but we'll try
3508 : // it if forceCommitment is set before growing the buffer.
3509 0 : RealignOutputQueue();
3510 :
3511 : // is there enough room now?
3512 0 : if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
3513 0 : return NS_OK;
3514 : }
3515 :
3516 : // resize the buffers as needed
3517 0 : EnsureOutputBuffer(count + kQueueReserved);
3518 :
3519 0 : MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
3520 : "buffer not as large as expected");
3521 :
3522 0 : return NS_OK;
3523 : }
3524 :
3525 : //-----------------------------------------------------------------------------
3526 : // nsAHttpSegmentWriter
3527 : //-----------------------------------------------------------------------------
3528 :
3529 : nsresult
3530 0 : Http2Session::OnWriteSegment(char *buf,
3531 : uint32_t count, uint32_t *countWritten)
3532 : {
3533 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3534 : nsresult rv;
3535 :
3536 0 : if (!mSegmentWriter) {
3537 : // the only way this could happen would be if Close() were called on the
3538 : // stack with WriteSegments()
3539 0 : return NS_ERROR_FAILURE;
3540 : }
3541 :
3542 0 : if (mDownstreamState == NOT_USING_NETWORK ||
3543 0 : mDownstreamState == BUFFERING_FRAME_HEADER ||
3544 0 : mDownstreamState == DISCARDING_DATA_FRAME_PADDING) {
3545 0 : return NS_BASE_STREAM_WOULD_BLOCK;
3546 : }
3547 :
3548 0 : if (mDownstreamState == PROCESSING_DATA_FRAME) {
3549 :
3550 0 : if (mInputFrameFinal &&
3551 0 : mInputFrameDataRead == mInputFrameDataSize) {
3552 0 : *countWritten = 0;
3553 0 : SetNeedsCleanup();
3554 0 : return NS_BASE_STREAM_CLOSED;
3555 : }
3556 :
3557 0 : count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
3558 0 : rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
3559 0 : if (NS_FAILED(rv))
3560 0 : return rv;
3561 :
3562 0 : LogIO(this, mInputFrameDataStream, "Reading Data Frame",
3563 0 : buf, *countWritten);
3564 :
3565 0 : mInputFrameDataRead += *countWritten;
3566 0 : if (mPaddingLength && (mInputFrameDataSize - mInputFrameDataRead <= mPaddingLength)) {
3567 : // We are crossing from real HTTP data into the realm of padding. If
3568 : // we've actually crossed the line, we need to munge countWritten for the
3569 : // sake of goodness and sanity. No matter what, any future calls to
3570 : // WriteSegments need to just discard data until we reach the end of this
3571 : // frame.
3572 0 : if (mInputFrameDataSize != mInputFrameDataRead) {
3573 : // Only change state if we still have padding to read. If we don't do
3574 : // this, we can end up hanging on frames that combine real data,
3575 : // padding, and END_STREAM (see bug 1019921)
3576 0 : ChangeDownstreamState(DISCARDING_DATA_FRAME_PADDING);
3577 : }
3578 0 : uint32_t paddingRead = mPaddingLength - (mInputFrameDataSize - mInputFrameDataRead);
3579 0 : LOG3(("Http2Session::OnWriteSegment %p stream 0x%X len=%d read=%d "
3580 : "crossed from HTTP data into padding (%d of %d) countWritten=%d",
3581 : this, mInputFrameID, mInputFrameDataSize, mInputFrameDataRead,
3582 : paddingRead, mPaddingLength, *countWritten));
3583 0 : *countWritten -= paddingRead;
3584 0 : LOG3(("Http2Session::OnWriteSegment %p stream 0x%X new countWritten=%d",
3585 : this, mInputFrameID, *countWritten));
3586 : }
3587 :
3588 0 : mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
3589 0 : if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameFinal)
3590 0 : ResetDownstreamState();
3591 :
3592 0 : return rv;
3593 : }
3594 :
3595 0 : if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
3596 :
3597 0 : if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
3598 0 : mInputFrameFinal) {
3599 0 : *countWritten = 0;
3600 0 : SetNeedsCleanup();
3601 0 : return NS_BASE_STREAM_CLOSED;
3602 : }
3603 :
3604 0 : count = std::min(count,
3605 0 : mFlatHTTPResponseHeaders.Length() -
3606 0 : mFlatHTTPResponseHeadersOut);
3607 0 : memcpy(buf,
3608 0 : mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
3609 0 : count);
3610 0 : mFlatHTTPResponseHeadersOut += count;
3611 0 : *countWritten = count;
3612 :
3613 0 : if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
3614 0 : if (!mInputFrameFinal) {
3615 : // If more frames are expected in this stream, then reset the state so they can be
3616 : // handled. Otherwise (e.g. a 0 length response with the fin on the incoming headers)
3617 : // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
3618 : // cleanup the stream.
3619 0 : ResetDownstreamState();
3620 : }
3621 : }
3622 :
3623 0 : return NS_OK;
3624 : }
3625 :
3626 0 : MOZ_ASSERT(false);
3627 : return NS_ERROR_UNEXPECTED;
3628 : }
3629 :
3630 : void
3631 0 : Http2Session::SetNeedsCleanup()
3632 : {
3633 0 : LOG3(("Http2Session::SetNeedsCleanup %p - recorded downstream fin of "
3634 : "stream %p 0x%X", this, mInputFrameDataStream,
3635 : mInputFrameDataStream->StreamID()));
3636 :
3637 : // This will result in Close() being called
3638 0 : MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
3639 0 : mInputFrameDataStream->SetResponseIsComplete();
3640 0 : mNeedsCleanup = mInputFrameDataStream;
3641 0 : ResetDownstreamState();
3642 0 : }
3643 :
3644 : void
3645 0 : Http2Session::ConnectPushedStream(Http2Stream *stream)
3646 : {
3647 0 : mPushesReadyForRead.Push(stream);
3648 0 : Unused << ForceRecv();
3649 0 : }
3650 :
3651 : void
3652 0 : Http2Session::ConnectSlowConsumer(Http2Stream *stream)
3653 : {
3654 0 : LOG3(("Http2Session::ConnectSlowConsumer %p 0x%X\n",
3655 : this, stream->StreamID()));
3656 0 : mSlowConsumersReadyForRead.Push(stream);
3657 0 : Unused << ForceRecv();
3658 0 : }
3659 :
3660 : uint32_t
3661 0 : Http2Session::FindTunnelCount(nsHttpConnectionInfo *aConnInfo)
3662 : {
3663 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3664 0 : uint32_t rv = 0;
3665 0 : mTunnelHash.Get(aConnInfo->HashKey(), &rv);
3666 0 : return rv;
3667 : }
3668 :
3669 : void
3670 0 : Http2Session::RegisterTunnel(Http2Stream *aTunnel)
3671 : {
3672 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3673 0 : nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
3674 0 : uint32_t newcount = FindTunnelCount(ci) + 1;
3675 0 : mTunnelHash.Remove(ci->HashKey());
3676 0 : mTunnelHash.Put(ci->HashKey(), newcount);
3677 0 : LOG3(("Http2Stream::RegisterTunnel %p stream=%p tunnels=%d [%s]",
3678 : this, aTunnel, newcount, ci->HashKey().get()));
3679 0 : }
3680 :
3681 : void
3682 0 : Http2Session::UnRegisterTunnel(Http2Stream *aTunnel)
3683 : {
3684 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3685 0 : nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
3686 0 : MOZ_ASSERT(FindTunnelCount(ci));
3687 0 : uint32_t newcount = FindTunnelCount(ci) - 1;
3688 0 : mTunnelHash.Remove(ci->HashKey());
3689 0 : if (newcount) {
3690 0 : mTunnelHash.Put(ci->HashKey(), newcount);
3691 : }
3692 0 : LOG3(("Http2Session::UnRegisterTunnel %p stream=%p tunnels=%d [%s]",
3693 : this, aTunnel, newcount, ci->HashKey().get()));
3694 0 : }
3695 :
3696 : void
3697 0 : Http2Session::CreateTunnel(nsHttpTransaction *trans,
3698 : nsHttpConnectionInfo *ci,
3699 : nsIInterfaceRequestor *aCallbacks)
3700 : {
3701 0 : LOG(("Http2Session::CreateTunnel %p %p make new tunnel\n", this, trans));
3702 : // The connect transaction will hold onto the underlying http
3703 : // transaction so that an auth created by the connect can be mappped
3704 : // to the correct security callbacks
3705 :
3706 : RefPtr<SpdyConnectTransaction> connectTrans =
3707 0 : new SpdyConnectTransaction(ci, aCallbacks, trans->Caps(), trans, this);
3708 0 : DebugOnly<bool> rv = AddStream(connectTrans, nsISupportsPriority::PRIORITY_NORMAL, false, nullptr);
3709 0 : MOZ_ASSERT(rv);
3710 0 : Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
3711 0 : MOZ_ASSERT(tunnel);
3712 0 : RegisterTunnel(tunnel);
3713 0 : }
3714 :
3715 : void
3716 0 : Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
3717 : nsIInterfaceRequestor *aCallbacks)
3718 : {
3719 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3720 0 : nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
3721 0 : nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
3722 0 : MOZ_ASSERT(trans);
3723 :
3724 0 : LOG3(("Http2Session::DispatchOnTunnel %p trans=%p", this, trans));
3725 :
3726 0 : aHttpTransaction->SetConnection(nullptr);
3727 :
3728 : // this transaction has done its work of setting up a tunnel, let
3729 : // the connection manager queue it if necessary
3730 0 : trans->SetTunnelProvider(this);
3731 0 : trans->EnableKeepAlive();
3732 :
3733 0 : if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
3734 0 : LOG3(("Http2Session::DispatchOnTunnel %p create on new tunnel %s",
3735 : this, ci->HashKey().get()));
3736 0 : CreateTunnel(trans, ci, aCallbacks);
3737 : } else {
3738 : // requeue it. The connection manager is responsible for actually putting
3739 : // this on the tunnel connection with the specific ci. If that can't
3740 : // happen the cmgr checks with us via MaybeReTunnel() to see if it should
3741 : // make a new tunnel or just wait longer.
3742 0 : LOG3(("Http2Session::DispatchOnTunnel %p trans=%p queue in connection manager",
3743 : this, trans));
3744 0 : nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
3745 0 : if (NS_FAILED(rv)) {
3746 0 : LOG3(("Http2Session::DispatchOnTunnel %p trans=%p failed to initiate "
3747 : "transaction (%08x)", this, trans, static_cast<uint32_t>(rv)));
3748 : }
3749 : }
3750 0 : }
3751 :
3752 : // From ASpdySession
3753 : bool
3754 0 : Http2Session::MaybeReTunnel(nsAHttpTransaction *aHttpTransaction)
3755 : {
3756 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3757 0 : nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
3758 0 : LOG(("Http2Session::MaybeReTunnel %p trans=%p\n", this, trans));
3759 0 : if (!trans || trans->TunnelProvider() != this) {
3760 : // this isn't really one of our transactions.
3761 0 : return false;
3762 : }
3763 :
3764 0 : if (mClosed || mShouldGoAway) {
3765 0 : LOG(("Http2Session::MaybeReTunnel %p %p session closed - requeue\n", this, trans));
3766 0 : trans->SetTunnelProvider(nullptr);
3767 0 : nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
3768 0 : if (NS_FAILED(rv)) {
3769 0 : LOG3(("Http2Session::MaybeReTunnel %p trans=%p failed to initiate "
3770 : "transaction (%08x)", this, trans, static_cast<uint32_t>(rv)));
3771 : }
3772 0 : return true;
3773 : }
3774 :
3775 0 : nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
3776 0 : LOG(("Http2Session:MaybeReTunnel %p %p count=%d limit %d\n",
3777 : this, trans, FindTunnelCount(ci), gHttpHandler->MaxConnectionsPerOrigin()));
3778 0 : if (FindTunnelCount(ci) >= gHttpHandler->MaxConnectionsPerOrigin()) {
3779 : // patience - a tunnel will open up.
3780 0 : return false;
3781 : }
3782 :
3783 0 : LOG(("Http2Session::MaybeReTunnel %p %p make new tunnel\n", this, trans));
3784 0 : CreateTunnel(trans, ci, trans->SecurityCallbacks());
3785 0 : return true;
3786 : }
3787 :
3788 : nsresult
3789 0 : Http2Session::BufferOutput(const char *buf,
3790 : uint32_t count,
3791 : uint32_t *countRead)
3792 : {
3793 0 : nsAHttpSegmentReader *old = mSegmentReader;
3794 0 : mSegmentReader = nullptr;
3795 0 : nsresult rv = OnReadSegment(buf, count, countRead);
3796 0 : mSegmentReader = old;
3797 0 : return rv;
3798 : }
3799 :
3800 : bool // static
3801 0 : Http2Session::ALPNCallback(nsISupports *securityInfo)
3802 : {
3803 0 : if (!gHttpHandler->IsH2MandatorySuiteEnabled()) {
3804 0 : LOG3(("Http2Session::ALPNCallback Mandatory Cipher Suite Unavailable\n"));
3805 0 : return false;
3806 : }
3807 :
3808 0 : nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
3809 0 : LOG3(("Http2Session::ALPNCallback sslsocketcontrol=%p\n", ssl.get()));
3810 0 : if (ssl) {
3811 0 : int16_t version = ssl->GetSSLVersionOffered();
3812 0 : LOG3(("Http2Session::ALPNCallback version=%x\n", version));
3813 0 : if (version >= nsISSLSocketControl::TLS_VERSION_1_2) {
3814 0 : return true;
3815 : }
3816 : }
3817 0 : return false;
3818 : }
3819 :
3820 : nsresult
3821 0 : Http2Session::ConfirmTLSProfile()
3822 : {
3823 0 : if (mTLSProfileConfirmed) {
3824 0 : return NS_OK;
3825 : }
3826 :
3827 0 : LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n",
3828 : this, mConnection.get()));
3829 :
3830 0 : if (mAttemptingEarlyData) {
3831 0 : LOG3(("Http2Session::ConfirmTLSProfile %p temporarily passing due to early data\n", this));
3832 0 : return NS_OK;
3833 : }
3834 :
3835 0 : if (!gHttpHandler->EnforceHttp2TlsProfile()) {
3836 0 : LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this));
3837 0 : mTLSProfileConfirmed = true;
3838 0 : return NS_OK;
3839 : }
3840 :
3841 0 : if (!mConnection)
3842 0 : return NS_ERROR_FAILURE;
3843 :
3844 0 : nsCOMPtr<nsISupports> securityInfo;
3845 0 : mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
3846 0 : nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo);
3847 0 : LOG3(("Http2Session::ConfirmTLSProfile %p sslsocketcontrol=%p\n", this, ssl.get()));
3848 0 : if (!ssl)
3849 0 : return NS_ERROR_FAILURE;
3850 :
3851 0 : int16_t version = ssl->GetSSLVersionUsed();
3852 0 : LOG3(("Http2Session::ConfirmTLSProfile %p version=%x\n", this, version));
3853 0 : if (version < nsISSLSocketControl::TLS_VERSION_1_2) {
3854 0 : LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of TLS1.2\n", this));
3855 0 : RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
3856 : }
3857 :
3858 0 : uint16_t kea = ssl->GetKEAUsed();
3859 0 : if (kea != ssl_kea_dh && kea != ssl_kea_ecdh) {
3860 0 : LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to invalid KEA %d\n",
3861 : this, kea));
3862 0 : RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
3863 : }
3864 :
3865 0 : uint32_t keybits = ssl->GetKEAKeyBits();
3866 0 : if (kea == ssl_kea_dh && keybits < 2048) {
3867 0 : LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to DH %d < 2048\n",
3868 : this, keybits));
3869 0 : RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
3870 0 : } else if (kea == ssl_kea_ecdh && keybits < 224) { // see rfc7540 9.2.1.
3871 0 : LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to ECDH %d < 224\n",
3872 : this, keybits));
3873 0 : RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
3874 : }
3875 :
3876 0 : int16_t macAlgorithm = ssl->GetMACAlgorithmUsed();
3877 0 : LOG3(("Http2Session::ConfirmTLSProfile %p MAC Algortihm (aead==6) %d\n",
3878 : this, macAlgorithm));
3879 0 : if (macAlgorithm != nsISSLSocketControl::SSL_MAC_AEAD) {
3880 0 : LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of AEAD\n", this));
3881 0 : RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY);
3882 : }
3883 :
3884 : /* We are required to send SNI. We do that already, so no check is done
3885 : * here to make sure we did. */
3886 :
3887 : /* We really should check to ensure TLS compression isn't enabled on
3888 : * this connection. However, we never enable TLS compression on our end,
3889 : * anyway, so it'll never be on. All the same, see https://bugzil.la/965881
3890 : * for the possibility for an interface to ensure it never gets turned on. */
3891 :
3892 0 : mTLSProfileConfirmed = true;
3893 0 : return NS_OK;
3894 : }
3895 :
3896 :
3897 : //-----------------------------------------------------------------------------
3898 : // Modified methods of nsAHttpConnection
3899 : //-----------------------------------------------------------------------------
3900 :
3901 : void
3902 0 : Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
3903 : {
3904 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3905 0 : LOG3(("Http2Session::TransactionHasDataToWrite %p trans=%p", this, caller));
3906 :
3907 : // a trapped signal from the http transaction to the connection that
3908 : // it is no longer blocked on read.
3909 :
3910 0 : Http2Stream *stream = mStreamTransactionHash.Get(caller);
3911 0 : if (!stream || !VerifyStream(stream)) {
3912 0 : LOG3(("Http2Session::TransactionHasDataToWrite %p caller %p not found",
3913 : this, caller));
3914 0 : return;
3915 : }
3916 :
3917 0 : LOG3(("Http2Session::TransactionHasDataToWrite %p ID is 0x%X\n",
3918 : this, stream->StreamID()));
3919 :
3920 0 : if (!mClosed) {
3921 0 : mReadyForWrite.Push(stream);
3922 0 : SetWriteCallbacks();
3923 : } else {
3924 0 : LOG3(("Http2Session::TransactionHasDataToWrite %p closed so not setting Ready4Write\n",
3925 : this));
3926 : }
3927 :
3928 : // NSPR poll will not poll the network if there are non system PR_FileDesc's
3929 : // that are ready - so we can get into a deadlock waiting for the system IO
3930 : // to come back here if we don't force the send loop manually.
3931 0 : Unused << ForceSend();
3932 : }
3933 :
3934 : void
3935 0 : Http2Session::TransactionHasDataToRecv(nsAHttpTransaction *caller)
3936 : {
3937 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3938 0 : LOG3(("Http2Session::TransactionHasDataToRecv %p trans=%p", this, caller));
3939 :
3940 : // a signal from the http transaction to the connection that it will consume more
3941 0 : Http2Stream *stream = mStreamTransactionHash.Get(caller);
3942 0 : if (!stream || !VerifyStream(stream)) {
3943 0 : LOG3(("Http2Session::TransactionHasDataToRecv %p caller %p not found",
3944 : this, caller));
3945 0 : return;
3946 : }
3947 :
3948 0 : LOG3(("Http2Session::TransactionHasDataToRecv %p ID is 0x%X\n",
3949 : this, stream->StreamID()));
3950 0 : ConnectSlowConsumer(stream);
3951 : }
3952 :
3953 : void
3954 0 : Http2Session::TransactionHasDataToWrite(Http2Stream *stream)
3955 : {
3956 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3957 0 : LOG3(("Http2Session::TransactionHasDataToWrite %p stream=%p ID=0x%x",
3958 : this, stream, stream->StreamID()));
3959 :
3960 0 : mReadyForWrite.Push(stream);
3961 0 : SetWriteCallbacks();
3962 0 : Unused << ForceSend();
3963 0 : }
3964 :
3965 : bool
3966 0 : Http2Session::IsPersistent()
3967 : {
3968 0 : return true;
3969 : }
3970 :
3971 : nsresult
3972 0 : Http2Session::TakeTransport(nsISocketTransport **,
3973 : nsIAsyncInputStream **, nsIAsyncOutputStream **)
3974 : {
3975 0 : MOZ_ASSERT(false, "TakeTransport of Http2Session");
3976 : return NS_ERROR_UNEXPECTED;
3977 : }
3978 :
3979 : already_AddRefed<nsHttpConnection>
3980 0 : Http2Session::TakeHttpConnection()
3981 : {
3982 0 : MOZ_ASSERT(false, "TakeHttpConnection of Http2Session");
3983 : return nullptr;
3984 : }
3985 :
3986 : already_AddRefed<nsHttpConnection>
3987 0 : Http2Session::HttpConnection()
3988 : {
3989 0 : if (mConnection) {
3990 0 : return mConnection->HttpConnection();
3991 : }
3992 0 : return nullptr;
3993 : }
3994 :
3995 : void
3996 0 : Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **aOut)
3997 : {
3998 0 : *aOut = nullptr;
3999 0 : }
4000 :
4001 : //-----------------------------------------------------------------------------
4002 : // unused methods of nsAHttpTransaction
4003 : // We can be sure of this because Http2Session is only constructed in
4004 : // nsHttpConnection and is never passed out of that object or a TLSFilterTransaction
4005 : // TLS tunnel
4006 : //-----------------------------------------------------------------------------
4007 :
4008 : void
4009 0 : Http2Session::SetConnection(nsAHttpConnection *)
4010 : {
4011 : // This is unexpected
4012 0 : MOZ_ASSERT(false, "Http2Session::SetConnection()");
4013 : }
4014 :
4015 : void
4016 0 : Http2Session::SetProxyConnectFailed()
4017 : {
4018 0 : MOZ_ASSERT(false, "Http2Session::SetProxyConnectFailed()");
4019 : }
4020 :
4021 : bool
4022 0 : Http2Session::IsDone()
4023 : {
4024 0 : return !mStreamTransactionHash.Count();
4025 : }
4026 :
4027 : nsresult
4028 0 : Http2Session::Status()
4029 : {
4030 0 : MOZ_ASSERT(false, "Http2Session::Status()");
4031 : return NS_ERROR_UNEXPECTED;
4032 : }
4033 :
4034 : uint32_t
4035 0 : Http2Session::Caps()
4036 : {
4037 0 : MOZ_ASSERT(false, "Http2Session::Caps()");
4038 : return 0;
4039 : }
4040 :
4041 : void
4042 0 : Http2Session::SetDNSWasRefreshed()
4043 : {
4044 0 : MOZ_ASSERT(false, "Http2Session::SetDNSWasRefreshed()");
4045 : }
4046 :
4047 : nsHttpRequestHead *
4048 0 : Http2Session::RequestHead()
4049 : {
4050 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
4051 0 : MOZ_ASSERT(false,
4052 : "Http2Session::RequestHead() "
4053 : "should not be called after http/2 is setup");
4054 : return NULL;
4055 : }
4056 :
4057 : uint32_t
4058 0 : Http2Session::Http1xTransactionCount()
4059 : {
4060 0 : return 0;
4061 : }
4062 :
4063 : nsresult
4064 0 : Http2Session::TakeSubTransactions(
4065 : nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
4066 : {
4067 : // Generally this cannot be done with http/2 as transactions are
4068 : // started right away.
4069 :
4070 0 : LOG3(("Http2Session::TakeSubTransactions %p\n", this));
4071 :
4072 0 : if (mConcurrentHighWater > 0)
4073 0 : return NS_ERROR_ALREADY_OPENED;
4074 :
4075 0 : LOG3((" taking %d\n", mStreamTransactionHash.Count()));
4076 :
4077 0 : for (auto iter = mStreamTransactionHash.Iter(); !iter.Done(); iter.Next()) {
4078 0 : outTransactions.AppendElement(iter.Key());
4079 :
4080 : // Removing the stream from the hash will delete the stream and drop the
4081 : // transaction reference the hash held.
4082 0 : iter.Remove();
4083 : }
4084 0 : return NS_OK;
4085 : }
4086 :
4087 : //-----------------------------------------------------------------------------
4088 : // Pass through methods of nsAHttpConnection
4089 : //-----------------------------------------------------------------------------
4090 :
4091 : nsAHttpConnection *
4092 0 : Http2Session::Connection()
4093 : {
4094 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
4095 0 : return mConnection;
4096 : }
4097 :
4098 : nsresult
4099 0 : Http2Session::OnHeadersAvailable(nsAHttpTransaction *transaction,
4100 : nsHttpRequestHead *requestHead,
4101 : nsHttpResponseHead *responseHead, bool *reset)
4102 : {
4103 0 : return mConnection->OnHeadersAvailable(transaction,
4104 : requestHead,
4105 : responseHead,
4106 0 : reset);
4107 : }
4108 :
4109 : bool
4110 0 : Http2Session::IsReused()
4111 : {
4112 0 : return mConnection->IsReused();
4113 : }
4114 :
4115 : nsresult
4116 0 : Http2Session::PushBack(const char *buf, uint32_t len)
4117 : {
4118 0 : return mConnection->PushBack(buf, len);
4119 : }
4120 :
4121 : void
4122 0 : Http2Session::SendPing()
4123 : {
4124 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
4125 :
4126 0 : if (mPreviousUsed) {
4127 : // alredy in progress, get out
4128 0 : return;
4129 : }
4130 :
4131 0 : mPingSentEpoch = PR_IntervalNow();
4132 0 : if (!mPingSentEpoch) {
4133 0 : mPingSentEpoch = 1; // avoid the 0 sentinel value
4134 : }
4135 0 : if (!mPingThreshold ||
4136 0 : (mPingThreshold > gHttpHandler->NetworkChangedTimeout())) {
4137 0 : mPreviousPingThreshold = mPingThreshold;
4138 0 : mPreviousUsed = true;
4139 0 : mPingThreshold = gHttpHandler->NetworkChangedTimeout();
4140 : }
4141 0 : GeneratePing(false);
4142 0 : Unused << ResumeRecv();
4143 : }
4144 :
4145 : bool
4146 0 : Http2Session::TestOriginFrame(const nsACString &hostname, int32_t port)
4147 : {
4148 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
4149 0 : MOZ_ASSERT(mOriginFrameActivated);
4150 :
4151 0 : nsAutoCString key(hostname);
4152 0 : key.Append (':');
4153 0 : key.AppendInt(port);
4154 0 : bool rv = mOriginFrame.Get(key);
4155 0 : LOG3(("TestOriginFrame() hash.get %p %s %d\n", this, key.get(), rv));
4156 0 : if (!rv && ConnectionInfo()) {
4157 : // the SNI is also implicitly in this list, so consult that too
4158 0 : nsHttpConnectionInfo *ci = ConnectionInfo();
4159 0 : rv = nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort());
4160 0 : LOG3(("TestOriginFrame() %p sni test %d\n", this, rv));
4161 : }
4162 0 : return rv;
4163 : }
4164 :
4165 : bool
4166 0 : Http2Session::TestJoinConnection(const nsACString &hostname, int32_t port)
4167 : {
4168 0 : return RealJoinConnection(hostname, port, true);
4169 : }
4170 :
4171 : bool
4172 0 : Http2Session::JoinConnection(const nsACString &hostname, int32_t port)
4173 : {
4174 0 : return RealJoinConnection(hostname, port, false);
4175 : }
4176 :
4177 : bool
4178 0 : Http2Session::RealJoinConnection(const nsACString &hostname, int32_t port,
4179 : bool justKidding)
4180 : {
4181 0 : if (!mConnection || mClosed || mShouldGoAway) {
4182 0 : return false;
4183 : }
4184 :
4185 0 : nsHttpConnectionInfo *ci = ConnectionInfo();
4186 0 : if (nsCString(hostname).EqualsIgnoreCase(ci->Origin()) && (port == ci->OriginPort())) {
4187 0 : return true;
4188 : }
4189 :
4190 0 : if (!mReceivedSettings) {
4191 0 : return false;
4192 : }
4193 :
4194 0 : if (mOriginFrameActivated) {
4195 0 : bool originFrameResult = TestOriginFrame(hostname, port);
4196 0 : if (!originFrameResult) {
4197 0 : return false;
4198 : }
4199 : } else {
4200 0 : LOG3(("JoinConnection %p no origin frame check used.\n", this));
4201 : }
4202 :
4203 0 : nsAutoCString key(hostname);
4204 0 : key.Append(':');
4205 0 : key.Append(justKidding ? 'k' : '.');
4206 0 : key.AppendInt(port);
4207 : bool cachedResult;
4208 0 : if (mJoinConnectionCache.Get(key, &cachedResult)) {
4209 0 : LOG(("joinconnection [%p %s] %s result=%d cache\n",
4210 : this, ConnectionInfo()->HashKey().get(), key.get(),
4211 : cachedResult));
4212 0 : return cachedResult;
4213 : }
4214 :
4215 : nsresult rv;
4216 0 : bool isJoined = false;
4217 :
4218 0 : nsCOMPtr<nsISupports> securityInfo;
4219 0 : nsCOMPtr<nsISSLSocketControl> sslSocketControl;
4220 :
4221 0 : mConnection->GetSecurityInfo(getter_AddRefs(securityInfo));
4222 0 : sslSocketControl = do_QueryInterface(securityInfo, &rv);
4223 0 : if (NS_FAILED(rv) || !sslSocketControl) {
4224 0 : return false;
4225 : }
4226 :
4227 : // try all the coalescable versions we support.
4228 0 : const SpdyInformation *info = gHttpHandler->SpdyInfo();
4229 : static_assert(SpdyInformation::kCount == 1, "assume 1 alpn version");
4230 0 : bool joinedReturn = false;
4231 0 : if (info->ProtocolEnabled(0)) {
4232 0 : if (justKidding) {
4233 0 : rv = sslSocketControl->TestJoinConnection(info->VersionString[0],
4234 0 : hostname, port, &isJoined);
4235 : } else {
4236 0 : rv = sslSocketControl->JoinConnection(info->VersionString[0],
4237 0 : hostname, port, &isJoined);
4238 : }
4239 0 : if (NS_SUCCEEDED(rv) && isJoined) {
4240 0 : joinedReturn = true;
4241 : }
4242 : }
4243 :
4244 0 : LOG(("joinconnection [%p %s] %s result=%d lookup\n",
4245 : this, ConnectionInfo()->HashKey().get(), key.get(), joinedReturn));
4246 0 : mJoinConnectionCache.Put(key, joinedReturn);
4247 0 : if (!justKidding) {
4248 : // cache a kidding entry too as this one is good for both
4249 0 : nsAutoCString key2(hostname);
4250 0 : key2.Append(':');
4251 0 : key2.Append('k');
4252 0 : key2.AppendInt(port);
4253 0 : if (!mJoinConnectionCache.Get(key2)) {
4254 0 : mJoinConnectionCache.Put(key2, joinedReturn);
4255 : }
4256 : }
4257 0 : return joinedReturn;
4258 : }
4259 :
4260 : } // namespace net
4261 : } // namespace mozilla
|