1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/Http2Session.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3079 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set sw=2 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +// Log on level :5, instead of default :4. 1.14 +#undef LOG 1.15 +#define LOG(args) LOG5(args) 1.16 +#undef LOG_ENABLED 1.17 +#define LOG_ENABLED() LOG5_ENABLED() 1.18 + 1.19 +#include <algorithm> 1.20 + 1.21 +#include "Http2Session.h" 1.22 +#include "Http2Stream.h" 1.23 +#include "Http2Push.h" 1.24 + 1.25 +#include "mozilla/Telemetry.h" 1.26 +#include "mozilla/Preferences.h" 1.27 +#include "nsHttp.h" 1.28 +#include "nsHttpHandler.h" 1.29 +#include "nsHttpConnection.h" 1.30 +#include "nsILoadGroup.h" 1.31 +#include "nsISSLSocketControl.h" 1.32 +#include "nsISSLStatus.h" 1.33 +#include "nsISSLStatusProvider.h" 1.34 +#include "prprf.h" 1.35 +#include "prnetdb.h" 1.36 +#include "sslt.h" 1.37 + 1.38 +#ifdef DEBUG 1.39 +// defined by the socket transport service while active 1.40 +extern PRThread *gSocketThread; 1.41 +#endif 1.42 + 1.43 +namespace mozilla { 1.44 +namespace net { 1.45 + 1.46 +// Http2Session has multiple inheritance of things that implement 1.47 +// nsISupports, so this magic is taken from nsHttpPipeline that 1.48 +// implements some of the same abstract classes. 1.49 +NS_IMPL_ADDREF(Http2Session) 1.50 +NS_IMPL_RELEASE(Http2Session) 1.51 +NS_INTERFACE_MAP_BEGIN(Http2Session) 1.52 +NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection) 1.53 +NS_INTERFACE_MAP_END 1.54 + 1.55 +// "magic" refers to the string that preceeds HTTP/2 on the wire 1.56 +// to help find any intermediaries speaking an older version of HTTP 1.57 +const uint8_t Http2Session::kMagicHello[] = { 1.58 + 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 1.59 + 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 1.60 + 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a 1.61 +}; 1.62 + 1.63 +#define RETURN_SESSION_ERROR(o,x) \ 1.64 +do { \ 1.65 + (o)->mGoAwayReason = (x); \ 1.66 + return NS_ERROR_ILLEGAL_VALUE; \ 1.67 + } while (0) 1.68 + 1.69 +Http2Session::Http2Session(nsAHttpTransaction *aHttpTransaction, 1.70 + nsISocketTransport *aSocketTransport, 1.71 + int32_t firstPriority) 1.72 + : mSocketTransport(aSocketTransport), 1.73 + mSegmentReader(nullptr), 1.74 + mSegmentWriter(nullptr), 1.75 + mNextStreamID(3), // 1 is reserved for Updgrade handshakes 1.76 + mConcurrentHighWater(0), 1.77 + mDownstreamState(BUFFERING_OPENING_SETTINGS), 1.78 + mInputFrameBufferSize(kDefaultBufferSize), 1.79 + mInputFrameBufferUsed(0), 1.80 + mInputFrameFinal(false), 1.81 + mInputFrameDataStream(nullptr), 1.82 + mNeedsCleanup(nullptr), 1.83 + mDownstreamRstReason(NO_HTTP_ERROR), 1.84 + mExpectedHeaderID(0), 1.85 + mExpectedPushPromiseID(0), 1.86 + mContinuedPromiseStream(0), 1.87 + mShouldGoAway(false), 1.88 + mClosed(false), 1.89 + mCleanShutdown(false), 1.90 + mTLSProfileConfirmed(false), 1.91 + mGoAwayReason(NO_HTTP_ERROR), 1.92 + mGoAwayID(0), 1.93 + mOutgoingGoAwayID(0), 1.94 + mMaxConcurrent(kDefaultMaxConcurrent), 1.95 + mConcurrent(0), 1.96 + mServerPushedResources(0), 1.97 + mServerInitialStreamWindow(kDefaultRwin), 1.98 + mLocalSessionWindow(kDefaultRwin), 1.99 + mServerSessionWindow(kDefaultRwin), 1.100 + mOutputQueueSize(kDefaultQueueSize), 1.101 + mOutputQueueUsed(0), 1.102 + mOutputQueueSent(0), 1.103 + mLastReadEpoch(PR_IntervalNow()), 1.104 + mPingSentEpoch(0) 1.105 +{ 1.106 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.107 + 1.108 + static uint64_t sSerial; 1.109 + mSerial = ++sSerial; 1.110 + 1.111 + LOG3(("Http2Session::Http2Session %p transaction 1 = %p serial=0x%X\n", 1.112 + this, aHttpTransaction, mSerial)); 1.113 + 1.114 + mConnection = aHttpTransaction->Connection(); 1.115 + mInputFrameBuffer = new char[mInputFrameBufferSize]; 1.116 + mOutputQueueBuffer = new char[mOutputQueueSize]; 1.117 + mDecompressBuffer.SetCapacity(kDefaultBufferSize); 1.118 + mDecompressor.SetCompressor(&mCompressor); 1.119 + 1.120 + mPushAllowance = gHttpHandler->SpdyPushAllowance(); 1.121 + 1.122 + mSendingChunkSize = gHttpHandler->SpdySendingChunkSize(); 1.123 + SendHello(); 1.124 + 1.125 + if (!aHttpTransaction->IsNullTransaction()) 1.126 + AddStream(aHttpTransaction, firstPriority); 1.127 + mLastDataReadEpoch = mLastReadEpoch; 1.128 + 1.129 + mPingThreshold = gHttpHandler->SpdyPingThreshold(); 1.130 +} 1.131 + 1.132 +PLDHashOperator 1.133 +Http2Session::ShutdownEnumerator(nsAHttpTransaction *key, 1.134 + nsAutoPtr<Http2Stream> &stream, 1.135 + void *closure) 1.136 +{ 1.137 + Http2Session *self = static_cast<Http2Session *>(closure); 1.138 + 1.139 + // On a clean server hangup the server sets the GoAwayID to be the ID of 1.140 + // the last transaction it processed. If the ID of stream in the 1.141 + // local stream is greater than that it can safely be restarted because the 1.142 + // server guarantees it was not partially processed. Streams that have not 1.143 + // registered an ID haven't actually been sent yet so they can always be 1.144 + // restarted. 1.145 + if (self->mCleanShutdown && 1.146 + (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) { 1.147 + self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted 1.148 + } else { 1.149 + self->CloseStream(stream, NS_ERROR_ABORT); 1.150 + } 1.151 + 1.152 + return PL_DHASH_NEXT; 1.153 +} 1.154 + 1.155 +PLDHashOperator 1.156 +Http2Session::GoAwayEnumerator(nsAHttpTransaction *key, 1.157 + nsAutoPtr<Http2Stream> &stream, 1.158 + void *closure) 1.159 +{ 1.160 + Http2Session *self = static_cast<Http2Session *>(closure); 1.161 + 1.162 + // these streams were not processed by the server and can be restarted. 1.163 + // Do that after the enumerator completes to avoid the risk of 1.164 + // a restart event re-entrantly modifying this hash. Be sure not to restart 1.165 + // a pushed (even numbered) stream 1.166 + if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) || 1.167 + !stream->HasRegisteredID()) { 1.168 + self->mGoAwayStreamsToRestart.Push(stream); 1.169 + } 1.170 + 1.171 + return PL_DHASH_NEXT; 1.172 +} 1.173 + 1.174 +Http2Session::~Http2Session() 1.175 +{ 1.176 + LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X", 1.177 + this, mDownstreamState)); 1.178 + 1.179 + mStreamTransactionHash.Enumerate(ShutdownEnumerator, this); 1.180 + Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater); 1.181 + Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2); 1.182 + Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS, 1.183 + mServerPushedResources); 1.184 +} 1.185 + 1.186 +void 1.187 +Http2Session::LogIO(Http2Session *self, Http2Stream *stream, 1.188 + const char *label, 1.189 + const char *data, uint32_t datalen) 1.190 +{ 1.191 + if (!LOG4_ENABLED()) 1.192 + return; 1.193 + 1.194 + LOG4(("Http2Session::LogIO %p stream=%p id=0x%X [%s]", 1.195 + self, stream, stream ? stream->StreamID() : 0, label)); 1.196 + 1.197 + // Max line is (16 * 3) + 10(prefix) + newline + null 1.198 + char linebuf[128]; 1.199 + uint32_t index; 1.200 + char *line = linebuf; 1.201 + 1.202 + linebuf[127] = 0; 1.203 + 1.204 + for (index = 0; index < datalen; ++index) { 1.205 + if (!(index % 16)) { 1.206 + if (index) { 1.207 + *line = 0; 1.208 + LOG4(("%s", linebuf)); 1.209 + } 1.210 + line = linebuf; 1.211 + PR_snprintf(line, 128, "%08X: ", index); 1.212 + line += 10; 1.213 + } 1.214 + PR_snprintf(line, 128 - (line - linebuf), "%02X ", 1.215 + (reinterpret_cast<const uint8_t *>(data))[index]); 1.216 + line += 3; 1.217 + } 1.218 + if (index) { 1.219 + *line = 0; 1.220 + LOG4(("%s", linebuf)); 1.221 + } 1.222 +} 1.223 + 1.224 +typedef nsresult (*Http2ControlFx) (Http2Session *self); 1.225 +static Http2ControlFx sControlFunctions[] = { 1.226 + nullptr, // type 0 data is not a control function 1.227 + Http2Session::RecvHeaders, 1.228 + Http2Session::RecvPriority, 1.229 + Http2Session::RecvRstStream, 1.230 + Http2Session::RecvSettings, 1.231 + Http2Session::RecvPushPromise, 1.232 + Http2Session::RecvPing, 1.233 + Http2Session::RecvGoAway, 1.234 + Http2Session::RecvWindowUpdate, 1.235 + Http2Session::RecvContinuation 1.236 +}; 1.237 + 1.238 +bool 1.239 +Http2Session::RoomForMoreConcurrent() 1.240 +{ 1.241 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.242 + return (mConcurrent < mMaxConcurrent); 1.243 +} 1.244 + 1.245 +bool 1.246 +Http2Session::RoomForMoreStreams() 1.247 +{ 1.248 + if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID) 1.249 + return false; 1.250 + 1.251 + return !mShouldGoAway; 1.252 +} 1.253 + 1.254 +PRIntervalTime 1.255 +Http2Session::IdleTime() 1.256 +{ 1.257 + return PR_IntervalNow() - mLastDataReadEpoch; 1.258 +} 1.259 + 1.260 +uint32_t 1.261 +Http2Session::ReadTimeoutTick(PRIntervalTime now) 1.262 +{ 1.263 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.264 + 1.265 + LOG3(("Http2Session::ReadTimeoutTick %p delta since last read %ds\n", 1.266 + this, PR_IntervalToSeconds(now - mLastReadEpoch))); 1.267 + 1.268 + if (!mPingThreshold) 1.269 + return UINT32_MAX; 1.270 + 1.271 + if ((now - mLastReadEpoch) < mPingThreshold) { 1.272 + // recent activity means ping is not an issue 1.273 + if (mPingSentEpoch) 1.274 + mPingSentEpoch = 0; 1.275 + 1.276 + return PR_IntervalToSeconds(mPingThreshold) - 1.277 + PR_IntervalToSeconds(now - mLastReadEpoch); 1.278 + } 1.279 + 1.280 + if (mPingSentEpoch) { 1.281 + LOG3(("Http2Session::ReadTimeoutTick %p handle outstanding ping\n")); 1.282 + if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) { 1.283 + LOG3(("Http2Session::ReadTimeoutTick %p Ping Timer Exhaustion\n", this)); 1.284 + mPingSentEpoch = 0; 1.285 + Close(NS_ERROR_NET_TIMEOUT); 1.286 + return UINT32_MAX; 1.287 + } 1.288 + return 1; // run the tick aggressively while ping is outstanding 1.289 + } 1.290 + 1.291 + LOG3(("Http2Session::ReadTimeoutTick %p generating ping\n", this)); 1.292 + 1.293 + mPingSentEpoch = PR_IntervalNow(); 1.294 + if (!mPingSentEpoch) 1.295 + mPingSentEpoch = 1; // avoid the 0 sentinel value 1.296 + GeneratePing(false); 1.297 + ResumeRecv(); // read the ping reply 1.298 + 1.299 + // Check for orphaned push streams. This looks expensive, but generally the 1.300 + // list is empty. 1.301 + Http2PushedStream *deleteMe; 1.302 + TimeStamp timestampNow; 1.303 + do { 1.304 + deleteMe = nullptr; 1.305 + 1.306 + for (uint32_t index = mPushedStreams.Length(); 1.307 + index > 0 ; --index) { 1.308 + Http2PushedStream *pushedStream = mPushedStreams[index - 1]; 1.309 + 1.310 + if (timestampNow.IsNull()) 1.311 + timestampNow = TimeStamp::Now(); // lazy initializer 1.312 + 1.313 + // if stream finished, but is not connected, and its been like that for 1.314 + // long then cleanup the stream. 1.315 + if (pushedStream->IsOrphaned(timestampNow)) 1.316 + { 1.317 + LOG3(("Http2Session Timeout Pushed Stream %p 0x%X\n", 1.318 + this, pushedStream->StreamID())); 1.319 + deleteMe = pushedStream; 1.320 + break; // don't CleanupStream() while iterating this vector 1.321 + } 1.322 + } 1.323 + if (deleteMe) 1.324 + CleanupStream(deleteMe, NS_ERROR_ABORT, CANCEL_ERROR); 1.325 + 1.326 + } while (deleteMe); 1.327 + 1.328 + return 1; // run the tick aggressively while ping is outstanding 1.329 +} 1.330 + 1.331 +uint32_t 1.332 +Http2Session::RegisterStreamID(Http2Stream *stream, uint32_t aNewID) 1.333 +{ 1.334 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.335 + MOZ_ASSERT(mNextStreamID < 0xfffffff0, 1.336 + "should have stopped admitting streams"); 1.337 + MOZ_ASSERT(!(aNewID & 1), 1.338 + "0 for autoassign pull, otherwise explicit even push assignment"); 1.339 + 1.340 + if (!aNewID) { 1.341 + // auto generate a new pull stream ID 1.342 + aNewID = mNextStreamID; 1.343 + MOZ_ASSERT(aNewID & 1, "pull ID must be odd."); 1.344 + mNextStreamID += 2; 1.345 + } 1.346 + 1.347 + LOG3(("Http2Session::RegisterStreamID session=%p stream=%p id=0x%X " 1.348 + "concurrent=%d",this, stream, aNewID, mConcurrent)); 1.349 + 1.350 + // We've used up plenty of ID's on this session. Start 1.351 + // moving to a new one before there is a crunch involving 1.352 + // server push streams or concurrent non-registered submits 1.353 + if (aNewID >= kMaxStreamID) 1.354 + mShouldGoAway = true; 1.355 + 1.356 + // integrity check 1.357 + if (mStreamIDHash.Get(aNewID)) { 1.358 + LOG3((" New ID already present\n")); 1.359 + MOZ_ASSERT(false, "New ID already present in mStreamIDHash"); 1.360 + mShouldGoAway = true; 1.361 + return kDeadStreamID; 1.362 + } 1.363 + 1.364 + mStreamIDHash.Put(aNewID, stream); 1.365 + return aNewID; 1.366 +} 1.367 + 1.368 +bool 1.369 +Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction, 1.370 + int32_t aPriority) 1.371 +{ 1.372 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.373 + 1.374 + // integrity check 1.375 + if (mStreamTransactionHash.Get(aHttpTransaction)) { 1.376 + LOG3((" New transaction already present\n")); 1.377 + MOZ_ASSERT(false, "AddStream duplicate transaction pointer"); 1.378 + return false; 1.379 + } 1.380 + 1.381 + aHttpTransaction->SetConnection(this); 1.382 + Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority); 1.383 + 1.384 + LOG3(("Http2Session::AddStream session=%p stream=%p NextID=0x%X (tentative)", 1.385 + this, stream, mNextStreamID)); 1.386 + 1.387 + mStreamTransactionHash.Put(aHttpTransaction, stream); 1.388 + 1.389 + if (RoomForMoreConcurrent()) { 1.390 + LOG3(("Http2Session::AddStream %p stream %p activated immediately.", 1.391 + this, stream)); 1.392 + ActivateStream(stream); 1.393 + } else { 1.394 + LOG3(("Http2Session::AddStream %p stream %p queued.", this, stream)); 1.395 + mQueuedStreams.Push(stream); 1.396 + } 1.397 + 1.398 + if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) { 1.399 + LOG3(("Http2Session::AddStream %p transaction %p forces keep-alive off.\n", 1.400 + this, aHttpTransaction)); 1.401 + DontReuse(); 1.402 + } 1.403 + 1.404 + return true; 1.405 +} 1.406 + 1.407 +void 1.408 +Http2Session::ActivateStream(Http2Stream *stream) 1.409 +{ 1.410 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.411 + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), 1.412 + "Do not activate pushed streams"); 1.413 + 1.414 + MOZ_ASSERT(!stream->CountAsActive()); 1.415 + stream->SetCountAsActive(true); 1.416 + ++mConcurrent; 1.417 + 1.418 + if (mConcurrent > mConcurrentHighWater) 1.419 + mConcurrentHighWater = mConcurrent; 1.420 + LOG3(("Http2Session::AddStream %p activating stream %p Currently %d " 1.421 + "streams in session, high water mark is %d", 1.422 + this, stream, mConcurrent, mConcurrentHighWater)); 1.423 + 1.424 + mReadyForWrite.Push(stream); 1.425 + SetWriteCallbacks(); 1.426 + 1.427 + // Kick off the headers transmit without waiting for the poll loop 1.428 + // This won't work for stream id=1 because there is no segment reader 1.429 + // yet. 1.430 + if (mSegmentReader) { 1.431 + uint32_t countRead; 1.432 + ReadSegments(nullptr, kDefaultBufferSize, &countRead); 1.433 + } 1.434 +} 1.435 + 1.436 +void 1.437 +Http2Session::ProcessPending() 1.438 +{ 1.439 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.440 + 1.441 + while (RoomForMoreConcurrent()) { 1.442 + Http2Stream *stream = static_cast<Http2Stream *>(mQueuedStreams.PopFront()); 1.443 + if (!stream) 1.444 + return; 1.445 + LOG3(("Http2Session::ProcessPending %p stream %p activated from queue.", 1.446 + this, stream)); 1.447 + ActivateStream(stream); 1.448 + } 1.449 +} 1.450 + 1.451 +nsresult 1.452 +Http2Session::NetworkRead(nsAHttpSegmentWriter *writer, char *buf, 1.453 + uint32_t count, uint32_t *countWritten) 1.454 +{ 1.455 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.456 + 1.457 + if (!count) { 1.458 + *countWritten = 0; 1.459 + return NS_OK; 1.460 + } 1.461 + 1.462 + nsresult rv = writer->OnWriteSegment(buf, count, countWritten); 1.463 + if (NS_SUCCEEDED(rv) && *countWritten > 0) 1.464 + mLastReadEpoch = PR_IntervalNow(); 1.465 + return rv; 1.466 +} 1.467 + 1.468 +void 1.469 +Http2Session::SetWriteCallbacks() 1.470 +{ 1.471 + if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed)) 1.472 + mConnection->ResumeSend(); 1.473 +} 1.474 + 1.475 +void 1.476 +Http2Session::RealignOutputQueue() 1.477 +{ 1.478 + mOutputQueueUsed -= mOutputQueueSent; 1.479 + memmove(mOutputQueueBuffer.get(), 1.480 + mOutputQueueBuffer.get() + mOutputQueueSent, 1.481 + mOutputQueueUsed); 1.482 + mOutputQueueSent = 0; 1.483 +} 1.484 + 1.485 +void 1.486 +Http2Session::FlushOutputQueue() 1.487 +{ 1.488 + if (!mSegmentReader || !mOutputQueueUsed) 1.489 + return; 1.490 + 1.491 + nsresult rv; 1.492 + uint32_t countRead; 1.493 + uint32_t avail = mOutputQueueUsed - mOutputQueueSent; 1.494 + 1.495 + rv = mSegmentReader-> 1.496 + OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail, 1.497 + &countRead); 1.498 + LOG3(("Http2Session::FlushOutputQueue %p sz=%d rv=%x actual=%d", 1.499 + this, avail, rv, countRead)); 1.500 + 1.501 + // Dont worry about errors on write, we will pick this up as a read error too 1.502 + if (NS_FAILED(rv)) 1.503 + return; 1.504 + 1.505 + if (countRead == avail) { 1.506 + mOutputQueueUsed = 0; 1.507 + mOutputQueueSent = 0; 1.508 + return; 1.509 + } 1.510 + 1.511 + mOutputQueueSent += countRead; 1.512 + 1.513 + // If the output queue is close to filling up and we have sent out a good 1.514 + // chunk of data from the beginning then realign it. 1.515 + 1.516 + if ((mOutputQueueSent >= kQueueMinimumCleanup) && 1.517 + ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) { 1.518 + RealignOutputQueue(); 1.519 + } 1.520 +} 1.521 + 1.522 +void 1.523 +Http2Session::DontReuse() 1.524 +{ 1.525 + mShouldGoAway = true; 1.526 + if (!mStreamTransactionHash.Count()) 1.527 + Close(NS_OK); 1.528 +} 1.529 + 1.530 +uint32_t 1.531 +Http2Session::GetWriteQueueSize() 1.532 +{ 1.533 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.534 + 1.535 + return mReadyForWrite.GetSize(); 1.536 +} 1.537 + 1.538 +void 1.539 +Http2Session::ChangeDownstreamState(enum internalStateType newState) 1.540 +{ 1.541 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.542 + 1.543 + LOG3(("Http2Stream::ChangeDownstreamState() %p from %X to %X", 1.544 + this, mDownstreamState, newState)); 1.545 + mDownstreamState = newState; 1.546 +} 1.547 + 1.548 +void 1.549 +Http2Session::ResetDownstreamState() 1.550 +{ 1.551 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.552 + 1.553 + LOG3(("Http2Stream::ResetDownstreamState() %p", this)); 1.554 + ChangeDownstreamState(BUFFERING_FRAME_HEADER); 1.555 + 1.556 + if (mInputFrameFinal && mInputFrameDataStream) { 1.557 + mInputFrameFinal = false; 1.558 + LOG3((" SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID())); 1.559 + mInputFrameDataStream->SetRecvdFin(true); 1.560 + MaybeDecrementConcurrent(mInputFrameDataStream); 1.561 + } 1.562 + mInputFrameBufferUsed = 0; 1.563 + mInputFrameDataStream = nullptr; 1.564 +} 1.565 + 1.566 +template<typename T> void 1.567 +Http2Session::EnsureBuffer(nsAutoArrayPtr<T> &buf, uint32_t newSize, 1.568 + uint32_t preserve, uint32_t &objSize) 1.569 +{ 1.570 + if (objSize >= newSize) 1.571 + return; 1.572 + 1.573 + // Leave a little slop on the new allocation - add 2KB to 1.574 + // what we need and then round the result up to a 4KB (page) 1.575 + // boundary. 1.576 + 1.577 + objSize = (newSize + 2048 + 4095) & ~4095; 1.578 + 1.579 + static_assert(sizeof(T) == 1, "sizeof(T) must be 1"); 1.580 + nsAutoArrayPtr<T> tmp(new T[objSize]); 1.581 + memcpy(tmp, buf, preserve); 1.582 + buf = tmp; 1.583 +} 1.584 + 1.585 +// Instantiate supported templates explicitly. 1.586 +template void 1.587 +Http2Session::EnsureBuffer(nsAutoArrayPtr<char> &buf, uint32_t newSize, 1.588 + uint32_t preserve, uint32_t &objSize); 1.589 + 1.590 +template void 1.591 +Http2Session::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, uint32_t newSize, 1.592 + uint32_t preserve, uint32_t &objSize); 1.593 + 1.594 +// call with data length (i.e. 0 for 0 data bytes - ignore 8 byte header) 1.595 +// dest must have 8 bytes of allocated space 1.596 +template<typename charType> void 1.597 +Http2Session::CreateFrameHeader(charType dest, uint16_t frameLength, 1.598 + uint8_t frameType, uint8_t frameFlags, 1.599 + uint32_t streamID) 1.600 +{ 1.601 + MOZ_ASSERT(frameLength <= kMaxFrameData, "framelength too large"); 1.602 + MOZ_ASSERT(!(streamID & 0x80000000)); 1.603 + 1.604 + frameLength = PR_htons(frameLength); 1.605 + streamID = PR_htonl(streamID); 1.606 + 1.607 + memcpy(dest, &frameLength, 2); 1.608 + dest[2] = frameType; 1.609 + dest[3] = frameFlags; 1.610 + memcpy(dest + 4, &streamID, 4); 1.611 +} 1.612 + 1.613 +char * 1.614 +Http2Session::EnsureOutputBuffer(uint32_t spaceNeeded) 1.615 +{ 1.616 + // this is an infallible allocation (if an allocation is 1.617 + // needed, which is probably isn't) 1.618 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + spaceNeeded, 1.619 + mOutputQueueUsed, mOutputQueueSize); 1.620 + return mOutputQueueBuffer.get() + mOutputQueueUsed; 1.621 +} 1.622 + 1.623 +template void 1.624 +Http2Session::CreateFrameHeader(char *dest, uint16_t frameLength, 1.625 + uint8_t frameType, uint8_t frameFlags, 1.626 + uint32_t streamID); 1.627 + 1.628 +template void 1.629 +Http2Session::CreateFrameHeader(uint8_t *dest, uint16_t frameLength, 1.630 + uint8_t frameType, uint8_t frameFlags, 1.631 + uint32_t streamID); 1.632 + 1.633 +void 1.634 +Http2Session::MaybeDecrementConcurrent(Http2Stream *aStream) 1.635 +{ 1.636 + LOG3(("MaybeDecrementConcurrent %p id=0x%X concurrent=%d active=%d\n", 1.637 + this, aStream->StreamID(), mConcurrent, aStream->CountAsActive())); 1.638 + 1.639 + if (!aStream->CountAsActive()) 1.640 + return; 1.641 + 1.642 + MOZ_ASSERT(mConcurrent); 1.643 + aStream->SetCountAsActive(false); 1.644 + --mConcurrent; 1.645 + ProcessPending(); 1.646 +} 1.647 + 1.648 +// Need to decompress some data in order to keep the compression 1.649 +// context correct, but we really don't care what the result is 1.650 +nsresult 1.651 +Http2Session::UncompressAndDiscard() 1.652 +{ 1.653 + nsresult rv; 1.654 + nsAutoCString trash; 1.655 + 1.656 + rv = mDecompressor.DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(mDecompressBuffer.BeginReading()), 1.657 + mDecompressBuffer.Length(), trash); 1.658 + mDecompressBuffer.Truncate(); 1.659 + if (NS_FAILED(rv)) { 1.660 + LOG3(("Http2Session::UncompressAndDiscard %p Compression Error\n", 1.661 + this)); 1.662 + mGoAwayReason = COMPRESSION_ERROR; 1.663 + return rv; 1.664 + } 1.665 + return NS_OK; 1.666 +} 1.667 + 1.668 +void 1.669 +Http2Session::GeneratePing(bool isAck) 1.670 +{ 1.671 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.672 + LOG3(("Http2Session::GeneratePing %p isAck=%d\n", this, isAck)); 1.673 + 1.674 + char *packet = EnsureOutputBuffer(16); 1.675 + mOutputQueueUsed += 16; 1.676 + 1.677 + if (isAck) { 1.678 + CreateFrameHeader(packet, 8, FRAME_TYPE_PING, kFlag_ACK, 0); 1.679 + memcpy(packet + 8, mInputFrameBuffer.get() + 8, 8); 1.680 + } else { 1.681 + CreateFrameHeader(packet, 8, FRAME_TYPE_PING, 0, 0); 1.682 + memset(packet + 8, 0, 8); 1.683 + } 1.684 + 1.685 + LogIO(this, nullptr, "Generate Ping", packet, 16); 1.686 + FlushOutputQueue(); 1.687 +} 1.688 + 1.689 +void 1.690 +Http2Session::GenerateSettingsAck() 1.691 +{ 1.692 + // need to generate ack of this settings frame 1.693 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.694 + LOG3(("Http2Session::GenerateSettingsAck %p\n", this)); 1.695 + 1.696 + char *packet = EnsureOutputBuffer(8); 1.697 + mOutputQueueUsed += 8; 1.698 + CreateFrameHeader(packet, 0, FRAME_TYPE_SETTINGS, kFlag_ACK, 0); 1.699 + LogIO(this, nullptr, "Generate Settings ACK", packet, 8); 1.700 + FlushOutputQueue(); 1.701 +} 1.702 + 1.703 +void 1.704 +Http2Session::GeneratePriority(uint32_t aID, uint32_t aPriority) 1.705 +{ 1.706 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.707 + LOG3(("Http2Session::GeneratePriority %p %X %X\n", 1.708 + this, aID, aPriority)); 1.709 + 1.710 + char *packet = EnsureOutputBuffer(12); 1.711 + mOutputQueueUsed += 12; 1.712 + 1.713 + CreateFrameHeader(packet, 4, FRAME_TYPE_PRIORITY, 0, aID); 1.714 + aPriority = PR_htonl(aPriority); 1.715 + memcpy(packet + 8, &aPriority, 4); 1.716 + LogIO(this, nullptr, "Generate Priority", packet, 12); 1.717 + FlushOutputQueue(); 1.718 +} 1.719 + 1.720 +void 1.721 +Http2Session::GenerateRstStream(uint32_t aStatusCode, uint32_t aID) 1.722 +{ 1.723 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.724 + 1.725 + // make sure we don't do this twice for the same stream (at least if we 1.726 + // have a stream entry for it) 1.727 + Http2Stream *stream = mStreamIDHash.Get(aID); 1.728 + if (stream) { 1.729 + if (stream->SentReset()) 1.730 + return; 1.731 + stream->SetSentReset(true); 1.732 + } 1.733 + 1.734 + LOG3(("Http2Session::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode)); 1.735 + 1.736 + char *packet = EnsureOutputBuffer(12); 1.737 + mOutputQueueUsed += 12; 1.738 + CreateFrameHeader(packet, 4, FRAME_TYPE_RST_STREAM, 0, aID); 1.739 + 1.740 + aStatusCode = PR_htonl(aStatusCode); 1.741 + memcpy(packet + 8, &aStatusCode, 4); 1.742 + 1.743 + LogIO(this, nullptr, "Generate Reset", packet, 12); 1.744 + FlushOutputQueue(); 1.745 +} 1.746 + 1.747 +void 1.748 +Http2Session::GenerateGoAway(uint32_t aStatusCode) 1.749 +{ 1.750 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.751 + LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode)); 1.752 + 1.753 + char *packet = EnsureOutputBuffer(16); 1.754 + mOutputQueueUsed += 16; 1.755 + 1.756 + memset(packet + 8, 0, 8); 1.757 + CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0); 1.758 + 1.759 + // last-good-stream-id are bytes 8-11 reflecting pushes 1.760 + uint32_t goAway = PR_htonl(mOutgoingGoAwayID); 1.761 + memcpy (packet + 7, &goAway, 4); 1.762 + 1.763 + // bytes 12-15 are the status code. 1.764 + aStatusCode = PR_htonl(aStatusCode); 1.765 + memcpy(packet + 12, &aStatusCode, 4); 1.766 + 1.767 + LogIO(this, nullptr, "Generate GoAway", packet, 16); 1.768 + FlushOutputQueue(); 1.769 +} 1.770 + 1.771 +// The Hello is comprised of 24 octets of magic, which are designed to 1.772 +// flush out silent but broken intermediaries, followed by a settings 1.773 +// frame which sets a small flow control window for pushes and a 1.774 +// window update frame which creates a large session flow control window 1.775 +void 1.776 +Http2Session::SendHello() 1.777 +{ 1.778 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.779 + LOG3(("Http2Session::SendHello %p\n", this)); 1.780 + 1.781 + // sized for magic + 2 settings and a session window update to follow 1.782 + // 24 magic, 23 for settings (8 header + 3 settings @5), 12 for window update 1.783 + static const uint32_t maxSettings = 3; 1.784 + static const uint32_t maxDataLen = 24 + 8 + maxSettings * 5 + 12; 1.785 + char *packet = EnsureOutputBuffer(maxDataLen); 1.786 + memcpy(packet, kMagicHello, 24); 1.787 + mOutputQueueUsed += 24; 1.788 + LogIO(this, nullptr, "Magic Connection Header", packet, 24); 1.789 + 1.790 + packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.791 + memset(packet, 0, maxDataLen - 24); 1.792 + 1.793 + // frame header will be filled in after we know how long the frame is 1.794 + uint8_t numberOfEntries = 0; 1.795 + 1.796 + // entries need to be listed in order by ID 1.797 + // 1st entry is bytes 8 to 12 1.798 + // 2nd entry is bytes 13 to 17 1.799 + // 3rd entry is bytes 18 to 22 1.800 + 1.801 + if (!gHttpHandler->AllowPush()) { 1.802 + // If we don't support push then set MAX_CONCURRENT to 0 and also 1.803 + // set ENABLE_PUSH to 0 1.804 + packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_ENABLE_PUSH; 1.805 + // The value portion of the setting pair is already initialized to 0 1.806 + numberOfEntries++; 1.807 + 1.808 + packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT; 1.809 + // The value portion of the setting pair is already initialized to 0 1.810 + numberOfEntries++; 1.811 + } 1.812 + 1.813 + // Advertise the Push RWIN for the session, and on each new pull stream 1.814 + // send a window update with END_FLOW_CONTROL 1.815 + packet[8 + 5 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW; 1.816 + uint32_t rwin = PR_htonl(mPushAllowance); 1.817 + memcpy(packet + 9 + 5 * numberOfEntries, &rwin, 4); 1.818 + numberOfEntries++; 1.819 + 1.820 + MOZ_ASSERT(numberOfEntries <= maxSettings); 1.821 + uint32_t dataLen = 5 * numberOfEntries; 1.822 + CreateFrameHeader(packet, dataLen, FRAME_TYPE_SETTINGS, 0, 0); 1.823 + mOutputQueueUsed += 8 + dataLen; 1.824 + 1.825 + LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen); 1.826 + 1.827 + // now bump the local session window from 64KB 1.828 + uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin; 1.829 + if (kDefaultRwin >= ASpdySession::kInitialRwin) 1.830 + goto sendHello_complete; 1.831 + 1.832 + // send a window update for the session (Stream 0) for something large 1.833 + sessionWindowBump = PR_htonl(sessionWindowBump); 1.834 + mLocalSessionWindow = ASpdySession::kInitialRwin; 1.835 + 1.836 + packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.837 + CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0); 1.838 + mOutputQueueUsed += 12; 1.839 + memcpy(packet + 8, &sessionWindowBump, 4); 1.840 + 1.841 + LOG3(("Session Window increase at start of session %p %u\n", 1.842 + this, PR_ntohl(sessionWindowBump))); 1.843 + LogIO(this, nullptr, "Session Window Bump ", packet, 12); 1.844 + 1.845 +sendHello_complete: 1.846 + FlushOutputQueue(); 1.847 +} 1.848 + 1.849 +// perform a bunch of integrity checks on the stream. 1.850 +// returns true if passed, false (plus LOG and ABORT) if failed. 1.851 +bool 1.852 +Http2Session::VerifyStream(Http2Stream *aStream, uint32_t aOptionalID = 0) 1.853 +{ 1.854 + // This is annoying, but at least it is O(1) 1.855 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.856 + 1.857 +#ifndef DEBUG 1.858 + // Only do the real verification in debug builds 1.859 + return true; 1.860 +#endif 1.861 + 1.862 + if (!aStream) 1.863 + return true; 1.864 + 1.865 + uint32_t test = 0; 1.866 + 1.867 + do { 1.868 + if (aStream->StreamID() == kDeadStreamID) 1.869 + break; 1.870 + 1.871 + nsAHttpTransaction *trans = aStream->Transaction(); 1.872 + 1.873 + test++; 1.874 + if (!trans) 1.875 + break; 1.876 + 1.877 + test++; 1.878 + if (mStreamTransactionHash.Get(trans) != aStream) 1.879 + break; 1.880 + 1.881 + if (aStream->StreamID()) { 1.882 + Http2Stream *idStream = mStreamIDHash.Get(aStream->StreamID()); 1.883 + 1.884 + test++; 1.885 + if (idStream != aStream) 1.886 + break; 1.887 + 1.888 + if (aOptionalID) { 1.889 + test++; 1.890 + if (idStream->StreamID() != aOptionalID) 1.891 + break; 1.892 + } 1.893 + } 1.894 + 1.895 + // tests passed 1.896 + return true; 1.897 + } while (0); 1.898 + 1.899 + LOG3(("Http2Session %p VerifyStream Failure %p stream->id=0x%X " 1.900 + "optionalID=0x%X trans=%p test=%d\n", 1.901 + this, aStream, aStream->StreamID(), 1.902 + aOptionalID, aStream->Transaction(), test)); 1.903 + 1.904 + MOZ_ASSERT(false, "VerifyStream"); 1.905 + return false; 1.906 +} 1.907 + 1.908 +void 1.909 +Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult, 1.910 + errorType aResetCode) 1.911 +{ 1.912 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.913 + LOG3(("Http2Session::CleanupStream %p %p 0x%X %X\n", 1.914 + this, aStream, aStream ? aStream->StreamID() : 0, aResult)); 1.915 + if (!aStream) { 1.916 + return; 1.917 + } 1.918 + 1.919 + Http2PushedStream *pushSource = nullptr; 1.920 + 1.921 + if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) { 1.922 + LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID())); 1.923 + return; 1.924 + } 1.925 + 1.926 + if (!VerifyStream(aStream)) { 1.927 + LOG3(("Http2Session::CleanupStream failed to verify stream\n")); 1.928 + return; 1.929 + } 1.930 + 1.931 + pushSource = aStream->PushSource(); 1.932 + 1.933 + if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID()) { 1.934 + LOG3(("Stream had not processed recv FIN, sending RST code %X\n", 1.935 + aResetCode)); 1.936 + GenerateRstStream(aResetCode, aStream->StreamID()); 1.937 + } 1.938 + 1.939 + CloseStream(aStream, aResult); 1.940 + 1.941 + // Remove the stream from the ID hash table and, if an even id, the pushed 1.942 + // table too. 1.943 + uint32_t id = aStream->StreamID(); 1.944 + if (id > 0) { 1.945 + mStreamIDHash.Remove(id); 1.946 + if (!(id & 1)) 1.947 + mPushedStreams.RemoveElement(aStream); 1.948 + } 1.949 + 1.950 + RemoveStreamFromQueues(aStream); 1.951 + 1.952 + // removing from the stream transaction hash will 1.953 + // delete the Http2Stream and drop the reference to 1.954 + // its transaction 1.955 + mStreamTransactionHash.Remove(aStream->Transaction()); 1.956 + 1.957 + if (mShouldGoAway && !mStreamTransactionHash.Count()) 1.958 + Close(NS_OK); 1.959 + 1.960 + if (pushSource) { 1.961 + pushSource->SetDeferCleanupOnSuccess(false); 1.962 + CleanupStream(pushSource, aResult, aResetCode); 1.963 + } 1.964 +} 1.965 + 1.966 +static void RemoveStreamFromQueue(Http2Stream *aStream, nsDeque &queue) 1.967 +{ 1.968 + uint32_t size = queue.GetSize(); 1.969 + for (uint32_t count = 0; count < size; ++count) { 1.970 + Http2Stream *stream = static_cast<Http2Stream *>(queue.PopFront()); 1.971 + if (stream != aStream) 1.972 + queue.Push(stream); 1.973 + } 1.974 +} 1.975 + 1.976 +void 1.977 +Http2Session::RemoveStreamFromQueues(Http2Stream *aStream) 1.978 +{ 1.979 + RemoveStreamFromQueue(aStream, mReadyForWrite); 1.980 + RemoveStreamFromQueue(aStream, mQueuedStreams); 1.981 + RemoveStreamFromQueue(aStream, mReadyForRead); 1.982 +} 1.983 + 1.984 +void 1.985 +Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult) 1.986 +{ 1.987 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.988 + LOG3(("Http2Session::CloseStream %p %p 0x%x %X\n", 1.989 + this, aStream, aStream->StreamID(), aResult)); 1.990 + 1.991 + MaybeDecrementConcurrent(aStream); 1.992 + 1.993 + // Check if partial frame reader 1.994 + if (aStream == mInputFrameDataStream) { 1.995 + LOG3(("Stream had active partial read frame on close")); 1.996 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.997 + mInputFrameDataStream = nullptr; 1.998 + } 1.999 + 1.1000 + RemoveStreamFromQueues(aStream); 1.1001 + 1.1002 + // Send the stream the close() indication 1.1003 + aStream->Close(aResult); 1.1004 +} 1.1005 + 1.1006 +nsresult 1.1007 +Http2Session::SetInputFrameDataStream(uint32_t streamID) 1.1008 +{ 1.1009 + mInputFrameDataStream = mStreamIDHash.Get(streamID); 1.1010 + if (VerifyStream(mInputFrameDataStream, streamID)) 1.1011 + return NS_OK; 1.1012 + 1.1013 + LOG3(("Http2Session::SetInputFrameDataStream failed to verify 0x%X\n", 1.1014 + streamID)); 1.1015 + mInputFrameDataStream = nullptr; 1.1016 + return NS_ERROR_UNEXPECTED; 1.1017 +} 1.1018 + 1.1019 +nsresult 1.1020 +Http2Session::ParsePadding(uint8_t &paddingControlBytes, uint16_t &paddingLength) 1.1021 +{ 1.1022 + if (mInputFrameFlags & kFlag_PAD_HIGH) { 1.1023 + uint8_t paddingHighValue = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + 8); 1.1024 + paddingLength = static_cast<uint16_t>(paddingHighValue) * 256; 1.1025 + ++paddingControlBytes; 1.1026 + } 1.1027 + 1.1028 + if (mInputFrameFlags & kFlag_PAD_LOW) { 1.1029 + uint8_t paddingLowValue = *reinterpret_cast<uint8_t *>(mInputFrameBuffer + 8 + paddingControlBytes); 1.1030 + paddingLength += paddingLowValue; 1.1031 + ++paddingControlBytes; 1.1032 + } 1.1033 + 1.1034 + if (paddingLength > mInputFrameDataSize) { 1.1035 + // This is fatal to the session 1.1036 + LOG3(("Http2Session::RecvHeaders %p stream 0x%x PROTOCOL_ERROR " 1.1037 + "paddingLength %d > frame size %d\n", 1.1038 + this, mInputFrameID, paddingLength, mInputFrameDataSize)); 1.1039 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.1040 + } 1.1041 + 1.1042 + return NS_OK; 1.1043 +} 1.1044 + 1.1045 +nsresult 1.1046 +Http2Session::RecvHeaders(Http2Session *self) 1.1047 +{ 1.1048 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_HEADERS); 1.1049 + 1.1050 + // If this doesn't have END_HEADERS set on it then require the next 1.1051 + // frame to be HEADERS of the same ID 1.1052 + bool endHeadersFlag = self->mInputFrameFlags & kFlag_END_HEADERS; 1.1053 + 1.1054 + if (endHeadersFlag) 1.1055 + self->mExpectedHeaderID = 0; 1.1056 + else 1.1057 + self->mExpectedHeaderID = self->mInputFrameID; 1.1058 + 1.1059 + uint32_t priorityLen = (self->mInputFrameFlags & kFlag_PRIORITY) ? 4 : 0; 1.1060 + self->SetInputFrameDataStream(self->mInputFrameID); 1.1061 + 1.1062 + // Find out how much padding this frame has, so we can only extract the real 1.1063 + // header data from the frame. 1.1064 + uint16_t paddingLength = 0; 1.1065 + uint8_t paddingControlBytes = 0; 1.1066 + 1.1067 + nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength); 1.1068 + if (NS_FAILED(rv)) { 1.1069 + return rv; 1.1070 + } 1.1071 + 1.1072 + LOG3(("Http2Session::RecvHeaders %p stream 0x%X priorityLen=%d stream=%p " 1.1073 + "end_stream=%d end_headers=%d priority_flag=%d paddingLength=%d " 1.1074 + "pad_high_flag=%d pad_low_flag=%d\n", 1.1075 + self, self->mInputFrameID, priorityLen, self->mInputFrameDataStream, 1.1076 + self->mInputFrameFlags & kFlag_END_STREAM, 1.1077 + self->mInputFrameFlags & kFlag_END_HEADERS, 1.1078 + self->mInputFrameFlags & kFlag_PRIORITY, 1.1079 + paddingLength, 1.1080 + self->mInputFrameFlags & kFlag_PAD_HIGH, 1.1081 + self->mInputFrameFlags & kFlag_PAD_LOW)); 1.1082 + 1.1083 + if (!self->mInputFrameDataStream) { 1.1084 + // Cannot find stream. We can continue the session, but we need to 1.1085 + // uncompress the header block to maintain the correct compression context 1.1086 + 1.1087 + LOG3(("Http2Session::RecvHeaders %p lookup mInputFrameID stream " 1.1088 + "0x%X failed. NextStreamID = 0x%X\n", 1.1089 + self, self->mInputFrameID, self->mNextStreamID)); 1.1090 + 1.1091 + if (self->mInputFrameID >= self->mNextStreamID) 1.1092 + self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID); 1.1093 + 1.1094 + self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen, 1.1095 + self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength); 1.1096 + 1.1097 + if (self->mInputFrameFlags & kFlag_END_HEADERS) { 1.1098 + rv = self->UncompressAndDiscard(); 1.1099 + if (NS_FAILED(rv)) { 1.1100 + LOG3(("Http2Session::RecvHeaders uncompress failed\n")); 1.1101 + // this is fatal to the session 1.1102 + self->mGoAwayReason = COMPRESSION_ERROR; 1.1103 + return rv; 1.1104 + } 1.1105 + } 1.1106 + 1.1107 + self->ResetDownstreamState(); 1.1108 + return NS_OK; 1.1109 + } 1.1110 + 1.1111 + // queue up any compression bytes 1.1112 + self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + priorityLen, 1.1113 + self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength); 1.1114 + 1.1115 + self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize); 1.1116 + self->mLastDataReadEpoch = self->mLastReadEpoch; 1.1117 + 1.1118 + if (!endHeadersFlag) { // more are coming - don't process yet 1.1119 + self->ResetDownstreamState(); 1.1120 + return NS_OK; 1.1121 + } 1.1122 + 1.1123 + rv = self->ResponseHeadersComplete(); 1.1124 + if (rv == NS_ERROR_ILLEGAL_VALUE) { 1.1125 + LOG3(("Http2Session::RecvHeaders %p PROTOCOL_ERROR detected stream 0x%X\n", 1.1126 + self, self->mInputFrameID)); 1.1127 + self->CleanupStream(self->mInputFrameDataStream, rv, PROTOCOL_ERROR); 1.1128 + self->ResetDownstreamState(); 1.1129 + rv = NS_OK; 1.1130 + } 1.1131 + return rv; 1.1132 +} 1.1133 + 1.1134 +// ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream 1.1135 +// should be reset with a PROTOCOL_ERROR, NS_OK when the response headers were 1.1136 +// fine, and any other error is fatal to the session. 1.1137 +nsresult 1.1138 +Http2Session::ResponseHeadersComplete() 1.1139 +{ 1.1140 + LOG3(("Http2Session::ResponseHeadersComplete %p for 0x%X fin=%d", 1.1141 + this, mInputFrameDataStream->StreamID(), mInputFrameFinal)); 1.1142 + 1.1143 + // only do this once, afterwards ignore trailers 1.1144 + if (mInputFrameDataStream->AllHeadersReceived()) 1.1145 + return NS_OK; 1.1146 + mInputFrameDataStream->SetAllHeadersReceived(true); 1.1147 + 1.1148 + // The stream needs to see flattened http headers 1.1149 + // Uncompressed http/2 format headers currently live in 1.1150 + // Http2Stream::mDecompressBuffer - convert that to HTTP format in 1.1151 + // mFlatHTTPResponseHeaders via ConvertHeaders() 1.1152 + 1.1153 + mFlatHTTPResponseHeadersOut = 0; 1.1154 + nsresult rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor, 1.1155 + mDecompressBuffer, 1.1156 + mFlatHTTPResponseHeaders); 1.1157 + if (NS_FAILED(rv)) 1.1158 + return rv; 1.1159 + 1.1160 + ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS); 1.1161 + return NS_OK; 1.1162 +} 1.1163 + 1.1164 +nsresult 1.1165 +Http2Session::RecvPriority(Http2Session *self) 1.1166 +{ 1.1167 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PRIORITY); 1.1168 + 1.1169 + if (self->mInputFrameDataSize != 4) { 1.1170 + LOG3(("Http2Session::RecvPriority %p wrong length data=%d\n", 1.1171 + self, self->mInputFrameDataSize)); 1.1172 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1173 + } 1.1174 + 1.1175 + if (!self->mInputFrameID) { 1.1176 + LOG3(("Http2Session::RecvPriority %p stream ID of 0.\n", self)); 1.1177 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1178 + } 1.1179 + 1.1180 + uint32_t newPriority = 1.1181 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]); 1.1182 + newPriority &= 0x7fffffff; 1.1183 + 1.1184 + nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID); 1.1185 + if (NS_FAILED(rv)) 1.1186 + return rv; 1.1187 + 1.1188 + if (self->mInputFrameDataStream) 1.1189 + self->mInputFrameDataStream->SetPriority(newPriority); 1.1190 + self->ResetDownstreamState(); 1.1191 + return NS_OK; 1.1192 +} 1.1193 + 1.1194 +nsresult 1.1195 +Http2Session::RecvRstStream(Http2Session *self) 1.1196 +{ 1.1197 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_RST_STREAM); 1.1198 + 1.1199 + if (self->mInputFrameDataSize != 4) { 1.1200 + LOG3(("Http2Session::RecvRstStream %p RST_STREAM wrong length data=%d", 1.1201 + self, self->mInputFrameDataSize)); 1.1202 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1203 + } 1.1204 + 1.1205 + if (!self->mInputFrameID) { 1.1206 + LOG3(("Http2Session::RecvRstStream %p stream ID of 0.\n", self)); 1.1207 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1208 + } 1.1209 + 1.1210 + self->mDownstreamRstReason = 1.1211 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]); 1.1212 + 1.1213 + LOG3(("Http2Session::RecvRstStream %p RST_STREAM Reason Code %u ID %x\n", 1.1214 + self, self->mDownstreamRstReason, self->mInputFrameID)); 1.1215 + 1.1216 + self->SetInputFrameDataStream(self->mInputFrameID); 1.1217 + if (!self->mInputFrameDataStream) { 1.1218 + // if we can't find the stream just ignore it (4.2 closed) 1.1219 + self->ResetDownstreamState(); 1.1220 + return NS_OK; 1.1221 + } 1.1222 + 1.1223 + self->mInputFrameDataStream->SetRecvdReset(true); 1.1224 + self->MaybeDecrementConcurrent(self->mInputFrameDataStream); 1.1225 + self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM); 1.1226 + return NS_OK; 1.1227 +} 1.1228 + 1.1229 +PLDHashOperator 1.1230 +Http2Session::UpdateServerRwinEnumerator(nsAHttpTransaction *key, 1.1231 + nsAutoPtr<Http2Stream> &stream, 1.1232 + void *closure) 1.1233 +{ 1.1234 + int32_t delta = *(static_cast<int32_t *>(closure)); 1.1235 + stream->UpdateServerReceiveWindow(delta); 1.1236 + return PL_DHASH_NEXT; 1.1237 +} 1.1238 + 1.1239 +nsresult 1.1240 +Http2Session::RecvSettings(Http2Session *self) 1.1241 +{ 1.1242 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_SETTINGS); 1.1243 + 1.1244 + if (self->mInputFrameID) { 1.1245 + LOG3(("Http2Session::RecvSettings %p needs stream ID of 0. 0x%X\n", 1.1246 + self, self->mInputFrameID)); 1.1247 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1248 + } 1.1249 + 1.1250 + if (self->mInputFrameDataSize % 5) { 1.1251 + // Number of Settings is determined by dividing by each 5 byte setting 1.1252 + // entry. So the payload must be a multiple of 5. 1.1253 + LOG3(("Http2Session::RecvSettings %p SETTINGS wrong length data=%d", 1.1254 + self, self->mInputFrameDataSize)); 1.1255 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1256 + } 1.1257 + 1.1258 + uint32_t numEntries = self->mInputFrameDataSize / 5; 1.1259 + LOG3(("Http2Session::RecvSettings %p SETTINGS Control Frame " 1.1260 + "with %d entries ack=%X", self, numEntries, 1.1261 + self->mInputFrameFlags & kFlag_ACK)); 1.1262 + 1.1263 + if ((self->mInputFrameFlags & kFlag_ACK) && self->mInputFrameDataSize) { 1.1264 + LOG3(("Http2Session::RecvSettings %p ACK with non zero payload is err\n")); 1.1265 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1266 + } 1.1267 + 1.1268 + for (uint32_t index = 0; index < numEntries; ++index) { 1.1269 + uint8_t *setting = reinterpret_cast<uint8_t *> 1.1270 + (self->mInputFrameBuffer.get()) + 8 + index * 5; 1.1271 + 1.1272 + uint8_t id = setting[0]; 1.1273 + uint32_t value = PR_ntohl(*reinterpret_cast<uint32_t *>(setting + 1)); 1.1274 + LOG3(("Settings ID %d, Value %d", id, value)); 1.1275 + 1.1276 + switch (id) 1.1277 + { 1.1278 + case SETTINGS_TYPE_HEADER_TABLE_SIZE: 1.1279 + LOG3(("Compression header table setting received: %d\n", value)); 1.1280 + self->mCompressor.SetMaxBufferSize(value); 1.1281 + break; 1.1282 + 1.1283 + case SETTINGS_TYPE_ENABLE_PUSH: 1.1284 + LOG3(("Client received an ENABLE Push SETTING. Odd.\n")); 1.1285 + // nop 1.1286 + break; 1.1287 + 1.1288 + case SETTINGS_TYPE_MAX_CONCURRENT: 1.1289 + self->mMaxConcurrent = value; 1.1290 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); 1.1291 + break; 1.1292 + 1.1293 + case SETTINGS_TYPE_INITIAL_WINDOW: 1.1294 + { 1.1295 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10); 1.1296 + int32_t delta = value - self->mServerInitialStreamWindow; 1.1297 + self->mServerInitialStreamWindow = value; 1.1298 + 1.1299 + // SETTINGS only adjusts stream windows. Leave the sesison window alone. 1.1300 + // we need to add the delta to all open streams (delta can be negative) 1.1301 + self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator, 1.1302 + &delta); 1.1303 + } 1.1304 + break; 1.1305 + 1.1306 + default: 1.1307 + break; 1.1308 + } 1.1309 + } 1.1310 + 1.1311 + self->ResetDownstreamState(); 1.1312 + 1.1313 + if (!(self->mInputFrameFlags & kFlag_ACK)) 1.1314 + self->GenerateSettingsAck(); 1.1315 + 1.1316 + return NS_OK; 1.1317 +} 1.1318 + 1.1319 +nsresult 1.1320 +Http2Session::RecvPushPromise(Http2Session *self) 1.1321 +{ 1.1322 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PUSH_PROMISE); 1.1323 + 1.1324 + // Find out how much padding this frame has, so we can only extract the real 1.1325 + // header data from the frame. 1.1326 + uint16_t paddingLength = 0; 1.1327 + uint8_t paddingControlBytes = 0; 1.1328 + // TODO - will need to change this once PUSH_PROMISE allows padding 1.1329 + // (post-draft10) 1.1330 + // Right now, only CONTINUATION frames can have padding, and 1.1331 + // mExpectedPushPromiseID being set indicates that we're actually processing a 1.1332 + // CONTINUATION frame. 1.1333 + if (self->mExpectedPushPromiseID) { 1.1334 + nsresult rv = self->ParsePadding(paddingControlBytes, paddingLength); 1.1335 + if (NS_FAILED(rv)) { 1.1336 + return rv; 1.1337 + } 1.1338 + } 1.1339 + 1.1340 + // If this doesn't have END_PUSH_PROMISE set on it then require the next 1.1341 + // frame to be PUSH_PROMISE of the same ID 1.1342 + uint32_t promiseLen; 1.1343 + uint32_t promisedID; 1.1344 + 1.1345 + if (self->mExpectedPushPromiseID) { 1.1346 + promiseLen = 0; // really a continuation frame 1.1347 + promisedID = self->mContinuedPromiseStream; 1.1348 + } else { 1.1349 + // TODO - will need to handle padding here when getting the promisedID, once 1.1350 + // PUSH_PROMISE allows padding (post-draft10) 1.1351 + promiseLen = 4; 1.1352 + promisedID = 1.1353 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]); 1.1354 + promisedID &= 0x7fffffff; 1.1355 + } 1.1356 + 1.1357 + uint32_t associatedID = self->mInputFrameID; 1.1358 + 1.1359 + if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) { 1.1360 + self->mExpectedPushPromiseID = 0; 1.1361 + self->mContinuedPromiseStream = 0; 1.1362 + } else { 1.1363 + self->mExpectedPushPromiseID = self->mInputFrameID; 1.1364 + self->mContinuedPromiseStream = promisedID; 1.1365 + } 1.1366 + 1.1367 + if (paddingLength > self->mInputFrameDataSize) { 1.1368 + // This is fatal to the session 1.1369 + LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X " 1.1370 + "PROTOCOL_ERROR paddingLength %d > frame size %d\n", 1.1371 + self, promisedID, associatedID, paddingLength, 1.1372 + self->mInputFrameDataSize)); 1.1373 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1374 + } 1.1375 + 1.1376 + LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X " 1.1377 + "paddingLength %d pad_high_flag %d pad_low_flag %d.\n", 1.1378 + self, promisedID, associatedID, paddingLength, 1.1379 + self->mInputFrameFlags & kFlag_PAD_HIGH, 1.1380 + self->mInputFrameFlags & kFlag_PAD_LOW)); 1.1381 + 1.1382 + if (!associatedID || !promisedID || (promisedID & 1)) { 1.1383 + LOG3(("Http2Session::RecvPushPromise %p ID invalid.\n", self)); 1.1384 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1385 + } 1.1386 + 1.1387 + // confirm associated-to 1.1388 + nsresult rv = self->SetInputFrameDataStream(associatedID); 1.1389 + if (NS_FAILED(rv)) 1.1390 + return rv; 1.1391 + 1.1392 + Http2Stream *associatedStream = self->mInputFrameDataStream; 1.1393 + ++(self->mServerPushedResources); 1.1394 + 1.1395 + // Anytime we start using the high bit of stream ID (either client or server) 1.1396 + // begin to migrate to a new session. 1.1397 + if (promisedID >= kMaxStreamID) 1.1398 + self->mShouldGoAway = true; 1.1399 + 1.1400 + bool resetStream = true; 1.1401 + SpdyPushCache *cache = nullptr; 1.1402 + 1.1403 + if (self->mShouldGoAway) { 1.1404 + LOG3(("Http2Session::RecvPushPromise %p push while in GoAway " 1.1405 + "mode refused.\n", self)); 1.1406 + self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); 1.1407 + } else if (!gHttpHandler->AllowPush()) { 1.1408 + // MAX_CONCURRENT_STREAMS of 0 in settings disabled push 1.1409 + LOG3(("Http2Session::RecvPushPromise Push Recevied when Disabled\n")); 1.1410 + self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); 1.1411 + } else if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) { 1.1412 + LOG3(("Http2Session::RecvPushPromise no support for multi frame push\n")); 1.1413 + self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); 1.1414 + } else if (!associatedStream) { 1.1415 + LOG3(("Http2Session::RecvPushPromise %p lookup associated ID failed.\n", self)); 1.1416 + self->GenerateRstStream(PROTOCOL_ERROR, promisedID); 1.1417 + } else { 1.1418 + nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo(); 1.1419 + if (loadGroupCI) { 1.1420 + loadGroupCI->GetSpdyPushCache(&cache); 1.1421 + if (!cache) { 1.1422 + cache = new SpdyPushCache(); 1.1423 + if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) { 1.1424 + delete cache; 1.1425 + cache = nullptr; 1.1426 + } 1.1427 + } 1.1428 + } 1.1429 + if (!cache) { 1.1430 + // this is unexpected, but we can handle it just by refusing the push 1.1431 + LOG3(("Http2Session::RecvPushPromise Push Recevied without loadgroup cache\n")); 1.1432 + self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); 1.1433 + } else { 1.1434 + resetStream = false; 1.1435 + } 1.1436 + } 1.1437 + 1.1438 + if (resetStream) { 1.1439 + // Need to decompress the headers even though we aren't using them yet in 1.1440 + // order to keep the compression context consistent for other frames 1.1441 + self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + paddingControlBytes + promiseLen, 1.1442 + self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength); 1.1443 + if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) { 1.1444 + rv = self->UncompressAndDiscard(); 1.1445 + if (NS_FAILED(rv)) { 1.1446 + LOG3(("Http2Session::RecvPushPromise uncompress failed\n")); 1.1447 + self->mGoAwayReason = COMPRESSION_ERROR; 1.1448 + return rv; 1.1449 + } 1.1450 + } 1.1451 + self->ResetDownstreamState(); 1.1452 + return NS_OK; 1.1453 + } 1.1454 + 1.1455 + // Create the buffering transaction and push stream 1.1456 + nsRefPtr<Http2PushTransactionBuffer> transactionBuffer = 1.1457 + new Http2PushTransactionBuffer(); 1.1458 + transactionBuffer->SetConnection(self); 1.1459 + Http2PushedStream *pushedStream = 1.1460 + new Http2PushedStream(transactionBuffer, self, 1.1461 + associatedStream, promisedID); 1.1462 + 1.1463 + // Ownership of the pushed stream is by the transaction hash, just as it 1.1464 + // is for a client initiated stream. Errors that aren't fatal to the 1.1465 + // whole session must call cleanupStream() after this point in order 1.1466 + // to remove the stream from that hash. 1.1467 + self->mStreamTransactionHash.Put(transactionBuffer, pushedStream); 1.1468 + self->mPushedStreams.AppendElement(pushedStream); 1.1469 + 1.1470 + self->mDecompressBuffer.Append(self->mInputFrameBuffer + 8 + promiseLen, 1.1471 + self->mInputFrameDataSize - promiseLen); 1.1472 + 1.1473 + nsAutoCString requestHeaders; 1.1474 + rv = pushedStream->ConvertPushHeaders(&self->mDecompressor, 1.1475 + self->mDecompressBuffer, requestHeaders); 1.1476 + 1.1477 + if (rv == NS_ERROR_NOT_IMPLEMENTED) { 1.1478 + LOG3(("Http2Session::PushPromise Semantics not Implemented\n")); 1.1479 + self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID); 1.1480 + return NS_OK; 1.1481 + } 1.1482 + 1.1483 + if (NS_FAILED(rv)) 1.1484 + return rv; 1.1485 + 1.1486 + if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) { 1.1487 + LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n")); 1.1488 + self->mGoAwayReason = INTERNAL_ERROR; 1.1489 + return NS_ERROR_FAILURE; 1.1490 + } 1.1491 + 1.1492 + if (promisedID > self->mOutgoingGoAwayID) 1.1493 + self->mOutgoingGoAwayID = promisedID; 1.1494 + 1.1495 + // Fake the request side of the pushed HTTP transaction. Sets up hash 1.1496 + // key and origin 1.1497 + uint32_t notUsed; 1.1498 + pushedStream->ReadSegments(nullptr, 1, ¬Used); 1.1499 + 1.1500 + nsAutoCString key; 1.1501 + if (!pushedStream->GetHashKey(key)) { 1.1502 + LOG3(("Http2Session::RecvPushPromise one of :authority :scheme :path missing from push\n")); 1.1503 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, PROTOCOL_ERROR); 1.1504 + self->ResetDownstreamState(); 1.1505 + return NS_OK; 1.1506 + } 1.1507 + 1.1508 + if (!associatedStream->Origin().Equals(pushedStream->Origin())) { 1.1509 + LOG3(("Http2Session::RecvPushPromise pushed stream mismatched origin\n")); 1.1510 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR); 1.1511 + self->ResetDownstreamState(); 1.1512 + return NS_OK; 1.1513 + } 1.1514 + 1.1515 + if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) { 1.1516 + LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n")); 1.1517 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR); 1.1518 + self->ResetDownstreamState(); 1.1519 + return NS_OK; 1.1520 + } 1.1521 + 1.1522 + static_assert(Http2Stream::kWorstPriority >= 0, 1.1523 + "kWorstPriority out of range"); 1.1524 + uint32_t unsignedPriority = static_cast<uint32_t>(Http2Stream::kWorstPriority); 1.1525 + pushedStream->SetPriority(unsignedPriority); 1.1526 + self->GeneratePriority(promisedID, unsignedPriority); 1.1527 + self->ResetDownstreamState(); 1.1528 + return NS_OK; 1.1529 +} 1.1530 + 1.1531 +nsresult 1.1532 +Http2Session::RecvPing(Http2Session *self) 1.1533 +{ 1.1534 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING); 1.1535 + 1.1536 + LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self, 1.1537 + self->mInputFrameFlags)); 1.1538 + 1.1539 + if (self->mInputFrameDataSize != 8) { 1.1540 + LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d", 1.1541 + self, self->mInputFrameDataSize)); 1.1542 + RETURN_SESSION_ERROR(self, FRAME_SIZE_ERROR); 1.1543 + } 1.1544 + 1.1545 + if (self->mInputFrameID) { 1.1546 + LOG3(("Http2Session::RecvPing %p PING needs stream ID of 0. 0x%X\n", 1.1547 + self, self->mInputFrameID)); 1.1548 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1549 + } 1.1550 + 1.1551 + if (self->mInputFrameFlags & kFlag_ACK) { 1.1552 + // presumably a reply to our timeout ping.. don't reply to it 1.1553 + self->mPingSentEpoch = 0; 1.1554 + } else { 1.1555 + // reply with a ack'd ping 1.1556 + self->GeneratePing(true); 1.1557 + } 1.1558 + 1.1559 + self->ResetDownstreamState(); 1.1560 + return NS_OK; 1.1561 +} 1.1562 + 1.1563 +nsresult 1.1564 +Http2Session::RecvGoAway(Http2Session *self) 1.1565 +{ 1.1566 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_GOAWAY); 1.1567 + 1.1568 + if (self->mInputFrameDataSize < 8) { 1.1569 + // data > 8 is an opaque token that we can't interpret. NSPR Logs will 1.1570 + // have the hex of all packets so there is no point in separately logging. 1.1571 + LOG3(("Http2Session::RecvGoAway %p GOAWAY had wrong amount of data %d", 1.1572 + self, self->mInputFrameDataSize)); 1.1573 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1574 + } 1.1575 + 1.1576 + if (self->mInputFrameID) { 1.1577 + LOG3(("Http2Session::RecvGoAway %p GOAWAY had non zero stream ID 0x%X\n", 1.1578 + self, self->mInputFrameID)); 1.1579 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1580 + } 1.1581 + 1.1582 + self->mShouldGoAway = true; 1.1583 + self->mGoAwayID = 1.1584 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]); 1.1585 + self->mGoAwayID &= 0x7fffffff; 1.1586 + self->mCleanShutdown = true; 1.1587 + uint32_t statusCode = 1.1588 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]); 1.1589 + 1.1590 + // Find streams greater than the last-good ID and mark them for deletion 1.1591 + // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The 1.1592 + // underlying transaction can be restarted. 1.1593 + self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self); 1.1594 + 1.1595 + // Process the streams marked for deletion and restart. 1.1596 + uint32_t size = self->mGoAwayStreamsToRestart.GetSize(); 1.1597 + for (uint32_t count = 0; count < size; ++count) { 1.1598 + Http2Stream *stream = 1.1599 + static_cast<Http2Stream *>(self->mGoAwayStreamsToRestart.PopFront()); 1.1600 + 1.1601 + self->CloseStream(stream, NS_ERROR_NET_RESET); 1.1602 + if (stream->HasRegisteredID()) 1.1603 + self->mStreamIDHash.Remove(stream->StreamID()); 1.1604 + self->mStreamTransactionHash.Remove(stream->Transaction()); 1.1605 + } 1.1606 + 1.1607 + // Queued streams can also be deleted from this session and restarted 1.1608 + // in another one. (they were never sent on the network so they implicitly 1.1609 + // are not covered by the last-good id. 1.1610 + size = self->mQueuedStreams.GetSize(); 1.1611 + for (uint32_t count = 0; count < size; ++count) { 1.1612 + Http2Stream *stream = 1.1613 + static_cast<Http2Stream *>(self->mQueuedStreams.PopFront()); 1.1614 + self->CloseStream(stream, NS_ERROR_NET_RESET); 1.1615 + self->mStreamTransactionHash.Remove(stream->Transaction()); 1.1616 + } 1.1617 + 1.1618 + LOG3(("Http2Session::RecvGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X " 1.1619 + "live streams=%d\n", self, self->mGoAwayID, statusCode, 1.1620 + self->mStreamTransactionHash.Count())); 1.1621 + 1.1622 + self->ResetDownstreamState(); 1.1623 + return NS_OK; 1.1624 +} 1.1625 + 1.1626 +PLDHashOperator 1.1627 +Http2Session::RestartBlockedOnRwinEnumerator(nsAHttpTransaction *key, 1.1628 + nsAutoPtr<Http2Stream> &stream, 1.1629 + void *closure) 1.1630 +{ 1.1631 + Http2Session *self = static_cast<Http2Session *>(closure); 1.1632 + MOZ_ASSERT(self->mServerSessionWindow > 0); 1.1633 + 1.1634 + if (!stream->BlockedOnRwin() || stream->ServerReceiveWindow() <= 0) 1.1635 + return PL_DHASH_NEXT; 1.1636 + 1.1637 + self->mReadyForWrite.Push(stream); 1.1638 + self->SetWriteCallbacks(); 1.1639 + return PL_DHASH_NEXT; 1.1640 +} 1.1641 + 1.1642 +nsresult 1.1643 +Http2Session::RecvWindowUpdate(Http2Session *self) 1.1644 +{ 1.1645 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_WINDOW_UPDATE); 1.1646 + 1.1647 + if (self->mInputFrameDataSize != 4) { 1.1648 + LOG3(("Http2Session::RecvWindowUpdate %p Window Update wrong length %d\n", 1.1649 + self, self->mInputFrameDataSize)); 1.1650 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1651 + } 1.1652 + 1.1653 + uint32_t delta = 1.1654 + PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]); 1.1655 + delta &= 0x7fffffff; 1.1656 + 1.1657 + LOG3(("Http2Session::RecvWindowUpdate %p len=%d Stream 0x%X.\n", 1.1658 + self, delta, self->mInputFrameID)); 1.1659 + 1.1660 + if (self->mInputFrameID) { // stream window 1.1661 + nsresult rv = self->SetInputFrameDataStream(self->mInputFrameID); 1.1662 + if (NS_FAILED(rv)) 1.1663 + return rv; 1.1664 + 1.1665 + if (!self->mInputFrameDataStream) { 1.1666 + LOG3(("Http2Session::RecvWindowUpdate %p lookup streamID 0x%X failed.\n", 1.1667 + self, self->mInputFrameID)); 1.1668 + // only resest the session if the ID is one we haven't ever opened 1.1669 + if (self->mInputFrameID >= self->mNextStreamID) 1.1670 + self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID); 1.1671 + self->ResetDownstreamState(); 1.1672 + return NS_OK; 1.1673 + } 1.1674 + 1.1675 + int64_t oldRemoteWindow = self->mInputFrameDataStream->ServerReceiveWindow(); 1.1676 + self->mInputFrameDataStream->UpdateServerReceiveWindow(delta); 1.1677 + if (self->mInputFrameDataStream->ServerReceiveWindow() >= 0x80000000) { 1.1678 + // a window cannot reach 2^31 and be in compliance. Our calculations 1.1679 + // are 64 bit safe though. 1.1680 + LOG3(("Http2Session::RecvWindowUpdate %p stream window " 1.1681 + "exceeds 2^31 - 1\n", self)); 1.1682 + self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE, 1.1683 + FLOW_CONTROL_ERROR); 1.1684 + self->ResetDownstreamState(); 1.1685 + return NS_OK; 1.1686 + } 1.1687 + 1.1688 + LOG3(("Http2Session::RecvWindowUpdate %p stream 0x%X window " 1.1689 + "%d increased by %d now %d.\n", self, self->mInputFrameID, 1.1690 + oldRemoteWindow, delta, oldRemoteWindow + delta)); 1.1691 + 1.1692 + } else { // session window update 1.1693 + int64_t oldRemoteWindow = self->mServerSessionWindow; 1.1694 + self->mServerSessionWindow += delta; 1.1695 + 1.1696 + if (self->mServerSessionWindow >= 0x80000000) { 1.1697 + // a window cannot reach 2^31 and be in compliance. Our calculations 1.1698 + // are 64 bit safe though. 1.1699 + LOG3(("Http2Session::RecvWindowUpdate %p session window " 1.1700 + "exceeds 2^31 - 1\n", self)); 1.1701 + RETURN_SESSION_ERROR(self, FLOW_CONTROL_ERROR); 1.1702 + } 1.1703 + 1.1704 + if ((oldRemoteWindow <= 0) && (self->mServerSessionWindow > 0)) { 1.1705 + LOG3(("Http2Session::RecvWindowUpdate %p restart session window\n", 1.1706 + self)); 1.1707 + self->mStreamTransactionHash.Enumerate(RestartBlockedOnRwinEnumerator, self); 1.1708 + } 1.1709 + LOG3(("Http2Session::RecvWindowUpdate %p session window " 1.1710 + "%d increased by %d now %d.\n", self, 1.1711 + oldRemoteWindow, delta, oldRemoteWindow + delta)); 1.1712 + } 1.1713 + 1.1714 + self->ResetDownstreamState(); 1.1715 + return NS_OK; 1.1716 +} 1.1717 + 1.1718 +nsresult 1.1719 +Http2Session::RecvContinuation(Http2Session *self) 1.1720 +{ 1.1721 + MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_CONTINUATION); 1.1722 + MOZ_ASSERT(self->mInputFrameID); 1.1723 + MOZ_ASSERT(self->mExpectedPushPromiseID || self->mExpectedHeaderID); 1.1724 + MOZ_ASSERT(!(self->mExpectedPushPromiseID && self->mExpectedHeaderID)); 1.1725 + 1.1726 + LOG3(("Http2Session::RecvContinuation %p Flags 0x%X id 0x%X " 1.1727 + "promise id 0x%X header id 0x%X\n", 1.1728 + self, self->mInputFrameFlags, self->mInputFrameID, 1.1729 + self->mExpectedPushPromiseID, self->mExpectedHeaderID)); 1.1730 + 1.1731 + self->SetInputFrameDataStream(self->mInputFrameID); 1.1732 + 1.1733 + if (!self->mInputFrameDataStream) { 1.1734 + LOG3(("Http2Session::RecvContination stream ID 0x%X not found.", 1.1735 + self->mInputFrameID)); 1.1736 + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); 1.1737 + } 1.1738 + 1.1739 + // continued headers 1.1740 + if (self->mExpectedHeaderID) { 1.1741 + self->mInputFrameFlags &= ~kFlag_PRIORITY; 1.1742 + return RecvHeaders(self); 1.1743 + } 1.1744 + 1.1745 + // continued push promise 1.1746 + if (self->mInputFrameFlags & kFlag_END_HEADERS) { 1.1747 + self->mInputFrameFlags &= ~kFlag_END_HEADERS; 1.1748 + self->mInputFrameFlags |= kFlag_END_PUSH_PROMISE; 1.1749 + } 1.1750 + return RecvPushPromise(self); 1.1751 +} 1.1752 + 1.1753 +//----------------------------------------------------------------------------- 1.1754 +// nsAHttpTransaction. It is expected that nsHttpConnection is the caller 1.1755 +// of these methods 1.1756 +//----------------------------------------------------------------------------- 1.1757 + 1.1758 +void 1.1759 +Http2Session::OnTransportStatus(nsITransport* aTransport, 1.1760 + nsresult aStatus, uint64_t aProgress) 1.1761 +{ 1.1762 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1763 + 1.1764 + switch (aStatus) { 1.1765 + // These should appear only once, deliver to the first 1.1766 + // transaction on the session. 1.1767 + case NS_NET_STATUS_RESOLVING_HOST: 1.1768 + case NS_NET_STATUS_RESOLVED_HOST: 1.1769 + case NS_NET_STATUS_CONNECTING_TO: 1.1770 + case NS_NET_STATUS_CONNECTED_TO: 1.1771 + { 1.1772 + Http2Stream *target = mStreamIDHash.Get(1); 1.1773 + nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr; 1.1774 + if (transaction) 1.1775 + transaction->OnTransportStatus(aTransport, aStatus, aProgress); 1.1776 + break; 1.1777 + } 1.1778 + 1.1779 + default: 1.1780 + // The other transport events are ignored here because there is no good 1.1781 + // way to map them to the right transaction in http/2. Instead, the events 1.1782 + // are generated again from the http/2 code and passed directly to the 1.1783 + // correct transaction. 1.1784 + 1.1785 + // NS_NET_STATUS_SENDING_TO: 1.1786 + // This is generated by the socket transport when (part) of 1.1787 + // a transaction is written out 1.1788 + // 1.1789 + // There is no good way to map it to the right transaction in http/2, 1.1790 + // so it is ignored here and generated separately when the request 1.1791 + // is sent from Http2Stream::TransmitFrame 1.1792 + 1.1793 + // NS_NET_STATUS_WAITING_FOR: 1.1794 + // Created by nsHttpConnection when the request has been totally sent. 1.1795 + // There is no good way to map it to the right transaction in http/2, 1.1796 + // so it is ignored here and generated separately when the same 1.1797 + // condition is complete in Http2Stream when there is no more 1.1798 + // request body left to be transmitted. 1.1799 + 1.1800 + // NS_NET_STATUS_RECEIVING_FROM 1.1801 + // Generated in session whenever we read a data frame or a HEADERS 1.1802 + // that can be attributed to a particular stream/transaction 1.1803 + 1.1804 + break; 1.1805 + } 1.1806 +} 1.1807 + 1.1808 +// ReadSegments() is used to write data to the network. Generally, HTTP 1.1809 +// request data is pulled from the approriate transaction and 1.1810 +// converted to http/2 data. Sometimes control data like window-update are 1.1811 +// generated instead. 1.1812 + 1.1813 +nsresult 1.1814 +Http2Session::ReadSegments(nsAHttpSegmentReader *reader, 1.1815 + uint32_t count, uint32_t *countRead) 1.1816 +{ 1.1817 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1818 + 1.1819 + MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader), 1.1820 + "Inconsistent Write Function Callback"); 1.1821 + 1.1822 + nsresult rv = ConfirmTLSProfile(); 1.1823 + if (NS_FAILED(rv)) 1.1824 + return rv; 1.1825 + 1.1826 + if (reader) 1.1827 + mSegmentReader = reader; 1.1828 + 1.1829 + *countRead = 0; 1.1830 + 1.1831 + LOG3(("Http2Session::ReadSegments %p", this)); 1.1832 + 1.1833 + Http2Stream *stream = static_cast<Http2Stream *>(mReadyForWrite.PopFront()); 1.1834 + if (!stream) { 1.1835 + LOG3(("Http2Session %p could not identify a stream to write; suspending.", 1.1836 + this)); 1.1837 + FlushOutputQueue(); 1.1838 + SetWriteCallbacks(); 1.1839 + return NS_BASE_STREAM_WOULD_BLOCK; 1.1840 + } 1.1841 + 1.1842 + LOG3(("Http2Session %p will write from Http2Stream %p 0x%X " 1.1843 + "block-input=%d block-output=%d\n", this, stream, stream->StreamID(), 1.1844 + stream->RequestBlockedOnRead(), stream->BlockedOnRwin())); 1.1845 + 1.1846 + rv = stream->ReadSegments(this, count, countRead); 1.1847 + 1.1848 + // Not every permutation of stream->ReadSegents produces data (and therefore 1.1849 + // tries to flush the output queue) - SENDING_FIN_STREAM can be an example 1.1850 + // of that. But we might still have old data buffered that would be good 1.1851 + // to flush. 1.1852 + FlushOutputQueue(); 1.1853 + 1.1854 + // Allow new server reads - that might be data or control information 1.1855 + // (e.g. window updates or http replies) that are responses to these writes 1.1856 + ResumeRecv(); 1.1857 + 1.1858 + if (stream->RequestBlockedOnRead()) { 1.1859 + 1.1860 + // We are blocked waiting for input - either more http headers or 1.1861 + // any request body data. When more data from the request stream 1.1862 + // becomes available the httptransaction will call conn->ResumeSend(). 1.1863 + 1.1864 + LOG3(("Http2Session::ReadSegments %p dealing with block on read", this)); 1.1865 + 1.1866 + // call readsegments again if there are other streams ready 1.1867 + // to run in this session 1.1868 + if (GetWriteQueueSize()) { 1.1869 + rv = NS_OK; 1.1870 + } else { 1.1871 + rv = NS_BASE_STREAM_WOULD_BLOCK; 1.1872 + } 1.1873 + SetWriteCallbacks(); 1.1874 + return rv; 1.1875 + } 1.1876 + 1.1877 + if (NS_FAILED(rv)) { 1.1878 + LOG3(("Http2Session::ReadSegments %p returning FAIL code %X", 1.1879 + this, rv)); 1.1880 + if (rv != NS_BASE_STREAM_WOULD_BLOCK) 1.1881 + CleanupStream(stream, rv, CANCEL_ERROR); 1.1882 + return rv; 1.1883 + } 1.1884 + 1.1885 + if (*countRead > 0) { 1.1886 + LOG3(("Http2Session::ReadSegments %p stream=%p countread=%d", 1.1887 + this, stream, *countRead)); 1.1888 + mReadyForWrite.Push(stream); 1.1889 + SetWriteCallbacks(); 1.1890 + return rv; 1.1891 + } 1.1892 + 1.1893 + if (stream->BlockedOnRwin()) { 1.1894 + LOG3(("Http2Session %p will stream %p 0x%X suspended for flow control\n", 1.1895 + this, stream, stream->StreamID())); 1.1896 + return NS_BASE_STREAM_WOULD_BLOCK; 1.1897 + } 1.1898 + 1.1899 + LOG3(("Http2Session::ReadSegments %p stream=%p stream send complete", 1.1900 + this, stream)); 1.1901 + 1.1902 + // call readsegments again if there are other streams ready 1.1903 + // to go in this session 1.1904 + SetWriteCallbacks(); 1.1905 + 1.1906 + return rv; 1.1907 +} 1.1908 + 1.1909 +nsresult 1.1910 +Http2Session::ReadyToProcessDataFrame(enum internalStateType newState) 1.1911 +{ 1.1912 + MOZ_ASSERT(newState == PROCESSING_DATA_FRAME || 1.1913 + newState == DISCARDING_DATA_FRAME_PADDING); 1.1914 + ChangeDownstreamState(newState); 1.1915 + 1.1916 + Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD, 1.1917 + mInputFrameDataSize >> 10); 1.1918 + mLastDataReadEpoch = mLastReadEpoch; 1.1919 + 1.1920 + if (!mInputFrameID) { 1.1921 + LOG3(("Http2Session::ReadyToProcessDataFrame %p data frame stream 0\n", 1.1922 + this)); 1.1923 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.1924 + } 1.1925 + 1.1926 + nsresult rv = SetInputFrameDataStream(mInputFrameID); 1.1927 + if (NS_FAILED(rv)) { 1.1928 + LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X " 1.1929 + "failed. probably due to verification.\n", this, mInputFrameID)); 1.1930 + return rv; 1.1931 + } 1.1932 + if (!mInputFrameDataStream) { 1.1933 + LOG3(("Http2Session::ReadyToProcessDataFrame %p lookup streamID 0x%X " 1.1934 + "failed. Next = 0x%X", this, mInputFrameID, mNextStreamID)); 1.1935 + if (mInputFrameID >= mNextStreamID) 1.1936 + GenerateRstStream(PROTOCOL_ERROR, mInputFrameID); 1.1937 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.1938 + } else if (mInputFrameDataStream->RecvdFin() || 1.1939 + mInputFrameDataStream->RecvdReset() || 1.1940 + mInputFrameDataStream->SentReset()) { 1.1941 + LOG3(("Http2Session::ReadyToProcessDataFrame %p streamID 0x%X " 1.1942 + "Data arrived for already server closed stream.\n", 1.1943 + this, mInputFrameID)); 1.1944 + if (mInputFrameDataStream->RecvdFin() || mInputFrameDataStream->RecvdReset()) 1.1945 + GenerateRstStream(STREAM_CLOSED_ERROR, mInputFrameID); 1.1946 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.1947 + } 1.1948 + 1.1949 + LOG3(("Start Processing Data Frame. " 1.1950 + "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d", 1.1951 + this, mInputFrameID, mInputFrameDataStream, mInputFrameFinal, 1.1952 + mInputFrameDataSize)); 1.1953 + UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize); 1.1954 + 1.1955 + return NS_OK; 1.1956 +} 1.1957 + 1.1958 +// WriteSegments() is used to read data off the socket. Generally this is 1.1959 +// just the http2 frame header and from there the appropriate *Stream 1.1960 +// is identified from the Stream-ID. The http transaction associated with 1.1961 +// that read then pulls in the data directly, which it will feed to 1.1962 +// OnWriteSegment(). That function will gateway it into http and feed 1.1963 +// it to the appropriate transaction. 1.1964 + 1.1965 +// we call writer->OnWriteSegment via NetworkRead() to get a http2 header.. 1.1966 +// and decide if it is data or control.. if it is control, just deal with it. 1.1967 +// if it is data, identify the stream 1.1968 +// call stream->WriteSegments which can call this::OnWriteSegment to get the 1.1969 +// data. It always gets full frames if they are part of the stream 1.1970 + 1.1971 +nsresult 1.1972 +Http2Session::WriteSegments(nsAHttpSegmentWriter *writer, 1.1973 + uint32_t count, uint32_t *countWritten) 1.1974 +{ 1.1975 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1976 + 1.1977 + LOG3(("Http2Session::WriteSegments %p InternalState %X\n", 1.1978 + this, mDownstreamState)); 1.1979 + 1.1980 + *countWritten = 0; 1.1981 + 1.1982 + if (mClosed) 1.1983 + return NS_ERROR_FAILURE; 1.1984 + 1.1985 + nsresult rv = ConfirmTLSProfile(); 1.1986 + if (NS_FAILED(rv)) 1.1987 + return rv; 1.1988 + 1.1989 + SetWriteCallbacks(); 1.1990 + 1.1991 + // If there are http transactions attached to a push stream with filled buffers 1.1992 + // trigger that data pump here. This only reads from buffers (not the network) 1.1993 + // so mDownstreamState doesn't matter. 1.1994 + Http2Stream *pushConnectedStream = 1.1995 + static_cast<Http2Stream *>(mReadyForRead.PopFront()); 1.1996 + if (pushConnectedStream) { 1.1997 + LOG3(("Http2Session::WriteSegments %p processing pushed stream 0x%X\n", 1.1998 + this, pushConnectedStream->StreamID())); 1.1999 + mSegmentWriter = writer; 1.2000 + rv = pushConnectedStream->WriteSegments(this, count, countWritten); 1.2001 + mSegmentWriter = nullptr; 1.2002 + 1.2003 + // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK 1.2004 + // so we need this check to determine the truth. 1.2005 + if (NS_SUCCEEDED(rv) && !*countWritten && 1.2006 + pushConnectedStream->PushSource() && 1.2007 + pushConnectedStream->PushSource()->GetPushComplete()) { 1.2008 + rv = NS_BASE_STREAM_CLOSED; 1.2009 + } 1.2010 + 1.2011 + if (rv == NS_BASE_STREAM_CLOSED) { 1.2012 + CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR); 1.2013 + rv = NS_OK; 1.2014 + } 1.2015 + 1.2016 + // if we return OK to nsHttpConnection it will use mSocketInCondition 1.2017 + // to determine whether to schedule more reads, incorrectly 1.2018 + // assuming that nsHttpConnection::OnSocketWrite() was called. 1.2019 + if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) { 1.2020 + rv = NS_BASE_STREAM_WOULD_BLOCK; 1.2021 + ResumeRecv(); 1.2022 + } 1.2023 + 1.2024 + return rv; 1.2025 + } 1.2026 + 1.2027 + // The BUFFERING_OPENING_SETTINGS state is just like any BUFFERING_FRAME_HEADER 1.2028 + // except the only frame type it will allow is SETTINGS 1.2029 + 1.2030 + // The session layer buffers the leading 8 byte header of every frame. 1.2031 + // Non-Data frames are then buffered for their full length, but data 1.2032 + // frames (type 0) are passed through to the http stack unprocessed 1.2033 + 1.2034 + if (mDownstreamState == BUFFERING_OPENING_SETTINGS || 1.2035 + mDownstreamState == BUFFERING_FRAME_HEADER) { 1.2036 + // The first 8 bytes of every frame is header information that 1.2037 + // we are going to want to strip before passing to http. That is 1.2038 + // true of both control and data packets. 1.2039 + 1.2040 + MOZ_ASSERT(mInputFrameBufferUsed < 8, 1.2041 + "Frame Buffer Used Too Large for State"); 1.2042 + 1.2043 + rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed, 1.2044 + 8 - mInputFrameBufferUsed, countWritten); 1.2045 + 1.2046 + if (NS_FAILED(rv)) { 1.2047 + LOG3(("Http2Session %p buffering frame header read failure %x\n", 1.2048 + this, rv)); 1.2049 + // maybe just blocked reading from network 1.2050 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2051 + rv = NS_OK; 1.2052 + return rv; 1.2053 + } 1.2054 + 1.2055 + LogIO(this, nullptr, "Reading Frame Header", 1.2056 + mInputFrameBuffer + mInputFrameBufferUsed, *countWritten); 1.2057 + 1.2058 + mInputFrameBufferUsed += *countWritten; 1.2059 + 1.2060 + if (mInputFrameBufferUsed < 8) 1.2061 + { 1.2062 + LOG3(("Http2Session::WriteSegments %p " 1.2063 + "BUFFERING FRAME HEADER incomplete size=%d", 1.2064 + this, mInputFrameBufferUsed)); 1.2065 + return rv; 1.2066 + } 1.2067 + 1.2068 + // 2 bytes of length, 1 type byte, 1 flag byte, 1 unused bit, 31 bits of ID 1.2069 + mInputFrameDataSize = 1.2070 + PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[0]); 1.2071 + mInputFrameType = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[2]; 1.2072 + mInputFrameFlags = reinterpret_cast<uint8_t *>(mInputFrameBuffer.get())[3]; 1.2073 + mInputFrameID = 1.2074 + PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[1]); 1.2075 + mInputFrameID &= 0x7fffffff; 1.2076 + mInputFrameDataRead = 0; 1.2077 + 1.2078 + if (mInputFrameType == FRAME_TYPE_DATA || mInputFrameType == FRAME_TYPE_HEADERS) { 1.2079 + mInputFrameFinal = mInputFrameFlags & kFlag_END_STREAM; 1.2080 + } else { 1.2081 + mInputFrameFinal = 0; 1.2082 + } 1.2083 + 1.2084 + mPaddingLength = 0; 1.2085 + if (mInputFrameType == FRAME_TYPE_DATA || 1.2086 + mInputFrameType == FRAME_TYPE_HEADERS || 1.2087 + // TODO: also mInputFrameType == FRAME_TYPE_PUSH_PROMISE after draft10 1.2088 + mInputFrameType == FRAME_TYPE_CONTINUATION) { 1.2089 + if ((mInputFrameFlags & kFlag_PAD_HIGH) && 1.2090 + !(mInputFrameFlags & kFlag_PAD_LOW)) { 1.2091 + LOG3(("Http2Session::WriteSegments %p PROTOCOL_ERROR pad_high present " 1.2092 + "without pad_low\n", this)); 1.2093 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.2094 + } 1.2095 + } 1.2096 + 1.2097 + if (mInputFrameDataSize >= 0x4000) { 1.2098 + // Section 9.1 HTTP frames cannot exceed 2^14 - 1 but receviers must ignore 1.2099 + // those bits 1.2100 + LOG3(("Http2Session::WriteSegments %p WARNING Frame Length bits past 14 are not 0 %08X\n", 1.2101 + this, mInputFrameDataSize)); 1.2102 + mInputFrameDataSize &= 0x3fff; 1.2103 + } 1.2104 + 1.2105 + LOG3(("Http2Session::WriteSegments[%p::%x] Frame Header Read " 1.2106 + "type %X data len %u flags %x id 0x%X", 1.2107 + this, mSerial, mInputFrameType, mInputFrameDataSize, mInputFrameFlags, 1.2108 + mInputFrameID)); 1.2109 + 1.2110 + // if mExpectedHeaderID is non 0, it means this frame must be a CONTINUATION of 1.2111 + // a HEADERS frame with a matching ID (section 6.2) 1.2112 + if (mExpectedHeaderID && 1.2113 + ((mInputFrameType != FRAME_TYPE_CONTINUATION) || 1.2114 + (mExpectedHeaderID != mInputFrameID))) { 1.2115 + LOG3(("Expected CONINUATION OF HEADERS for ID 0x%X\n", mExpectedHeaderID)); 1.2116 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.2117 + } 1.2118 + 1.2119 + // if mExpectedPushPromiseID is non 0, it means this frame must be a 1.2120 + // CONTINUATION of a PUSH_PROMISE with a matching ID (section 6.2) 1.2121 + if (mExpectedPushPromiseID && 1.2122 + ((mInputFrameType != FRAME_TYPE_CONTINUATION) || 1.2123 + (mExpectedPushPromiseID != mInputFrameID))) { 1.2124 + LOG3(("Expected CONTINUATION of PUSH PROMISE for ID 0x%X\n", 1.2125 + mExpectedPushPromiseID)); 1.2126 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.2127 + } 1.2128 + 1.2129 + if (mDownstreamState == BUFFERING_OPENING_SETTINGS && 1.2130 + mInputFrameType != FRAME_TYPE_SETTINGS) { 1.2131 + LOG3(("First Frame Type Must Be Settings\n")); 1.2132 + RETURN_SESSION_ERROR(this, PROTOCOL_ERROR); 1.2133 + } 1.2134 + 1.2135 + if (mInputFrameType != FRAME_TYPE_DATA) { // control frame 1.2136 + EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8, 1.2137 + mInputFrameBufferSize); 1.2138 + ChangeDownstreamState(BUFFERING_CONTROL_FRAME); 1.2139 + } else if (mInputFrameFlags & (kFlag_PAD_LOW | kFlag_PAD_HIGH)) { 1.2140 + ChangeDownstreamState(PROCESSING_DATA_FRAME_PADDING_CONTROL); 1.2141 + } else { 1.2142 + rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME); 1.2143 + if (NS_FAILED(rv)) { 1.2144 + return rv; 1.2145 + } 1.2146 + } 1.2147 + } 1.2148 + 1.2149 + if (mDownstreamState == PROCESSING_DATA_FRAME_PADDING_CONTROL) { 1.2150 + uint32_t numControlBytes = 0; 1.2151 + if (mInputFrameFlags & kFlag_PAD_LOW) { 1.2152 + ++numControlBytes; 1.2153 + } 1.2154 + if (mInputFrameFlags & kFlag_PAD_HIGH) { 1.2155 + ++numControlBytes; 1.2156 + } 1.2157 + 1.2158 + MOZ_ASSERT(numControlBytes, 1.2159 + "Processing padding control with no control bytes!"); 1.2160 + MOZ_ASSERT(mInputFrameBufferUsed < (8 + numControlBytes), 1.2161 + "Frame buffer used too large for state"); 1.2162 + 1.2163 + rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed, 1.2164 + (8 + numControlBytes) - mInputFrameBufferUsed, 1.2165 + countWritten); 1.2166 + 1.2167 + if (NS_FAILED(rv)) { 1.2168 + LOG3(("Http2Session %p buffering data frame padding control read failure %x\n", 1.2169 + this, rv)); 1.2170 + // maybe just blocked reading from network 1.2171 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2172 + rv = NS_OK; 1.2173 + return rv; 1.2174 + } 1.2175 + 1.2176 + LogIO(this, nullptr, "Reading Data Frame Padding Control", 1.2177 + mInputFrameBuffer + mInputFrameBufferUsed, *countWritten); 1.2178 + 1.2179 + mInputFrameBufferUsed += *countWritten; 1.2180 + 1.2181 + if (mInputFrameBufferUsed - 8 < numControlBytes) { 1.2182 + LOG3(("Http2Session::WriteSegments %p " 1.2183 + "BUFFERING DATA FRAME CONTROL PADDING incomplete size=%d", 1.2184 + this, mInputFrameBufferUsed - 8)); 1.2185 + return rv; 1.2186 + } 1.2187 + 1.2188 + mInputFrameDataRead += numControlBytes; 1.2189 + 1.2190 + char *control = mInputFrameBuffer + 8; 1.2191 + if (mInputFrameFlags & kFlag_PAD_HIGH) { 1.2192 + mPaddingLength = static_cast<uint16_t>(*control) * 256; 1.2193 + ++control; 1.2194 + } 1.2195 + mPaddingLength += static_cast<uint8_t>(*control); 1.2196 + 1.2197 + LOG3(("Http2Session::WriteSegments %p stream 0x%X mPaddingLength=%d", this, 1.2198 + mInputFrameID, mPaddingLength)); 1.2199 + 1.2200 + if (numControlBytes + mPaddingLength == mInputFrameDataSize) { 1.2201 + // This frame consists entirely of padding, we can just discard it 1.2202 + LOG3(("Http2Session::WriteSegments %p stream 0x%X frame with only padding", 1.2203 + this, mInputFrameID)); 1.2204 + rv = ReadyToProcessDataFrame(DISCARDING_DATA_FRAME_PADDING); 1.2205 + if (NS_FAILED(rv)) { 1.2206 + return rv; 1.2207 + } 1.2208 + } else { 1.2209 + LOG3(("Http2Session::WriteSegments %p stream 0x%X ready to read HTTP data", 1.2210 + this, mInputFrameID)); 1.2211 + rv = ReadyToProcessDataFrame(PROCESSING_DATA_FRAME); 1.2212 + if (NS_FAILED(rv)) { 1.2213 + return rv; 1.2214 + } 1.2215 + } 1.2216 + } 1.2217 + 1.2218 + if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) { 1.2219 + nsresult streamCleanupCode; 1.2220 + 1.2221 + // There is no bounds checking on the error code.. we provide special 1.2222 + // handling for a couple of cases and all others (including unknown) are 1.2223 + // equivalent to cancel. 1.2224 + if (mDownstreamRstReason == REFUSED_STREAM_ERROR) { 1.2225 + streamCleanupCode = NS_ERROR_NET_RESET; // can retry this 100% safely 1.2226 + } else { 1.2227 + streamCleanupCode = NS_ERROR_NET_INTERRUPT; 1.2228 + } 1.2229 + 1.2230 + if (mDownstreamRstReason == COMPRESSION_ERROR) 1.2231 + mShouldGoAway = true; 1.2232 + 1.2233 + // mInputFrameDataStream is reset by ChangeDownstreamState 1.2234 + Http2Stream *stream = mInputFrameDataStream; 1.2235 + ResetDownstreamState(); 1.2236 + LOG3(("Http2Session::WriteSegments cleanup stream on recv of rst " 1.2237 + "session=%p stream=%p 0x%X\n", this, stream, 1.2238 + stream ? stream->StreamID() : 0)); 1.2239 + CleanupStream(stream, streamCleanupCode, CANCEL_ERROR); 1.2240 + return NS_OK; 1.2241 + } 1.2242 + 1.2243 + if (mDownstreamState == PROCESSING_DATA_FRAME || 1.2244 + mDownstreamState == PROCESSING_COMPLETE_HEADERS) { 1.2245 + 1.2246 + // The cleanup stream should only be set while stream->WriteSegments is 1.2247 + // on the stack and then cleaned up in this code block afterwards. 1.2248 + MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly"); 1.2249 + mNeedsCleanup = nullptr; /* just in case */ 1.2250 + 1.2251 + mSegmentWriter = writer; 1.2252 + rv = mInputFrameDataStream->WriteSegments(this, count, countWritten); 1.2253 + mSegmentWriter = nullptr; 1.2254 + 1.2255 + mLastDataReadEpoch = mLastReadEpoch; 1.2256 + 1.2257 + if (SoftStreamError(rv)) { 1.2258 + // This will happen when the transaction figures out it is EOF, generally 1.2259 + // due to a content-length match being made. Return OK from this function 1.2260 + // otherwise the whole session would be torn down. 1.2261 + Http2Stream *stream = mInputFrameDataStream; 1.2262 + 1.2263 + // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state 1.2264 + // back to PROCESSING_DATA_FRAME where we came from 1.2265 + mDownstreamState = PROCESSING_DATA_FRAME; 1.2266 + 1.2267 + if (mInputFrameDataRead == mInputFrameDataSize) 1.2268 + ResetDownstreamState(); 1.2269 + LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X " 1.2270 + "needscleanup=%p. cleanup stream based on " 1.2271 + "stream->writeSegments returning code %x\n", 1.2272 + this, stream, stream ? stream->StreamID() : 0, 1.2273 + mNeedsCleanup, rv)); 1.2274 + CleanupStream(stream, NS_OK, CANCEL_ERROR); 1.2275 + MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame"); 1.2276 + mNeedsCleanup = nullptr; /* just in case */ 1.2277 + return NS_OK; 1.2278 + } 1.2279 + 1.2280 + if (mNeedsCleanup) { 1.2281 + LOG3(("Http2Session::WriteSegments session=%p stream=%p 0x%X " 1.2282 + "cleanup stream based on mNeedsCleanup.\n", 1.2283 + this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0)); 1.2284 + CleanupStream(mNeedsCleanup, NS_OK, CANCEL_ERROR); 1.2285 + mNeedsCleanup = nullptr; 1.2286 + } 1.2287 + 1.2288 + if (NS_FAILED(rv)) { 1.2289 + LOG3(("Http2Session %p data frame read failure %x\n", this, rv)); 1.2290 + // maybe just blocked reading from network 1.2291 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2292 + rv = NS_OK; 1.2293 + } 1.2294 + 1.2295 + return rv; 1.2296 + } 1.2297 + 1.2298 + if (mDownstreamState == DISCARDING_DATA_FRAME || 1.2299 + mDownstreamState == DISCARDING_DATA_FRAME_PADDING) { 1.2300 + char trash[4096]; 1.2301 + uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead); 1.2302 + LOG3(("Http2Session::WriteSegments %p trying to discard %d bytes of data", 1.2303 + this, count)); 1.2304 + 1.2305 + if (!count) { 1.2306 + ResetDownstreamState(); 1.2307 + ResumeRecv(); 1.2308 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2309 + } 1.2310 + 1.2311 + rv = NetworkRead(writer, trash, count, countWritten); 1.2312 + 1.2313 + if (NS_FAILED(rv)) { 1.2314 + LOG3(("Http2Session %p discard frame read failure %x\n", this, rv)); 1.2315 + // maybe just blocked reading from network 1.2316 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2317 + rv = NS_OK; 1.2318 + return rv; 1.2319 + } 1.2320 + 1.2321 + LogIO(this, nullptr, "Discarding Frame", trash, *countWritten); 1.2322 + 1.2323 + mInputFrameDataRead += *countWritten; 1.2324 + 1.2325 + if (mInputFrameDataRead == mInputFrameDataSize) { 1.2326 + Http2Stream *streamToCleanup = nullptr; 1.2327 + if (mInputFrameFinal) { 1.2328 + streamToCleanup = mInputFrameDataStream; 1.2329 + } 1.2330 + 1.2331 + ResetDownstreamState(); 1.2332 + 1.2333 + if (streamToCleanup) { 1.2334 + CleanupStream(streamToCleanup, NS_OK, CANCEL_ERROR); 1.2335 + } 1.2336 + } 1.2337 + return rv; 1.2338 + } 1.2339 + 1.2340 + if (mDownstreamState != BUFFERING_CONTROL_FRAME) { 1.2341 + MOZ_ASSERT(false); // this cannot happen 1.2342 + return NS_ERROR_UNEXPECTED; 1.2343 + } 1.2344 + 1.2345 + MOZ_ASSERT(mInputFrameBufferUsed == 8, "Frame Buffer Header Not Present"); 1.2346 + MOZ_ASSERT(mInputFrameDataSize + 8 <= mInputFrameBufferSize, 1.2347 + "allocation for control frame insufficient"); 1.2348 + 1.2349 + rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead, 1.2350 + mInputFrameDataSize - mInputFrameDataRead, countWritten); 1.2351 + 1.2352 + if (NS_FAILED(rv)) { 1.2353 + LOG3(("Http2Session %p buffering control frame read failure %x\n", 1.2354 + this, rv)); 1.2355 + // maybe just blocked reading from network 1.2356 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2357 + rv = NS_OK; 1.2358 + return rv; 1.2359 + } 1.2360 + 1.2361 + LogIO(this, nullptr, "Reading Control Frame", 1.2362 + mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten); 1.2363 + 1.2364 + mInputFrameDataRead += *countWritten; 1.2365 + 1.2366 + if (mInputFrameDataRead != mInputFrameDataSize) 1.2367 + return NS_OK; 1.2368 + 1.2369 + MOZ_ASSERT(mInputFrameType != FRAME_TYPE_DATA); 1.2370 + if (mInputFrameType < FRAME_TYPE_LAST) { 1.2371 + rv = sControlFunctions[mInputFrameType](this); 1.2372 + } else { 1.2373 + // Section 4.1 requires this to be ignored; though protocol_error would 1.2374 + // be better 1.2375 + LOG3(("Http2Session %p unknow frame type %x ignored\n", 1.2376 + this, mInputFrameType)); 1.2377 + ResetDownstreamState(); 1.2378 + rv = NS_OK; 1.2379 + } 1.2380 + 1.2381 + MOZ_ASSERT(NS_FAILED(rv) || 1.2382 + mDownstreamState != BUFFERING_CONTROL_FRAME, 1.2383 + "Control Handler returned OK but did not change state"); 1.2384 + 1.2385 + if (mShouldGoAway && !mStreamTransactionHash.Count()) 1.2386 + Close(NS_OK); 1.2387 + return rv; 1.2388 +} 1.2389 + 1.2390 +void 1.2391 +Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes) 1.2392 +{ 1.2393 + if (!stream) // this is ok - it means there was a data frame for a rst stream 1.2394 + return; 1.2395 + 1.2396 + // If this data packet was not for a valid or live stream then there 1.2397 + // is no reason to mess with the flow control 1.2398 + if (!stream || stream->RecvdFin() || stream->RecvdReset() || 1.2399 + mInputFrameFinal) { 1.2400 + return; 1.2401 + } 1.2402 + 1.2403 + stream->DecrementClientReceiveWindow(bytes); 1.2404 + 1.2405 + // Don't necessarily ack every data packet. Only do it 1.2406 + // after a significant amount of data. 1.2407 + uint64_t unacked = stream->LocalUnAcked(); 1.2408 + int64_t localWindow = stream->ClientReceiveWindow(); 1.2409 + 1.2410 + LOG3(("Http2Session::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u " 1.2411 + "unacked=%llu localWindow=%lld\n", 1.2412 + this, stream->StreamID(), bytes, unacked, localWindow)); 1.2413 + 1.2414 + if (!unacked) 1.2415 + return; 1.2416 + 1.2417 + if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold)) 1.2418 + return; 1.2419 + 1.2420 + if (!stream->HasSink()) { 1.2421 + LOG3(("Http2Session::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n", 1.2422 + this, stream->StreamID())); 1.2423 + return; 1.2424 + } 1.2425 + 1.2426 + // Generate window updates directly out of session instead of the stream 1.2427 + // in order to avoid queue delays in getting the 'ACK' out. 1.2428 + uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU; 1.2429 + 1.2430 + LOG3(("Http2Session::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n", 1.2431 + this, stream->StreamID(), toack)); 1.2432 + stream->IncrementClientReceiveWindow(toack); 1.2433 + 1.2434 + // room for this packet needs to be ensured before calling this function 1.2435 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.2436 + mOutputQueueUsed += 12; 1.2437 + MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize); 1.2438 + 1.2439 + CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, stream->StreamID()); 1.2440 + toack = PR_htonl(toack); 1.2441 + memcpy(packet + 8, &toack, 4); 1.2442 + 1.2443 + LogIO(this, stream, "Stream Window Update", packet, 12); 1.2444 + // dont flush here, this write can commonly be coalesced with a 1.2445 + // session window update to immediately follow. 1.2446 +} 1.2447 + 1.2448 +void 1.2449 +Http2Session::UpdateLocalSessionWindow(uint32_t bytes) 1.2450 +{ 1.2451 + if (!bytes) 1.2452 + return; 1.2453 + 1.2454 + mLocalSessionWindow -= bytes; 1.2455 + 1.2456 + LOG3(("Http2Session::UpdateLocalSessionWindow this=%p newbytes=%u " 1.2457 + "localWindow=%lld\n", this, bytes, mLocalSessionWindow)); 1.2458 + 1.2459 + // Don't necessarily ack every data packet. Only do it 1.2460 + // after a significant amount of data. 1.2461 + if ((mLocalSessionWindow > (ASpdySession::kInitialRwin - kMinimumToAck)) && 1.2462 + (mLocalSessionWindow > kEmergencyWindowThreshold)) 1.2463 + return; 1.2464 + 1.2465 + // Only send max bits of window updates at a time. 1.2466 + uint64_t toack64 = ASpdySession::kInitialRwin - mLocalSessionWindow; 1.2467 + uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU; 1.2468 + 1.2469 + LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n", 1.2470 + this, toack)); 1.2471 + mLocalSessionWindow += toack; 1.2472 + 1.2473 + // room for this packet needs to be ensured before calling this function 1.2474 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.2475 + mOutputQueueUsed += 12; 1.2476 + MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize); 1.2477 + 1.2478 + CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0); 1.2479 + toack = PR_htonl(toack); 1.2480 + memcpy(packet + 8, &toack, 4); 1.2481 + 1.2482 + LogIO(this, nullptr, "Session Window Update", packet, 12); 1.2483 + // dont flush here, this write can commonly be coalesced with others 1.2484 +} 1.2485 + 1.2486 +void 1.2487 +Http2Session::UpdateLocalRwin(Http2Stream *stream, uint32_t bytes) 1.2488 +{ 1.2489 + // make sure there is room for 2 window updates even though 1.2490 + // we may not generate any. 1.2491 + EnsureOutputBuffer(16 * 2); 1.2492 + 1.2493 + UpdateLocalStreamWindow(stream, bytes); 1.2494 + UpdateLocalSessionWindow(bytes); 1.2495 + FlushOutputQueue(); 1.2496 +} 1.2497 + 1.2498 +void 1.2499 +Http2Session::Close(nsresult aReason) 1.2500 +{ 1.2501 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2502 + 1.2503 + if (mClosed) 1.2504 + return; 1.2505 + 1.2506 + LOG3(("Http2Session::Close %p %X", this, aReason)); 1.2507 + 1.2508 + mClosed = true; 1.2509 + 1.2510 + mStreamTransactionHash.Enumerate(ShutdownEnumerator, this); 1.2511 + mStreamIDHash.Clear(); 1.2512 + mStreamTransactionHash.Clear(); 1.2513 + 1.2514 + uint32_t goAwayReason; 1.2515 + if (mGoAwayReason != NO_HTTP_ERROR) { 1.2516 + goAwayReason = mGoAwayReason; 1.2517 + } else if (NS_SUCCEEDED(aReason)) { 1.2518 + goAwayReason = NO_HTTP_ERROR; 1.2519 + } else if (aReason == NS_ERROR_ILLEGAL_VALUE) { 1.2520 + goAwayReason = PROTOCOL_ERROR; 1.2521 + } else { 1.2522 + goAwayReason = INTERNAL_ERROR; 1.2523 + } 1.2524 + GenerateGoAway(goAwayReason); 1.2525 + mConnection = nullptr; 1.2526 + mSegmentReader = nullptr; 1.2527 + mSegmentWriter = nullptr; 1.2528 +} 1.2529 + 1.2530 +void 1.2531 +Http2Session::CloseTransaction(nsAHttpTransaction *aTransaction, 1.2532 + nsresult aResult) 1.2533 +{ 1.2534 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2535 + LOG3(("Http2Session::CloseTransaction %p %p %x", this, aTransaction, aResult)); 1.2536 + 1.2537 + // Generally this arrives as a cancel event from the connection manager. 1.2538 + 1.2539 + // need to find the stream and call CleanupStream() on it. 1.2540 + Http2Stream *stream = mStreamTransactionHash.Get(aTransaction); 1.2541 + if (!stream) { 1.2542 + LOG3(("Http2Session::CloseTransaction %p %p %x - not found.", 1.2543 + this, aTransaction, aResult)); 1.2544 + return; 1.2545 + } 1.2546 + LOG3(("Http2Session::CloseTranscation probably a cancel. " 1.2547 + "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p", 1.2548 + this, aTransaction, aResult, stream->StreamID(), stream)); 1.2549 + CleanupStream(stream, aResult, CANCEL_ERROR); 1.2550 + ResumeRecv(); 1.2551 +} 1.2552 + 1.2553 +//----------------------------------------------------------------------------- 1.2554 +// nsAHttpSegmentReader 1.2555 +//----------------------------------------------------------------------------- 1.2556 + 1.2557 +nsresult 1.2558 +Http2Session::OnReadSegment(const char *buf, 1.2559 + uint32_t count, uint32_t *countRead) 1.2560 +{ 1.2561 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2562 + nsresult rv; 1.2563 + 1.2564 + // If we can release old queued data then we can try and write the new 1.2565 + // data directly to the network without using the output queue at all 1.2566 + if (mOutputQueueUsed) 1.2567 + FlushOutputQueue(); 1.2568 + 1.2569 + if (!mOutputQueueUsed && mSegmentReader) { 1.2570 + // try and write directly without output queue 1.2571 + rv = mSegmentReader->OnReadSegment(buf, count, countRead); 1.2572 + 1.2573 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 1.2574 + *countRead = 0; 1.2575 + } else if (NS_FAILED(rv)) { 1.2576 + return rv; 1.2577 + } 1.2578 + 1.2579 + if (*countRead < count) { 1.2580 + uint32_t required = count - *countRead; 1.2581 + // assuming a commitment() happened, this ensurebuffer is a nop 1.2582 + // but just in case the queuesize is too small for the required data 1.2583 + // call ensurebuffer(). 1.2584 + EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize); 1.2585 + memcpy(mOutputQueueBuffer.get(), buf + *countRead, required); 1.2586 + mOutputQueueUsed = required; 1.2587 + } 1.2588 + 1.2589 + *countRead = count; 1.2590 + return NS_OK; 1.2591 + } 1.2592 + 1.2593 + // At this point we are going to buffer the new data in the output 1.2594 + // queue if it fits. By coalescing multiple small submissions into one larger 1.2595 + // buffer we can get larger writes out to the network later on. 1.2596 + 1.2597 + // This routine should not be allowed to fill up the output queue 1.2598 + // all on its own - at least kQueueReserved bytes are always left 1.2599 + // for other routines to use - but this is an all-or-nothing function, 1.2600 + // so if it will not all fit just return WOULD_BLOCK 1.2601 + 1.2602 + if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved)) 1.2603 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2604 + 1.2605 + memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count); 1.2606 + mOutputQueueUsed += count; 1.2607 + *countRead = count; 1.2608 + 1.2609 + FlushOutputQueue(); 1.2610 + 1.2611 + return NS_OK; 1.2612 +} 1.2613 + 1.2614 +nsresult 1.2615 +Http2Session::CommitToSegmentSize(uint32_t count, bool forceCommitment) 1.2616 +{ 1.2617 + if (mOutputQueueUsed) 1.2618 + FlushOutputQueue(); 1.2619 + 1.2620 + // would there be enough room to buffer this if needed? 1.2621 + if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved)) 1.2622 + return NS_OK; 1.2623 + 1.2624 + // if we are using part of our buffers already, try again later unless 1.2625 + // forceCommitment is set. 1.2626 + if (mOutputQueueUsed && !forceCommitment) 1.2627 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2628 + 1.2629 + if (mOutputQueueUsed) { 1.2630 + // normally we avoid the memmove of RealignOutputQueue, but we'll try 1.2631 + // it if forceCommitment is set before growing the buffer. 1.2632 + RealignOutputQueue(); 1.2633 + 1.2634 + // is there enough room now? 1.2635 + if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved)) 1.2636 + return NS_OK; 1.2637 + } 1.2638 + 1.2639 + // resize the buffers as needed 1.2640 + EnsureOutputBuffer(count + kQueueReserved); 1.2641 + 1.2642 + MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved), 1.2643 + "buffer not as large as expected"); 1.2644 + 1.2645 + return NS_OK; 1.2646 +} 1.2647 + 1.2648 +//----------------------------------------------------------------------------- 1.2649 +// nsAHttpSegmentWriter 1.2650 +//----------------------------------------------------------------------------- 1.2651 + 1.2652 +nsresult 1.2653 +Http2Session::OnWriteSegment(char *buf, 1.2654 + uint32_t count, uint32_t *countWritten) 1.2655 +{ 1.2656 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2657 + nsresult rv; 1.2658 + 1.2659 + if (!mSegmentWriter) { 1.2660 + // the only way this could happen would be if Close() were called on the 1.2661 + // stack with WriteSegments() 1.2662 + return NS_ERROR_FAILURE; 1.2663 + } 1.2664 + 1.2665 + if (mDownstreamState == PROCESSING_DATA_FRAME) { 1.2666 + 1.2667 + if (mInputFrameFinal && 1.2668 + mInputFrameDataRead == mInputFrameDataSize) { 1.2669 + *countWritten = 0; 1.2670 + SetNeedsCleanup(); 1.2671 + return NS_BASE_STREAM_CLOSED; 1.2672 + } 1.2673 + 1.2674 + count = std::min(count, mInputFrameDataSize - mInputFrameDataRead); 1.2675 + rv = NetworkRead(mSegmentWriter, buf, count, countWritten); 1.2676 + if (NS_FAILED(rv)) 1.2677 + return rv; 1.2678 + 1.2679 + LogIO(this, mInputFrameDataStream, "Reading Data Frame", 1.2680 + buf, *countWritten); 1.2681 + 1.2682 + mInputFrameDataRead += *countWritten; 1.2683 + if (mPaddingLength && (mInputFrameDataSize - mInputFrameDataRead <= mPaddingLength)) { 1.2684 + // We are crossing from real HTTP data into the realm of padding. If 1.2685 + // we've actually crossed the line, we need to munge countWritten for the 1.2686 + // sake of goodness and sanity. No matter what, any future calls to 1.2687 + // WriteSegments need to just discard data until we reach the end of this 1.2688 + // frame. 1.2689 + ChangeDownstreamState(DISCARDING_DATA_FRAME_PADDING); 1.2690 + uint32_t paddingRead = mPaddingLength - (mInputFrameDataSize - mInputFrameDataRead); 1.2691 + LOG3(("Http2Session::OnWriteSegment %p stream 0x%X len=%d read=%d " 1.2692 + "crossed from HTTP data into padding (%d of %d) countWritten=%d", 1.2693 + this, mInputFrameID, mInputFrameDataSize, mInputFrameDataRead, 1.2694 + paddingRead, mPaddingLength, *countWritten)); 1.2695 + *countWritten -= paddingRead; 1.2696 + LOG3(("Http2Session::OnWriteSegment %p stream 0x%X new countWritten=%d", 1.2697 + this, mInputFrameID, *countWritten)); 1.2698 + } 1.2699 + 1.2700 + mInputFrameDataStream->UpdateTransportReadEvents(*countWritten); 1.2701 + if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameFinal) 1.2702 + ResetDownstreamState(); 1.2703 + 1.2704 + return rv; 1.2705 + } 1.2706 + 1.2707 + if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) { 1.2708 + 1.2709 + if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut && 1.2710 + mInputFrameFinal) { 1.2711 + *countWritten = 0; 1.2712 + SetNeedsCleanup(); 1.2713 + return NS_BASE_STREAM_CLOSED; 1.2714 + } 1.2715 + 1.2716 + count = std::min(count, 1.2717 + mFlatHTTPResponseHeaders.Length() - 1.2718 + mFlatHTTPResponseHeadersOut); 1.2719 + memcpy(buf, 1.2720 + mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut, 1.2721 + count); 1.2722 + mFlatHTTPResponseHeadersOut += count; 1.2723 + *countWritten = count; 1.2724 + 1.2725 + if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) { 1.2726 + if (!mInputFrameFinal) { 1.2727 + // If more frames are expected in this stream, then reset the state so they can be 1.2728 + // handled. Otherwise (e.g. a 0 length response with the fin on the incoming headers) 1.2729 + // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can 1.2730 + // cleanup the stream. 1.2731 + ResetDownstreamState(); 1.2732 + } 1.2733 + } 1.2734 + 1.2735 + return NS_OK; 1.2736 + } 1.2737 + 1.2738 + return NS_ERROR_UNEXPECTED; 1.2739 +} 1.2740 + 1.2741 +void 1.2742 +Http2Session::SetNeedsCleanup() 1.2743 +{ 1.2744 + LOG3(("Http2Session::SetNeedsCleanup %p - recorded downstream fin of " 1.2745 + "stream %p 0x%X", this, mInputFrameDataStream, 1.2746 + mInputFrameDataStream->StreamID())); 1.2747 + 1.2748 + // This will result in Close() being called 1.2749 + MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set"); 1.2750 + mNeedsCleanup = mInputFrameDataStream; 1.2751 + ResetDownstreamState(); 1.2752 +} 1.2753 + 1.2754 +void 1.2755 +Http2Session::ConnectPushedStream(Http2Stream *stream) 1.2756 +{ 1.2757 + mReadyForRead.Push(stream); 1.2758 + ForceRecv(); 1.2759 +} 1.2760 + 1.2761 +nsresult 1.2762 +Http2Session::BufferOutput(const char *buf, 1.2763 + uint32_t count, 1.2764 + uint32_t *countRead) 1.2765 +{ 1.2766 + nsAHttpSegmentReader *old = mSegmentReader; 1.2767 + mSegmentReader = nullptr; 1.2768 + nsresult rv = OnReadSegment(buf, count, countRead); 1.2769 + mSegmentReader = old; 1.2770 + return rv; 1.2771 +} 1.2772 + 1.2773 +nsresult 1.2774 +Http2Session::ConfirmTLSProfile() 1.2775 +{ 1.2776 + if (mTLSProfileConfirmed) 1.2777 + return NS_OK; 1.2778 + 1.2779 + LOG3(("Http2Session::ConfirmTLSProfile %p mConnection=%p\n", 1.2780 + this, mConnection.get())); 1.2781 + 1.2782 + if (!gHttpHandler->EnforceHttp2TlsProfile()) { 1.2783 + LOG3(("Http2Session::ConfirmTLSProfile %p passed due to configuration bypass\n", this)); 1.2784 + mTLSProfileConfirmed = true; 1.2785 + return NS_OK; 1.2786 + } 1.2787 + 1.2788 + if (!mConnection) 1.2789 + return NS_ERROR_FAILURE; 1.2790 + 1.2791 + nsCOMPtr<nsISupports> securityInfo; 1.2792 + mConnection->GetSecurityInfo(getter_AddRefs(securityInfo)); 1.2793 + nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo); 1.2794 + LOG3(("Http2Session::ConfirmTLSProfile %p sslsocketcontrol=%p\n", this, ssl.get())); 1.2795 + if (!ssl) 1.2796 + return NS_ERROR_FAILURE; 1.2797 + 1.2798 + int16_t version = ssl->GetSSLVersionUsed(); 1.2799 + LOG3(("Http2Session::ConfirmTLSProfile %p version=%x\n", this, version)); 1.2800 + if (version < nsISSLSocketControl::TLS_VERSION_1_2) { 1.2801 + LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of TLS1.2\n", this)); 1.2802 + RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY); 1.2803 + } 1.2804 + 1.2805 +#if 0 1.2806 + uint16_t kea = ssl->GetKEAUsed(); 1.2807 + if (kea != ssl_kea_dh && kea != ssl_kea_ecdh) { 1.2808 + LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to invalid KEA %d\n", 1.2809 + this, kea)); 1.2810 + RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY); 1.2811 + } 1.2812 +#endif 1.2813 + 1.2814 + /* TODO: Enforce DHE >= 2048 || ECDHE >= 128 */ 1.2815 + 1.2816 + /* We are required to send SNI. We do that already, so no check is done 1.2817 + * here to make sure we did. */ 1.2818 + 1.2819 + /* We really should check to ensure TLS compression isn't enabled on 1.2820 + * this connection. However, we never enable TLS compression on our end, 1.2821 + * anyway, so it'll never be on. All the same, see https://bugzil.la/965881 1.2822 + * for the possibility for an interface to ensure it never gets turned on. */ 1.2823 + 1.2824 + mTLSProfileConfirmed = true; 1.2825 + return NS_OK; 1.2826 +} 1.2827 + 1.2828 + 1.2829 +//----------------------------------------------------------------------------- 1.2830 +// Modified methods of nsAHttpConnection 1.2831 +//----------------------------------------------------------------------------- 1.2832 + 1.2833 +void 1.2834 +Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller) 1.2835 +{ 1.2836 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2837 + LOG3(("Http2Session::TransactionHasDataToWrite %p trans=%p", this, caller)); 1.2838 + 1.2839 + // a trapped signal from the http transaction to the connection that 1.2840 + // it is no longer blocked on read. 1.2841 + 1.2842 + Http2Stream *stream = mStreamTransactionHash.Get(caller); 1.2843 + if (!stream || !VerifyStream(stream)) { 1.2844 + LOG3(("Http2Session::TransactionHasDataToWrite %p caller %p not found", 1.2845 + this, caller)); 1.2846 + return; 1.2847 + } 1.2848 + 1.2849 + LOG3(("Http2Session::TransactionHasDataToWrite %p ID is 0x%X\n", 1.2850 + this, stream->StreamID())); 1.2851 + 1.2852 + mReadyForWrite.Push(stream); 1.2853 +} 1.2854 + 1.2855 +void 1.2856 +Http2Session::TransactionHasDataToWrite(Http2Stream *stream) 1.2857 +{ 1.2858 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2859 + LOG3(("Http2Session::TransactionHasDataToWrite %p stream=%p ID=0x%x", 1.2860 + this, stream, stream->StreamID())); 1.2861 + 1.2862 + mReadyForWrite.Push(stream); 1.2863 + SetWriteCallbacks(); 1.2864 +} 1.2865 + 1.2866 +bool 1.2867 +Http2Session::IsPersistent() 1.2868 +{ 1.2869 + return true; 1.2870 +} 1.2871 + 1.2872 +nsresult 1.2873 +Http2Session::TakeTransport(nsISocketTransport **, 1.2874 + nsIAsyncInputStream **, nsIAsyncOutputStream **) 1.2875 +{ 1.2876 + MOZ_ASSERT(false, "TakeTransport of Http2Session"); 1.2877 + return NS_ERROR_UNEXPECTED; 1.2878 +} 1.2879 + 1.2880 +nsHttpConnection * 1.2881 +Http2Session::TakeHttpConnection() 1.2882 +{ 1.2883 + MOZ_ASSERT(false, "TakeHttpConnection of Http2Session"); 1.2884 + return nullptr; 1.2885 +} 1.2886 + 1.2887 +uint32_t 1.2888 +Http2Session::CancelPipeline(nsresult reason) 1.2889 +{ 1.2890 + // we don't pipeline inside http/2, so this isn't an issue 1.2891 + return 0; 1.2892 +} 1.2893 + 1.2894 +nsAHttpTransaction::Classifier 1.2895 +Http2Session::Classification() 1.2896 +{ 1.2897 + if (!mConnection) 1.2898 + return nsAHttpTransaction::CLASS_GENERAL; 1.2899 + return mConnection->Classification(); 1.2900 +} 1.2901 + 1.2902 +//----------------------------------------------------------------------------- 1.2903 +// unused methods of nsAHttpTransaction 1.2904 +// We can be sure of this because Http2Session is only constructed in 1.2905 +// nsHttpConnection and is never passed out of that object 1.2906 +//----------------------------------------------------------------------------- 1.2907 + 1.2908 +void 1.2909 +Http2Session::SetConnection(nsAHttpConnection *) 1.2910 +{ 1.2911 + // This is unexpected 1.2912 + MOZ_ASSERT(false, "Http2Session::SetConnection()"); 1.2913 +} 1.2914 + 1.2915 +void 1.2916 +Http2Session::GetSecurityCallbacks(nsIInterfaceRequestor **) 1.2917 +{ 1.2918 + // This is unexpected 1.2919 + MOZ_ASSERT(false, "Http2Session::GetSecurityCallbacks()"); 1.2920 +} 1.2921 + 1.2922 +void 1.2923 +Http2Session::SetProxyConnectFailed() 1.2924 +{ 1.2925 + MOZ_ASSERT(false, "Http2Session::SetProxyConnectFailed()"); 1.2926 +} 1.2927 + 1.2928 +bool 1.2929 +Http2Session::IsDone() 1.2930 +{ 1.2931 + return !mStreamTransactionHash.Count(); 1.2932 +} 1.2933 + 1.2934 +nsresult 1.2935 +Http2Session::Status() 1.2936 +{ 1.2937 + MOZ_ASSERT(false, "Http2Session::Status()"); 1.2938 + return NS_ERROR_UNEXPECTED; 1.2939 +} 1.2940 + 1.2941 +uint32_t 1.2942 +Http2Session::Caps() 1.2943 +{ 1.2944 + MOZ_ASSERT(false, "Http2Session::Caps()"); 1.2945 + return 0; 1.2946 +} 1.2947 + 1.2948 +void 1.2949 +Http2Session::SetDNSWasRefreshed() 1.2950 +{ 1.2951 + MOZ_ASSERT(false, "Http2Session::SetDNSWasRefreshed()"); 1.2952 +} 1.2953 + 1.2954 +uint64_t 1.2955 +Http2Session::Available() 1.2956 +{ 1.2957 + MOZ_ASSERT(false, "Http2Session::Available()"); 1.2958 + return 0; 1.2959 +} 1.2960 + 1.2961 +nsHttpRequestHead * 1.2962 +Http2Session::RequestHead() 1.2963 +{ 1.2964 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2965 + MOZ_ASSERT(false, 1.2966 + "Http2Session::RequestHead() " 1.2967 + "should not be called after http/2 is setup"); 1.2968 + return NULL; 1.2969 +} 1.2970 + 1.2971 +uint32_t 1.2972 +Http2Session::Http1xTransactionCount() 1.2973 +{ 1.2974 + return 0; 1.2975 +} 1.2976 + 1.2977 +// used as an enumerator by TakeSubTransactions() 1.2978 +static PLDHashOperator 1.2979 + TakeStream(nsAHttpTransaction *key, 1.2980 + nsAutoPtr<Http2Stream> &stream, 1.2981 + void *closure) 1.2982 +{ 1.2983 + nsTArray<nsRefPtr<nsAHttpTransaction> > *list = 1.2984 + static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure); 1.2985 + 1.2986 + list->AppendElement(key); 1.2987 + 1.2988 + // removing the stream from the hash will delete the stream 1.2989 + // and drop the transaction reference the hash held 1.2990 + return PL_DHASH_REMOVE; 1.2991 +} 1.2992 + 1.2993 +nsresult 1.2994 +Http2Session::TakeSubTransactions( 1.2995 + nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) 1.2996 +{ 1.2997 + // Generally this cannot be done with http/2 as transactions are 1.2998 + // started right away. 1.2999 + 1.3000 + LOG3(("Http2Session::TakeSubTransactions %p\n", this)); 1.3001 + 1.3002 + if (mConcurrentHighWater > 0) 1.3003 + return NS_ERROR_ALREADY_OPENED; 1.3004 + 1.3005 + LOG3((" taking %d\n", mStreamTransactionHash.Count())); 1.3006 + 1.3007 + mStreamTransactionHash.Enumerate(TakeStream, &outTransactions); 1.3008 + return NS_OK; 1.3009 +} 1.3010 + 1.3011 +nsresult 1.3012 +Http2Session::AddTransaction(nsAHttpTransaction *) 1.3013 +{ 1.3014 + // This API is meant for pipelining, Http2Session's should be 1.3015 + // extended with AddStream() 1.3016 + 1.3017 + MOZ_ASSERT(false, 1.3018 + "Http2Session::AddTransaction() should not be called"); 1.3019 + 1.3020 + return NS_ERROR_NOT_IMPLEMENTED; 1.3021 +} 1.3022 + 1.3023 +uint32_t 1.3024 +Http2Session::PipelineDepth() 1.3025 +{ 1.3026 + return IsDone() ? 0 : 1; 1.3027 +} 1.3028 + 1.3029 +nsresult 1.3030 +Http2Session::SetPipelinePosition(int32_t position) 1.3031 +{ 1.3032 + // This API is meant for pipelining, Http2Session's should be 1.3033 + // extended with AddStream() 1.3034 + 1.3035 + MOZ_ASSERT(false, 1.3036 + "Http2Session::SetPipelinePosition() should not be called"); 1.3037 + 1.3038 + return NS_ERROR_NOT_IMPLEMENTED; 1.3039 +} 1.3040 + 1.3041 +int32_t 1.3042 +Http2Session::PipelinePosition() 1.3043 +{ 1.3044 + return 0; 1.3045 +} 1.3046 + 1.3047 +//----------------------------------------------------------------------------- 1.3048 +// Pass through methods of nsAHttpConnection 1.3049 +//----------------------------------------------------------------------------- 1.3050 + 1.3051 +nsAHttpConnection * 1.3052 +Http2Session::Connection() 1.3053 +{ 1.3054 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.3055 + return mConnection; 1.3056 +} 1.3057 + 1.3058 +nsresult 1.3059 +Http2Session::OnHeadersAvailable(nsAHttpTransaction *transaction, 1.3060 + nsHttpRequestHead *requestHead, 1.3061 + nsHttpResponseHead *responseHead, bool *reset) 1.3062 +{ 1.3063 + return mConnection->OnHeadersAvailable(transaction, 1.3064 + requestHead, 1.3065 + responseHead, 1.3066 + reset); 1.3067 +} 1.3068 + 1.3069 +bool 1.3070 +Http2Session::IsReused() 1.3071 +{ 1.3072 + return mConnection->IsReused(); 1.3073 +} 1.3074 + 1.3075 +nsresult 1.3076 +Http2Session::PushBack(const char *buf, uint32_t len) 1.3077 +{ 1.3078 + return mConnection->PushBack(buf, len); 1.3079 +} 1.3080 + 1.3081 +} // namespace mozilla::net 1.3082 +} // namespace mozilla