1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/SpdySession3.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2721 @@ 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 "mozilla/Endian.h" 1.20 +#include "mozilla/Telemetry.h" 1.21 +#include "nsHttp.h" 1.22 +#include "nsHttpHandler.h" 1.23 +#include "nsILoadGroup.h" 1.24 +#include "prprf.h" 1.25 +#include "SpdyPush3.h" 1.26 +#include "SpdySession3.h" 1.27 +#include "SpdyStream3.h" 1.28 +#include "PSpdyPush.h" 1.29 +#include "SpdyZlibReporter.h" 1.30 + 1.31 +#include <algorithm> 1.32 + 1.33 +#ifdef DEBUG 1.34 +// defined by the socket transport service while active 1.35 +extern PRThread *gSocketThread; 1.36 +#endif 1.37 + 1.38 +namespace mozilla { 1.39 +namespace net { 1.40 + 1.41 +// SpdySession3 has multiple inheritance of things that implement 1.42 +// nsISupports, so this magic is taken from nsHttpPipeline that 1.43 +// implements some of the same abstract classes. 1.44 +NS_IMPL_ADDREF(SpdySession3) 1.45 +NS_IMPL_RELEASE(SpdySession3) 1.46 +NS_INTERFACE_MAP_BEGIN(SpdySession3) 1.47 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection) 1.48 +NS_INTERFACE_MAP_END 1.49 + 1.50 +SpdySession3::SpdySession3(nsAHttpTransaction *aHttpTransaction, 1.51 + nsISocketTransport *aSocketTransport, 1.52 + int32_t firstPriority) 1.53 + : mSocketTransport(aSocketTransport), 1.54 + mSegmentReader(nullptr), 1.55 + mSegmentWriter(nullptr), 1.56 + mNextStreamID(1), 1.57 + mConcurrentHighWater(0), 1.58 + mDownstreamState(BUFFERING_FRAME_HEADER), 1.59 + mInputFrameBufferSize(kDefaultBufferSize), 1.60 + mInputFrameBufferUsed(0), 1.61 + mInputFrameDataLast(false), 1.62 + mInputFrameDataStream(nullptr), 1.63 + mNeedsCleanup(nullptr), 1.64 + mShouldGoAway(false), 1.65 + mClosed(false), 1.66 + mCleanShutdown(false), 1.67 + mDataPending(false), 1.68 + mGoAwayID(0), 1.69 + mMaxConcurrent(kDefaultMaxConcurrent), 1.70 + mConcurrent(0), 1.71 + mServerPushedResources(0), 1.72 + mServerInitialWindow(kDefaultServerRwin), 1.73 + mOutputQueueSize(kDefaultQueueSize), 1.74 + mOutputQueueUsed(0), 1.75 + mOutputQueueSent(0), 1.76 + mLastReadEpoch(PR_IntervalNow()), 1.77 + mPingSentEpoch(0), 1.78 + mNextPingID(1) 1.79 +{ 1.80 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.81 + 1.82 + static uint64_t sSerial; 1.83 + mSerial = ++sSerial; 1.84 + 1.85 + LOG3(("SpdySession3::SpdySession3 %p transaction 1 = %p serial=0x%X\n", 1.86 + this, aHttpTransaction, mSerial)); 1.87 + 1.88 + mConnection = aHttpTransaction->Connection(); 1.89 + mInputFrameBuffer = new char[mInputFrameBufferSize]; 1.90 + mOutputQueueBuffer = new char[mOutputQueueSize]; 1.91 + zlibInit(); 1.92 + 1.93 + mPushAllowance = gHttpHandler->SpdyPushAllowance(); 1.94 + mSendingChunkSize = gHttpHandler->SpdySendingChunkSize(); 1.95 + GenerateSettings(); 1.96 + 1.97 + if (!aHttpTransaction->IsNullTransaction()) 1.98 + AddStream(aHttpTransaction, firstPriority); 1.99 + mLastDataReadEpoch = mLastReadEpoch; 1.100 + 1.101 + mPingThreshold = gHttpHandler->SpdyPingThreshold(); 1.102 +} 1.103 + 1.104 +PLDHashOperator 1.105 +SpdySession3::ShutdownEnumerator(nsAHttpTransaction *key, 1.106 + nsAutoPtr<SpdyStream3> &stream, 1.107 + void *closure) 1.108 +{ 1.109 + SpdySession3 *self = static_cast<SpdySession3 *>(closure); 1.110 + 1.111 + // On a clean server hangup the server sets the GoAwayID to be the ID of 1.112 + // the last transaction it processed. If the ID of stream in the 1.113 + // local stream is greater than that it can safely be restarted because the 1.114 + // server guarantees it was not partially processed. Streams that have not 1.115 + // registered an ID haven't actually been sent yet so they can always be 1.116 + // restarted. 1.117 + if (self->mCleanShutdown && 1.118 + (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) 1.119 + self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted 1.120 + else 1.121 + self->CloseStream(stream, NS_ERROR_ABORT); 1.122 + 1.123 + return PL_DHASH_NEXT; 1.124 +} 1.125 + 1.126 +PLDHashOperator 1.127 +SpdySession3::GoAwayEnumerator(nsAHttpTransaction *key, 1.128 + nsAutoPtr<SpdyStream3> &stream, 1.129 + void *closure) 1.130 +{ 1.131 + SpdySession3 *self = static_cast<SpdySession3 *>(closure); 1.132 + 1.133 + // these streams were not processed by the server and can be restarted. 1.134 + // Do that after the enumerator completes to avoid the risk of 1.135 + // a restart event re-entrantly modifying this hash. Be sure not to restart 1.136 + // a pushed (even numbered) stream 1.137 + if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) || 1.138 + !stream->HasRegisteredID()) { 1.139 + self->mGoAwayStreamsToRestart.Push(stream); 1.140 + } 1.141 + 1.142 + return PL_DHASH_NEXT; 1.143 +} 1.144 + 1.145 +SpdySession3::~SpdySession3() 1.146 +{ 1.147 + LOG3(("SpdySession3::~SpdySession3 %p mDownstreamState=%X", 1.148 + this, mDownstreamState)); 1.149 + 1.150 + inflateEnd(&mDownstreamZlib); 1.151 + deflateEnd(&mUpstreamZlib); 1.152 + 1.153 + mStreamTransactionHash.Enumerate(ShutdownEnumerator, this); 1.154 + Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater); 1.155 + Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2); 1.156 + Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS, 1.157 + mServerPushedResources); 1.158 +} 1.159 + 1.160 +void 1.161 +SpdySession3::LogIO(SpdySession3 *self, SpdyStream3 *stream, const char *label, 1.162 + const char *data, uint32_t datalen) 1.163 +{ 1.164 + if (!LOG4_ENABLED()) 1.165 + return; 1.166 + 1.167 + LOG4(("SpdySession3::LogIO %p stream=%p id=0x%X [%s]", 1.168 + self, stream, stream ? stream->StreamID() : 0, label)); 1.169 + 1.170 + // Max line is (16 * 3) + 10(prefix) + newline + null 1.171 + char linebuf[128]; 1.172 + uint32_t index; 1.173 + char *line = linebuf; 1.174 + 1.175 + linebuf[127] = 0; 1.176 + 1.177 + for (index = 0; index < datalen; ++index) { 1.178 + if (!(index % 16)) { 1.179 + if (index) { 1.180 + *line = 0; 1.181 + LOG4(("%s", linebuf)); 1.182 + } 1.183 + line = linebuf; 1.184 + PR_snprintf(line, 128, "%08X: ", index); 1.185 + line += 10; 1.186 + } 1.187 + PR_snprintf(line, 128 - (line - linebuf), "%02X ", 1.188 + ((unsigned char *)data)[index]); 1.189 + line += 3; 1.190 + } 1.191 + if (index) { 1.192 + *line = 0; 1.193 + LOG4(("%s", linebuf)); 1.194 + } 1.195 +} 1.196 + 1.197 +bool 1.198 +SpdySession3::RoomForMoreConcurrent() 1.199 +{ 1.200 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.201 + 1.202 + return (mConcurrent < mMaxConcurrent); 1.203 +} 1.204 + 1.205 +bool 1.206 +SpdySession3::RoomForMoreStreams() 1.207 +{ 1.208 + if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID) 1.209 + return false; 1.210 + 1.211 + return !mShouldGoAway; 1.212 +} 1.213 + 1.214 +PRIntervalTime 1.215 +SpdySession3::IdleTime() 1.216 +{ 1.217 + return PR_IntervalNow() - mLastDataReadEpoch; 1.218 +} 1.219 + 1.220 +uint32_t 1.221 +SpdySession3::ReadTimeoutTick(PRIntervalTime now) 1.222 +{ 1.223 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.224 + MOZ_ASSERT(mNextPingID & 1, "Ping Counter Not Odd"); 1.225 + 1.226 + LOG(("SpdySession3::ReadTimeoutTick %p delta since last read %ds\n", 1.227 + this, PR_IntervalToSeconds(now - mLastReadEpoch))); 1.228 + 1.229 + if (!mPingThreshold) 1.230 + return UINT32_MAX; 1.231 + 1.232 + if ((now - mLastReadEpoch) < mPingThreshold) { 1.233 + // recent activity means ping is not an issue 1.234 + if (mPingSentEpoch) 1.235 + mPingSentEpoch = 0; 1.236 + 1.237 + return PR_IntervalToSeconds(mPingThreshold) - 1.238 + PR_IntervalToSeconds(now - mLastReadEpoch); 1.239 + } 1.240 + 1.241 + if (mPingSentEpoch) { 1.242 + LOG(("SpdySession3::ReadTimeoutTick %p handle outstanding ping\n")); 1.243 + if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) { 1.244 + LOG(("SpdySession3::ReadTimeoutTick %p Ping Timer Exhaustion\n", 1.245 + this)); 1.246 + mPingSentEpoch = 0; 1.247 + Close(NS_ERROR_NET_TIMEOUT); 1.248 + return UINT32_MAX; 1.249 + } 1.250 + return 1; // run the tick aggressively while ping is outstanding 1.251 + } 1.252 + 1.253 + LOG(("SpdySession3::ReadTimeoutTick %p generating ping 0x%X\n", 1.254 + this, mNextPingID)); 1.255 + 1.256 + if (mNextPingID == 0xffffffff) { 1.257 + LOG(("SpdySession3::ReadTimeoutTick %p cannot form ping - ids exhausted\n", 1.258 + this)); 1.259 + return UINT32_MAX; 1.260 + } 1.261 + 1.262 + mPingSentEpoch = PR_IntervalNow(); 1.263 + if (!mPingSentEpoch) 1.264 + mPingSentEpoch = 1; // avoid the 0 sentinel value 1.265 + GeneratePing(mNextPingID); 1.266 + mNextPingID += 2; 1.267 + ResumeRecv(); // read the ping reply 1.268 + 1.269 + // Check for orphaned push streams. This looks expensive, but generally the 1.270 + // list is empty. 1.271 + SpdyPushedStream3 *deleteMe; 1.272 + TimeStamp timestampNow; 1.273 + do { 1.274 + deleteMe = nullptr; 1.275 + 1.276 + for (uint32_t index = mPushedStreams.Length(); 1.277 + index > 0 ; --index) { 1.278 + SpdyPushedStream3 *pushedStream = mPushedStreams[index - 1]; 1.279 + 1.280 + if (timestampNow.IsNull()) 1.281 + timestampNow = TimeStamp::Now(); // lazy initializer 1.282 + 1.283 + // if spdy finished, but not connected, and its been like that for too long.. 1.284 + // cleanup the stream.. 1.285 + if (pushedStream->IsOrphaned(timestampNow)) 1.286 + { 1.287 + LOG3(("SpdySession3 Timeout Pushed Stream %p 0x%X\n", 1.288 + this, pushedStream->StreamID())); 1.289 + deleteMe = pushedStream; 1.290 + break; // don't CleanupStream() while iterating this vector 1.291 + } 1.292 + } 1.293 + if (deleteMe) 1.294 + CleanupStream(deleteMe, NS_ERROR_ABORT, RST_CANCEL); 1.295 + 1.296 + } while (deleteMe); 1.297 + 1.298 + if (mNextPingID == 0xffffffff) { 1.299 + LOG(("SpdySession3::ReadTimeoutTick %p " 1.300 + "ping ids exhausted marking goaway\n", this)); 1.301 + mShouldGoAway = true; 1.302 + } 1.303 + return 1; // run the tick aggressively while ping is outstanding 1.304 +} 1.305 + 1.306 +uint32_t 1.307 +SpdySession3::RegisterStreamID(SpdyStream3 *stream, uint32_t aNewID) 1.308 +{ 1.309 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.310 + 1.311 + MOZ_ASSERT(mNextStreamID < 0xfffffff0, 1.312 + "should have stopped admitting streams"); 1.313 + 1.314 + MOZ_ASSERT(!(aNewID & 1), 1.315 + "0 for autoassign pull, otherwise explicit even push assignment"); 1.316 + if (!aNewID) { 1.317 + // auto generate a new pull stream ID 1.318 + aNewID = mNextStreamID; 1.319 + MOZ_ASSERT(aNewID & 1, "pull ID must be odd."); 1.320 + mNextStreamID += 2; 1.321 + } 1.322 + 1.323 + LOG3(("SpdySession3::RegisterStreamID session=%p stream=%p id=0x%X " 1.324 + "concurrent=%d",this, stream, aNewID, mConcurrent)); 1.325 + 1.326 + // We've used up plenty of ID's on this session. Start 1.327 + // moving to a new one before there is a crunch involving 1.328 + // server push streams or concurrent non-registered submits 1.329 + if (aNewID >= kMaxStreamID) 1.330 + mShouldGoAway = true; 1.331 + 1.332 + // integrity check 1.333 + if (mStreamIDHash.Get(aNewID)) { 1.334 + LOG3((" New ID already present\n")); 1.335 + MOZ_ASSERT(false, "New ID already present in mStreamIDHash"); 1.336 + mShouldGoAway = true; 1.337 + return kDeadStreamID; 1.338 + } 1.339 + 1.340 + mStreamIDHash.Put(aNewID, stream); 1.341 + return aNewID; 1.342 +} 1.343 + 1.344 +bool 1.345 +SpdySession3::AddStream(nsAHttpTransaction *aHttpTransaction, 1.346 + int32_t aPriority) 1.347 +{ 1.348 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.349 + 1.350 + // integrity check 1.351 + if (mStreamTransactionHash.Get(aHttpTransaction)) { 1.352 + LOG3((" New transaction already present\n")); 1.353 + MOZ_ASSERT(false, "AddStream duplicate transaction pointer"); 1.354 + return false; 1.355 + } 1.356 + 1.357 + aHttpTransaction->SetConnection(this); 1.358 + SpdyStream3 *stream = new SpdyStream3(aHttpTransaction, this, aPriority); 1.359 + 1.360 + LOG3(("SpdySession3::AddStream session=%p stream=%p NextID=0x%X (tentative)", 1.361 + this, stream, mNextStreamID)); 1.362 + 1.363 + mStreamTransactionHash.Put(aHttpTransaction, stream); 1.364 + 1.365 + if (RoomForMoreConcurrent()) { 1.366 + LOG3(("SpdySession3::AddStream %p stream %p activated immediately.", 1.367 + this, stream)); 1.368 + ActivateStream(stream); 1.369 + } 1.370 + else { 1.371 + LOG3(("SpdySession3::AddStream %p stream %p queued.", this, stream)); 1.372 + mQueuedStreams.Push(stream); 1.373 + } 1.374 + 1.375 + if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) { 1.376 + LOG3(("SpdySession3::AddStream %p transaction %p forces keep-alive off.\n", 1.377 + this, aHttpTransaction)); 1.378 + DontReuse(); 1.379 + } 1.380 + return true; 1.381 +} 1.382 + 1.383 +void 1.384 +SpdySession3::ActivateStream(SpdyStream3 *stream) 1.385 +{ 1.386 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.387 + MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1), 1.388 + "Do not activate pushed streams"); 1.389 + 1.390 + ++mConcurrent; 1.391 + if (mConcurrent > mConcurrentHighWater) 1.392 + mConcurrentHighWater = mConcurrent; 1.393 + LOG3(("SpdySession3::AddStream %p activating stream %p Currently %d " 1.394 + "streams in session, high water mark is %d", 1.395 + this, stream, mConcurrent, mConcurrentHighWater)); 1.396 + 1.397 + mReadyForWrite.Push(stream); 1.398 + SetWriteCallbacks(); 1.399 + 1.400 + // Kick off the SYN transmit without waiting for the poll loop 1.401 + // This won't work for stream id=1 because there is no segment reader 1.402 + // yet. 1.403 + if (mSegmentReader) { 1.404 + uint32_t countRead; 1.405 + ReadSegments(nullptr, kDefaultBufferSize, &countRead); 1.406 + } 1.407 +} 1.408 + 1.409 +void 1.410 +SpdySession3::ProcessPending() 1.411 +{ 1.412 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.413 + 1.414 + while (RoomForMoreConcurrent()) { 1.415 + SpdyStream3 *stream = static_cast<SpdyStream3 *>(mQueuedStreams.PopFront()); 1.416 + if (!stream) 1.417 + return; 1.418 + LOG3(("SpdySession3::ProcessPending %p stream %p activated from queue.", 1.419 + this, stream)); 1.420 + ActivateStream(stream); 1.421 + } 1.422 +} 1.423 + 1.424 +nsresult 1.425 +SpdySession3::NetworkRead(nsAHttpSegmentWriter *writer, char *buf, 1.426 + uint32_t count, uint32_t *countWritten) 1.427 +{ 1.428 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.429 + 1.430 + if (!count) { 1.431 + *countWritten = 0; 1.432 + return NS_OK; 1.433 + } 1.434 + 1.435 + nsresult rv = writer->OnWriteSegment(buf, count, countWritten); 1.436 + if (NS_SUCCEEDED(rv) && *countWritten > 0) 1.437 + mLastReadEpoch = PR_IntervalNow(); 1.438 + return rv; 1.439 +} 1.440 + 1.441 +void 1.442 +SpdySession3::SetWriteCallbacks() 1.443 +{ 1.444 + if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed)) 1.445 + mConnection->ResumeSend(); 1.446 +} 1.447 + 1.448 +void 1.449 +SpdySession3::RealignOutputQueue() 1.450 +{ 1.451 + mOutputQueueUsed -= mOutputQueueSent; 1.452 + memmove(mOutputQueueBuffer.get(), 1.453 + mOutputQueueBuffer.get() + mOutputQueueSent, 1.454 + mOutputQueueUsed); 1.455 + mOutputQueueSent = 0; 1.456 +} 1.457 + 1.458 +void 1.459 +SpdySession3::FlushOutputQueue() 1.460 +{ 1.461 + if (!mSegmentReader || !mOutputQueueUsed) 1.462 + return; 1.463 + 1.464 + nsresult rv; 1.465 + uint32_t countRead; 1.466 + uint32_t avail = mOutputQueueUsed - mOutputQueueSent; 1.467 + 1.468 + rv = mSegmentReader-> 1.469 + OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail, 1.470 + &countRead); 1.471 + LOG3(("SpdySession3::FlushOutputQueue %p sz=%d rv=%x actual=%d", 1.472 + this, avail, rv, countRead)); 1.473 + 1.474 + // Dont worry about errors on write, we will pick this up as a read error too 1.475 + if (NS_FAILED(rv)) 1.476 + return; 1.477 + 1.478 + if (countRead == avail) { 1.479 + mOutputQueueUsed = 0; 1.480 + mOutputQueueSent = 0; 1.481 + return; 1.482 + } 1.483 + 1.484 + mOutputQueueSent += countRead; 1.485 + 1.486 + // If the output queue is close to filling up and we have sent out a good 1.487 + // chunk of data from the beginning then realign it. 1.488 + 1.489 + if ((mOutputQueueSent >= kQueueMinimumCleanup) && 1.490 + ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) { 1.491 + RealignOutputQueue(); 1.492 + } 1.493 +} 1.494 + 1.495 +void 1.496 +SpdySession3::DontReuse() 1.497 +{ 1.498 + mShouldGoAway = true; 1.499 + if (!mStreamTransactionHash.Count()) 1.500 + Close(NS_OK); 1.501 +} 1.502 + 1.503 +uint32_t 1.504 +SpdySession3::GetWriteQueueSize() 1.505 +{ 1.506 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.507 + 1.508 + return mReadyForWrite.GetSize(); 1.509 +} 1.510 + 1.511 +void 1.512 +SpdySession3::ChangeDownstreamState(enum stateType newState) 1.513 +{ 1.514 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.515 + 1.516 + LOG3(("SpdyStream3::ChangeDownstreamState() %p from %X to %X", 1.517 + this, mDownstreamState, newState)); 1.518 + mDownstreamState = newState; 1.519 +} 1.520 + 1.521 +void 1.522 +SpdySession3::ResetDownstreamState() 1.523 +{ 1.524 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.525 + 1.526 + LOG3(("SpdyStream3::ResetDownstreamState() %p", this)); 1.527 + ChangeDownstreamState(BUFFERING_FRAME_HEADER); 1.528 + 1.529 + if (mInputFrameDataLast && mInputFrameDataStream) { 1.530 + mInputFrameDataLast = false; 1.531 + if (!mInputFrameDataStream->RecvdFin()) { 1.532 + LOG3((" SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID())); 1.533 + mInputFrameDataStream->SetRecvdFin(true); 1.534 + DecrementConcurrent(mInputFrameDataStream); 1.535 + } 1.536 + } 1.537 + mInputFrameBufferUsed = 0; 1.538 + mInputFrameDataStream = nullptr; 1.539 +} 1.540 + 1.541 +template<typename T> void 1.542 +SpdySession3::EnsureBuffer(nsAutoArrayPtr<T> &buf, 1.543 + uint32_t newSize, 1.544 + uint32_t preserve, 1.545 + uint32_t &objSize) 1.546 +{ 1.547 + if (objSize >= newSize) 1.548 + return; 1.549 + 1.550 + // Leave a little slop on the new allocation - add 2KB to 1.551 + // what we need and then round the result up to a 4KB (page) 1.552 + // boundary. 1.553 + 1.554 + objSize = (newSize + 2048 + 4095) & ~4095; 1.555 + 1.556 + static_assert(sizeof(T) == 1, "sizeof(T) must be 1"); 1.557 + nsAutoArrayPtr<T> tmp(new T[objSize]); 1.558 + memcpy(tmp, buf, preserve); 1.559 + buf = tmp; 1.560 +} 1.561 + 1.562 +// Instantiate supported templates explicitly. 1.563 +template void 1.564 +SpdySession3::EnsureBuffer(nsAutoArrayPtr<char> &buf, 1.565 + uint32_t newSize, 1.566 + uint32_t preserve, 1.567 + uint32_t &objSize); 1.568 + 1.569 +template void 1.570 +SpdySession3::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf, 1.571 + uint32_t newSize, 1.572 + uint32_t preserve, 1.573 + uint32_t &objSize); 1.574 + 1.575 +void 1.576 +SpdySession3::DecrementConcurrent(SpdyStream3 *aStream) 1.577 +{ 1.578 + uint32_t id = aStream->StreamID(); 1.579 + 1.580 + if (id && !(id & 0x1)) 1.581 + return; // pushed streams aren't counted in concurrent limit 1.582 + 1.583 + MOZ_ASSERT(mConcurrent); 1.584 + --mConcurrent; 1.585 + LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n", 1.586 + this, id, mConcurrent)); 1.587 + ProcessPending(); 1.588 +} 1.589 + 1.590 +void 1.591 +SpdySession3::zlibInit() 1.592 +{ 1.593 + mDownstreamZlib.zalloc = SpdyZlibReporter::Alloc; 1.594 + mDownstreamZlib.zfree = SpdyZlibReporter::Free; 1.595 + mDownstreamZlib.opaque = Z_NULL; 1.596 + 1.597 + inflateInit(&mDownstreamZlib); 1.598 + 1.599 + mUpstreamZlib.zalloc = SpdyZlibReporter::Alloc; 1.600 + mUpstreamZlib.zfree = SpdyZlibReporter::Free; 1.601 + mUpstreamZlib.opaque = Z_NULL; 1.602 + 1.603 + // mixing carte blanche compression with tls subjects us to traffic 1.604 + // analysis attacks 1.605 + deflateInit(&mUpstreamZlib, Z_NO_COMPRESSION); 1.606 + deflateSetDictionary(&mUpstreamZlib, 1.607 + SpdyStream3::kDictionary, 1.608 + sizeof(SpdyStream3::kDictionary)); 1.609 +} 1.610 + 1.611 +// Need to decompress some data in order to keep the compression 1.612 +// context correct, but we really don't care what the result is 1.613 +nsresult 1.614 +SpdySession3::UncompressAndDiscard(uint32_t offset, 1.615 + uint32_t blockLen) 1.616 +{ 1.617 + char *blockStart = mInputFrameBuffer + offset; 1.618 + unsigned char trash[2048]; 1.619 + mDownstreamZlib.avail_in = blockLen; 1.620 + mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart); 1.621 + bool triedDictionary = false; 1.622 + 1.623 + do { 1.624 + mDownstreamZlib.next_out = trash; 1.625 + mDownstreamZlib.avail_out = sizeof(trash); 1.626 + int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH); 1.627 + 1.628 + if (zlib_rv == Z_NEED_DICT) { 1.629 + if (triedDictionary) { 1.630 + LOG3(("SpdySession3::UncompressAndDiscard %p Dictionary Error\n", this)); 1.631 + return NS_ERROR_ILLEGAL_VALUE; 1.632 + } 1.633 + 1.634 + triedDictionary = true; 1.635 + inflateSetDictionary(&mDownstreamZlib, SpdyStream3::kDictionary, 1.636 + sizeof(SpdyStream3::kDictionary)); 1.637 + } 1.638 + 1.639 + if (zlib_rv == Z_DATA_ERROR) 1.640 + return NS_ERROR_ILLEGAL_VALUE; 1.641 + 1.642 + if (zlib_rv == Z_MEM_ERROR) 1.643 + return NS_ERROR_FAILURE; 1.644 + } 1.645 + while (mDownstreamZlib.avail_in); 1.646 + return NS_OK; 1.647 +} 1.648 + 1.649 +void 1.650 +SpdySession3::GeneratePing(uint32_t aID) 1.651 +{ 1.652 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.653 + LOG3(("SpdySession3::GeneratePing %p 0x%X\n", this, aID)); 1.654 + 1.655 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12, 1.656 + mOutputQueueUsed, mOutputQueueSize); 1.657 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.658 + mOutputQueueUsed += 12; 1.659 + 1.660 + packet[0] = kFlag_Control; 1.661 + packet[1] = kVersion; 1.662 + packet[2] = 0; 1.663 + packet[3] = CONTROL_TYPE_PING; 1.664 + packet[4] = 0; /* flags */ 1.665 + packet[5] = 0; 1.666 + packet[6] = 0; 1.667 + packet[7] = 4; /* length */ 1.668 + 1.669 + NetworkEndian::writeUint32(packet + 8, aID); 1.670 + 1.671 + LogIO(this, nullptr, "Generate Ping", packet, 12); 1.672 + FlushOutputQueue(); 1.673 +} 1.674 + 1.675 +void 1.676 +SpdySession3::GenerateRstStream(uint32_t aStatusCode, uint32_t aID) 1.677 +{ 1.678 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.679 + LOG3(("SpdySession3::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode)); 1.680 + 1.681 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16, 1.682 + mOutputQueueUsed, mOutputQueueSize); 1.683 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.684 + mOutputQueueUsed += 16; 1.685 + 1.686 + packet[0] = kFlag_Control; 1.687 + packet[1] = kVersion; 1.688 + packet[2] = 0; 1.689 + packet[3] = CONTROL_TYPE_RST_STREAM; 1.690 + packet[4] = 0; /* flags */ 1.691 + packet[5] = 0; 1.692 + packet[6] = 0; 1.693 + packet[7] = 8; /* length */ 1.694 + 1.695 + NetworkEndian::writeUint32(packet + 8, aID); 1.696 + NetworkEndian::writeUint32(packet + 12, aStatusCode); 1.697 + 1.698 + LogIO(this, nullptr, "Generate Reset", packet, 16); 1.699 + FlushOutputQueue(); 1.700 +} 1.701 + 1.702 +void 1.703 +SpdySession3::GenerateGoAway(uint32_t aStatusCode) 1.704 +{ 1.705 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.706 + LOG3(("SpdySession3::GenerateGoAway %p code=%X\n", this, aStatusCode)); 1.707 + 1.708 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16, 1.709 + mOutputQueueUsed, mOutputQueueSize); 1.710 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.711 + mOutputQueueUsed += 16; 1.712 + 1.713 + memset(packet, 0, 16); 1.714 + packet[0] = kFlag_Control; 1.715 + packet[1] = kVersion; 1.716 + packet[3] = CONTROL_TYPE_GOAWAY; 1.717 + packet[7] = 8; /* data length */ 1.718 + 1.719 + // last-good-stream-id are bytes 8-11, when we accept server push this will 1.720 + // need to be set non zero 1.721 + 1.722 + // bytes 12-15 are the status code. 1.723 + NetworkEndian::writeUint32(packet + 12, aStatusCode); 1.724 + 1.725 + LogIO(this, nullptr, "Generate GoAway", packet, 16); 1.726 + FlushOutputQueue(); 1.727 +} 1.728 + 1.729 +void 1.730 +SpdySession3::GenerateSettings() 1.731 +{ 1.732 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.733 + LOG3(("SpdySession3::GenerateSettings %p\n", this)); 1.734 + 1.735 + static const uint32_t maxDataLen = 4 + 3 * 8; // sized for 3 settings 1.736 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + maxDataLen, 1.737 + mOutputQueueUsed, mOutputQueueSize); 1.738 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.739 + 1.740 + memset(packet, 0, 8 + maxDataLen); 1.741 + packet[0] = kFlag_Control; 1.742 + packet[1] = kVersion; 1.743 + packet[3] = CONTROL_TYPE_SETTINGS; 1.744 + 1.745 + uint8_t numberOfEntries = 0; 1.746 + 1.747 + // entries need to be listed in order by ID 1.748 + // 1st entry is bytes 12 to 19 1.749 + // 2nd entry is bytes 20 to 27 1.750 + // 3rd entry is bytes 28 to 35 1.751 + 1.752 + if (!gHttpHandler->AllowPush()) { 1.753 + // announcing that we accept 0 incoming streams is done to 1.754 + // disable server push 1.755 + packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT; 1.756 + // The value portion of the setting pair is already initialized to 0 1.757 + numberOfEntries++; 1.758 + } 1.759 + 1.760 + nsRefPtr<nsHttpConnectionInfo> ci; 1.761 + uint32_t cwnd = 0; 1.762 + GetConnectionInfo(getter_AddRefs(ci)); 1.763 + if (ci) 1.764 + cwnd = gHttpHandler->ConnMgr()->GetSpdyCWNDSetting(ci); 1.765 + if (cwnd) { 1.766 + packet[12 + 8 * numberOfEntries] = PERSISTED_VALUE; 1.767 + packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_CWND; 1.768 + LOG(("SpdySession3::GenerateSettings %p sending CWND %u\n", this, cwnd)); 1.769 + NetworkEndian::writeUint32(packet + 16 + 8 * numberOfEntries, cwnd); 1.770 + numberOfEntries++; 1.771 + } 1.772 + 1.773 + // Advertise the Push RWIN and on each client SYN_STREAM pipeline 1.774 + // a window update with it in order to use larger initial windows with pulled 1.775 + // streams. 1.776 + packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW; 1.777 + NetworkEndian::writeUint32(packet + 16 + 8 * numberOfEntries, mPushAllowance); 1.778 + numberOfEntries++; 1.779 + 1.780 + uint32_t dataLen = 4 + 8 * numberOfEntries; 1.781 + mOutputQueueUsed += 8 + dataLen; 1.782 + packet[7] = dataLen; 1.783 + packet[11] = numberOfEntries; 1.784 + 1.785 + LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen); 1.786 + FlushOutputQueue(); 1.787 +} 1.788 + 1.789 +// perform a bunch of integrity checks on the stream. 1.790 +// returns true if passed, false (plus LOG and ABORT) if failed. 1.791 +bool 1.792 +SpdySession3::VerifyStream(SpdyStream3 *aStream, uint32_t aOptionalID = 0) 1.793 +{ 1.794 + // This is annoying, but at least it is O(1) 1.795 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.796 + 1.797 +#ifndef DEBUG 1.798 + // Only do the real verification in debug builds 1.799 + return true; 1.800 +#endif 1.801 + 1.802 + if (!aStream) 1.803 + return true; 1.804 + 1.805 + uint32_t test = 0; 1.806 + 1.807 + do { 1.808 + if (aStream->StreamID() == kDeadStreamID) 1.809 + break; 1.810 + 1.811 + nsAHttpTransaction *trans = aStream->Transaction(); 1.812 + 1.813 + test++; 1.814 + if (!trans) 1.815 + break; 1.816 + 1.817 + test++; 1.818 + if (mStreamTransactionHash.Get(trans) != aStream) 1.819 + break; 1.820 + 1.821 + if (aStream->StreamID()) { 1.822 + SpdyStream3 *idStream = mStreamIDHash.Get(aStream->StreamID()); 1.823 + 1.824 + test++; 1.825 + if (idStream != aStream) 1.826 + break; 1.827 + 1.828 + if (aOptionalID) { 1.829 + test++; 1.830 + if (idStream->StreamID() != aOptionalID) 1.831 + break; 1.832 + } 1.833 + } 1.834 + 1.835 + // tests passed 1.836 + return true; 1.837 + } while (0); 1.838 + 1.839 + LOG(("SpdySession3 %p VerifyStream Failure %p stream->id=0x%X " 1.840 + "optionalID=0x%X trans=%p test=%d\n", 1.841 + this, aStream, aStream->StreamID(), 1.842 + aOptionalID, aStream->Transaction(), test)); 1.843 + 1.844 + MOZ_ASSERT(false, "VerifyStream"); 1.845 + return false; 1.846 +} 1.847 + 1.848 +void 1.849 +SpdySession3::CleanupStream(SpdyStream3 *aStream, nsresult aResult, 1.850 + rstReason aResetCode) 1.851 +{ 1.852 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.853 + LOG3(("SpdySession3::CleanupStream %p %p 0x%X %X\n", 1.854 + this, aStream, aStream ? aStream->StreamID() : 0, aResult)); 1.855 + if (!aStream) { 1.856 + return; 1.857 + } 1.858 + 1.859 + SpdyPushedStream3 *pushSource = nullptr; 1.860 + 1.861 + if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) { 1.862 + LOG(("SpdySession3::CleanupStream 0x%X deferred\n", aStream->StreamID())); 1.863 + return; 1.864 + } 1.865 + 1.866 + if (!VerifyStream(aStream)) { 1.867 + LOG(("SpdySession3::CleanupStream failed to verify stream\n")); 1.868 + return; 1.869 + } 1.870 + 1.871 + pushSource = aStream->PushSource(); 1.872 + 1.873 + if (!aStream->RecvdFin() && aStream->StreamID()) { 1.874 + LOG3(("Stream had not processed recv FIN, sending RST code %X\n", 1.875 + aResetCode)); 1.876 + GenerateRstStream(aResetCode, aStream->StreamID()); 1.877 + DecrementConcurrent(aStream); 1.878 + } 1.879 + 1.880 + CloseStream(aStream, aResult); 1.881 + 1.882 + // Remove the stream from the ID hash table and, if an even id, the pushed 1.883 + // table too. 1.884 + uint32_t id = aStream->StreamID(); 1.885 + if (id > 0) { 1.886 + mStreamIDHash.Remove(id); 1.887 + if (!(id & 1)) 1.888 + mPushedStreams.RemoveElement(aStream); 1.889 + } 1.890 + 1.891 + RemoveStreamFromQueues(aStream); 1.892 + 1.893 + // removing from the stream transaction hash will 1.894 + // delete the SpdyStream3 and drop the reference to 1.895 + // its transaction 1.896 + mStreamTransactionHash.Remove(aStream->Transaction()); 1.897 + 1.898 + if (mShouldGoAway && !mStreamTransactionHash.Count()) 1.899 + Close(NS_OK); 1.900 + 1.901 + if (pushSource) { 1.902 + pushSource->SetDeferCleanupOnSuccess(false); 1.903 + CleanupStream(pushSource, aResult, aResetCode); 1.904 + } 1.905 +} 1.906 + 1.907 +static void RemoveStreamFromQueue(SpdyStream3 *aStream, nsDeque &queue) 1.908 +{ 1.909 + uint32_t size = queue.GetSize(); 1.910 + for (uint32_t count = 0; count < size; ++count) { 1.911 + SpdyStream3 *stream = static_cast<SpdyStream3 *>(queue.PopFront()); 1.912 + if (stream != aStream) 1.913 + queue.Push(stream); 1.914 + } 1.915 +} 1.916 + 1.917 +void 1.918 +SpdySession3::RemoveStreamFromQueues(SpdyStream3 *aStream) 1.919 +{ 1.920 + RemoveStreamFromQueue(aStream, mReadyForWrite); 1.921 + RemoveStreamFromQueue(aStream, mQueuedStreams); 1.922 + RemoveStreamFromQueue(aStream, mReadyForRead); 1.923 +} 1.924 + 1.925 +void 1.926 +SpdySession3::CloseStream(SpdyStream3 *aStream, nsresult aResult) 1.927 +{ 1.928 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.929 + LOG3(("SpdySession3::CloseStream %p %p 0x%x %X\n", 1.930 + this, aStream, aStream->StreamID(), aResult)); 1.931 + 1.932 + // Check if partial frame reader 1.933 + if (aStream == mInputFrameDataStream) { 1.934 + LOG3(("Stream had active partial read frame on close")); 1.935 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.936 + mInputFrameDataStream = nullptr; 1.937 + } 1.938 + 1.939 + RemoveStreamFromQueues(aStream); 1.940 + 1.941 + // Send the stream the close() indication 1.942 + aStream->Close(aResult); 1.943 +} 1.944 + 1.945 +nsresult 1.946 +SpdySession3::HandleSynStream(SpdySession3 *self) 1.947 +{ 1.948 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM); 1.949 + 1.950 + if (self->mInputFrameDataSize < 18) { 1.951 + LOG3(("SpdySession3::HandleSynStream %p SYN_STREAM too short data=%d", 1.952 + self, self->mInputFrameDataSize)); 1.953 + return NS_ERROR_ILLEGAL_VALUE; 1.954 + } 1.955 + 1.956 + uint32_t streamID = 1.957 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.958 + uint32_t associatedID = 1.959 + NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t)); 1.960 + uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4]; 1.961 + 1.962 + LOG3(("SpdySession3::HandleSynStream %p recv SYN_STREAM (push) " 1.963 + "for ID 0x%X associated with 0x%X.\n", 1.964 + self, streamID, associatedID)); 1.965 + 1.966 + if (streamID & 0x01) { // test for odd stream ID 1.967 + LOG3(("SpdySession3::HandleSynStream %p recvd SYN_STREAM id must be even.", 1.968 + self)); 1.969 + return NS_ERROR_ILLEGAL_VALUE; 1.970 + } 1.971 + 1.972 + // confirm associated-to 1.973 + nsresult rv = self->SetInputFrameDataStream(associatedID); 1.974 + if (NS_FAILED(rv)) 1.975 + return rv; 1.976 + SpdyStream3 *associatedStream = self->mInputFrameDataStream; 1.977 + 1.978 + ++(self->mServerPushedResources); 1.979 + 1.980 + // Anytime we start using the high bit of stream ID (either client or server) 1.981 + // begin to migrate to a new session. 1.982 + if (streamID >= kMaxStreamID) 1.983 + self->mShouldGoAway = true; 1.984 + 1.985 + bool resetStream = true; 1.986 + SpdyPushCache *cache = nullptr; 1.987 + 1.988 + if (!(flags & kFlag_Data_UNI)) { 1.989 + // pushed streams require UNIDIRECTIONAL flag 1.990 + LOG3(("SpdySession3::HandleSynStream %p ID %0x%X associated ID 0x%X failed.\n", 1.991 + self, streamID, associatedID)); 1.992 + self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID); 1.993 + 1.994 + } else if (!associatedID) { 1.995 + // associated stream 0 will never find a match, but the spec requires a 1.996 + // PROTOCOL_ERROR in this specific case 1.997 + LOG3(("SpdySession3::HandleSynStream %p associated ID of 0 failed.\n", self)); 1.998 + self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID); 1.999 + 1.1000 + } else if (!gHttpHandler->AllowPush()) { 1.1001 + // MAX_CONCURRENT_STREAMS of 0 in settings should have disabled push, 1.1002 + // but some servers are buggy about that.. or the config could have 1.1003 + // been updated after the settings frame was sent. In both cases just 1.1004 + // reject the pushed stream as refused 1.1005 + LOG3(("SpdySession3::HandleSynStream Push Recevied when Disabled\n")); 1.1006 + self->GenerateRstStream(RST_REFUSED_STREAM, streamID); 1.1007 + 1.1008 + } else if (!associatedStream) { 1.1009 + LOG3(("SpdySession3::HandleSynStream %p lookup associated ID failed.\n", self)); 1.1010 + self->GenerateRstStream(RST_INVALID_STREAM, streamID); 1.1011 + 1.1012 + } else { 1.1013 + nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo(); 1.1014 + if (loadGroupCI) { 1.1015 + loadGroupCI->GetSpdyPushCache(&cache); 1.1016 + if (!cache) { 1.1017 + cache = new SpdyPushCache(); 1.1018 + if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) { 1.1019 + delete cache; 1.1020 + cache = nullptr; 1.1021 + } 1.1022 + } 1.1023 + } 1.1024 + if (!cache) { 1.1025 + // this is unexpected, but we can handle it just be refusing the push 1.1026 + LOG3(("SpdySession3::HandleSynStream Push Recevied without loadgroup cache\n")); 1.1027 + self->GenerateRstStream(RST_REFUSED_STREAM, streamID); 1.1028 + } 1.1029 + else { 1.1030 + resetStream = false; 1.1031 + } 1.1032 + } 1.1033 + 1.1034 + if (resetStream) { 1.1035 + // Need to decompress the headers even though we aren't using them yet in 1.1036 + // order to keep the compression context consistent for other syn_reply frames 1.1037 + rv = self->UncompressAndDiscard(18, self->mInputFrameDataSize - 10); 1.1038 + if (NS_FAILED(rv)) { 1.1039 + LOG(("SpdySession3::HandleSynStream uncompress failed\n")); 1.1040 + return rv; 1.1041 + } 1.1042 + self->ResetDownstreamState(); 1.1043 + return NS_OK; 1.1044 + } 1.1045 + 1.1046 + // Create the buffering transaction and push stream 1.1047 + nsRefPtr<SpdyPush3TransactionBuffer> transactionBuffer = 1.1048 + new SpdyPush3TransactionBuffer(); 1.1049 + transactionBuffer->SetConnection(self); 1.1050 + SpdyPushedStream3 *pushedStream = 1.1051 + new SpdyPushedStream3(transactionBuffer, self, 1.1052 + associatedStream, streamID); 1.1053 + 1.1054 + // ownership of the pushed stream is by the transaction hash, just as it 1.1055 + // is for a client initiated stream. Errors that aren't fatal to the 1.1056 + // whole session must call cleanupStream() after this point in order 1.1057 + // to remove the stream from that hash. 1.1058 + self->mStreamTransactionHash.Put(transactionBuffer, pushedStream); 1.1059 + self->mPushedStreams.AppendElement(pushedStream); 1.1060 + 1.1061 + // The pushed stream is unidirectional so it is fully open immediately 1.1062 + pushedStream->SetFullyOpen(); 1.1063 + 1.1064 + // Uncompress the response headers into a stream specific buffer, leaving them 1.1065 + // in spdy format for the time being. 1.1066 + rv = pushedStream->Uncompress(&self->mDownstreamZlib, 1.1067 + self->mInputFrameBuffer + 18, 1.1068 + self->mInputFrameDataSize - 10); 1.1069 + if (NS_FAILED(rv)) { 1.1070 + LOG(("SpdySession3::HandleSynStream uncompress failed\n")); 1.1071 + return rv; 1.1072 + } 1.1073 + 1.1074 + if (self->RegisterStreamID(pushedStream, streamID) == kDeadStreamID) { 1.1075 + LOG(("SpdySession3::HandleSynStream registerstreamid failed\n")); 1.1076 + return NS_ERROR_FAILURE; 1.1077 + } 1.1078 + 1.1079 + // Fake the request side of the pushed HTTP transaction. Sets up hash 1.1080 + // key and origin 1.1081 + uint32_t notUsed; 1.1082 + pushedStream->ReadSegments(nullptr, 1, ¬Used); 1.1083 + 1.1084 + nsAutoCString key; 1.1085 + if (!pushedStream->GetHashKey(key)) { 1.1086 + LOG(("SpdySession3::HandleSynStream one of :host :scheme :path missing from push\n")); 1.1087 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM); 1.1088 + self->ResetDownstreamState(); 1.1089 + return NS_OK; 1.1090 + } 1.1091 + 1.1092 + if (!associatedStream->Origin().Equals(pushedStream->Origin())) { 1.1093 + LOG(("SpdySession3::HandleSynStream pushed stream mismatched origin\n")); 1.1094 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM); 1.1095 + self->ResetDownstreamState(); 1.1096 + return NS_OK; 1.1097 + } 1.1098 + 1.1099 + if (!cache->RegisterPushedStreamSpdy3(key, pushedStream)) { 1.1100 + LOG(("SpdySession3::HandleSynStream registerPushedStream Failed\n")); 1.1101 + self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM); 1.1102 + self->ResetDownstreamState(); 1.1103 + return NS_OK; 1.1104 + } 1.1105 + 1.1106 + self->ResetDownstreamState(); 1.1107 + return NS_OK; 1.1108 +} 1.1109 + 1.1110 +nsresult 1.1111 +SpdySession3::SetInputFrameDataStream(uint32_t streamID) 1.1112 +{ 1.1113 + mInputFrameDataStream = mStreamIDHash.Get(streamID); 1.1114 + if (VerifyStream(mInputFrameDataStream, streamID)) 1.1115 + return NS_OK; 1.1116 + 1.1117 + LOG(("SpdySession3::SetInputFrameDataStream failed to verify 0x%X\n", 1.1118 + streamID)); 1.1119 + mInputFrameDataStream = nullptr; 1.1120 + return NS_ERROR_UNEXPECTED; 1.1121 +} 1.1122 + 1.1123 +nsresult 1.1124 +SpdySession3::HandleSynReply(SpdySession3 *self) 1.1125 +{ 1.1126 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY); 1.1127 + 1.1128 + if (self->mInputFrameDataSize < 4) { 1.1129 + LOG3(("SpdySession3::HandleSynReply %p SYN REPLY too short data=%d", 1.1130 + self, self->mInputFrameDataSize)); 1.1131 + // A framing error is a session wide error that cannot be recovered 1.1132 + return NS_ERROR_ILLEGAL_VALUE; 1.1133 + } 1.1134 + 1.1135 + LOG3(("SpdySession3::HandleSynReply %p lookup via streamID in syn_reply.\n", 1.1136 + self)); 1.1137 + uint32_t streamID = 1.1138 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1139 + nsresult rv = self->SetInputFrameDataStream(streamID); 1.1140 + if (NS_FAILED(rv)) 1.1141 + return rv; 1.1142 + 1.1143 + if (!self->mInputFrameDataStream) { 1.1144 + // Cannot find stream. We can continue the SPDY session, but we need to 1.1145 + // uncompress the header block to maintain the correct compression context 1.1146 + 1.1147 + LOG3(("SpdySession3::HandleSynReply %p lookup streamID in syn_reply " 1.1148 + "0x%X failed. NextStreamID = 0x%X\n", 1.1149 + self, streamID, self->mNextStreamID)); 1.1150 + 1.1151 + if (streamID >= self->mNextStreamID) 1.1152 + self->GenerateRstStream(RST_INVALID_STREAM, streamID); 1.1153 + 1.1154 + rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4); 1.1155 + if (NS_FAILED(rv)) { 1.1156 + LOG(("SpdySession3::HandleSynReply uncompress failed\n")); 1.1157 + // this is fatal to the session 1.1158 + return rv; 1.1159 + } 1.1160 + 1.1161 + self->ResetDownstreamState(); 1.1162 + return NS_OK; 1.1163 + } 1.1164 + 1.1165 + // Uncompress the headers into a stream specific buffer, leaving them in 1.1166 + // spdy format for the time being. Make certain to do this 1.1167 + // step before any error handling that might abort the stream but not 1.1168 + // the session becuase the session compression context will become 1.1169 + // inconsistent if all of the compressed data is not processed. 1.1170 + rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib, 1.1171 + self->mInputFrameBuffer + 12, 1.1172 + self->mInputFrameDataSize - 4); 1.1173 + 1.1174 + if (NS_FAILED(rv)) { 1.1175 + LOG(("SpdySession3::HandleSynReply uncompress failed\n")); 1.1176 + return rv; 1.1177 + } 1.1178 + 1.1179 + if (self->mInputFrameDataStream->GetFullyOpen()) { 1.1180 + // "If an endpoint receives multiple SYN_REPLY frames for the same active 1.1181 + // stream ID, it MUST issue a stream error (Section 2.4.2) with the error 1.1182 + // code STREAM_IN_USE." 1.1183 + // 1.1184 + // "STREAM_ALREADY_CLOSED. The endpoint received a data or SYN_REPLY 1.1185 + // frame for a stream which is half closed." 1.1186 + // 1.1187 + // If the stream is open then just RST_STREAM with STREAM_IN_USE 1.1188 + // If the stream is half closed then RST_STREAM with STREAM_ALREADY_CLOSED 1.1189 + // abort the session 1.1190 + // 1.1191 + LOG3(("SpdySession3::HandleSynReply %p dup SYN_REPLY for 0x%X" 1.1192 + " recvdfin=%d", self, self->mInputFrameDataStream->StreamID(), 1.1193 + self->mInputFrameDataStream->RecvdFin())); 1.1194 + 1.1195 + self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ALREADY_OPENED, 1.1196 + self->mInputFrameDataStream->RecvdFin() ? 1.1197 + RST_STREAM_ALREADY_CLOSED : RST_STREAM_IN_USE); 1.1198 + self->ResetDownstreamState(); 1.1199 + return NS_OK; 1.1200 + } 1.1201 + self->mInputFrameDataStream->SetFullyOpen(); 1.1202 + 1.1203 + self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN; 1.1204 + self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize); 1.1205 + self->mLastDataReadEpoch = self->mLastReadEpoch; 1.1206 + 1.1207 + if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) { 1.1208 + LOG3(("SynReply %p had undefined flag set 0x%X\n", self, streamID)); 1.1209 + self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE, 1.1210 + RST_PROTOCOL_ERROR); 1.1211 + self->ResetDownstreamState(); 1.1212 + return NS_OK; 1.1213 + } 1.1214 + 1.1215 + if (!self->mInputFrameDataLast) { 1.1216 + // don't process the headers yet as there could be more coming from HEADERS 1.1217 + // frames 1.1218 + self->ResetDownstreamState(); 1.1219 + return NS_OK; 1.1220 + } 1.1221 + 1.1222 + rv = self->ResponseHeadersComplete(); 1.1223 + if (rv == NS_ERROR_ILLEGAL_VALUE) { 1.1224 + LOG3(("SpdySession3::HandleSynReply %p PROTOCOL_ERROR detected 0x%X\n", 1.1225 + self, streamID)); 1.1226 + self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR); 1.1227 + self->ResetDownstreamState(); 1.1228 + rv = NS_OK; 1.1229 + } 1.1230 + return rv; 1.1231 +} 1.1232 + 1.1233 +// ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream 1.1234 +// should be reset with a PROTOCOL_ERROR, NS_OK when the SYN_REPLY was 1.1235 +// fine, and any other error is fatal to the session. 1.1236 +nsresult 1.1237 +SpdySession3::ResponseHeadersComplete() 1.1238 +{ 1.1239 + LOG3(("SpdySession3::ResponseHeadersComplete %p for 0x%X fin=%d", 1.1240 + this, mInputFrameDataStream->StreamID(), mInputFrameDataLast)); 1.1241 + 1.1242 + // The spdystream needs to see flattened http headers 1.1243 + // Uncompressed spdy format headers currently live in 1.1244 + // SpdyStream3::mDecompressBuffer - convert that to HTTP format in 1.1245 + // mFlatHTTPResponseHeaders via ConvertHeaders() 1.1246 + 1.1247 + mFlatHTTPResponseHeadersOut = 0; 1.1248 + nsresult rv = mInputFrameDataStream->ConvertHeaders(mFlatHTTPResponseHeaders); 1.1249 + if (NS_FAILED(rv)) 1.1250 + return rv; 1.1251 + 1.1252 + ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS); 1.1253 + return NS_OK; 1.1254 +} 1.1255 + 1.1256 +nsresult 1.1257 +SpdySession3::HandleRstStream(SpdySession3 *self) 1.1258 +{ 1.1259 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_RST_STREAM); 1.1260 + 1.1261 + if (self->mInputFrameDataSize != 8) { 1.1262 + LOG3(("SpdySession3::HandleRstStream %p RST_STREAM wrong length data=%d", 1.1263 + self, self->mInputFrameDataSize)); 1.1264 + return NS_ERROR_ILLEGAL_VALUE; 1.1265 + } 1.1266 + 1.1267 + uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4]; 1.1268 + 1.1269 + uint32_t streamID = 1.1270 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1271 + 1.1272 + self->mDownstreamRstReason = 1.1273 + NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t)); 1.1274 + 1.1275 + LOG3(("SpdySession3::HandleRstStream %p RST_STREAM Reason Code %u ID %x " 1.1276 + "flags %x", self, self->mDownstreamRstReason, streamID, flags)); 1.1277 + 1.1278 + if (flags != 0) { 1.1279 + LOG3(("SpdySession3::HandleRstStream %p RST_STREAM with flags is illegal", 1.1280 + self)); 1.1281 + return NS_ERROR_ILLEGAL_VALUE; 1.1282 + } 1.1283 + 1.1284 + if (self->mDownstreamRstReason == RST_INVALID_STREAM || 1.1285 + self->mDownstreamRstReason == RST_STREAM_IN_USE || 1.1286 + self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) { 1.1287 + // basically just ignore this 1.1288 + LOG3(("SpdySession3::HandleRstStream %p No Reset Processing Needed.\n")); 1.1289 + self->ResetDownstreamState(); 1.1290 + return NS_OK; 1.1291 + } 1.1292 + 1.1293 + nsresult rv = self->SetInputFrameDataStream(streamID); 1.1294 + 1.1295 + if (!self->mInputFrameDataStream) { 1.1296 + if (NS_FAILED(rv)) 1.1297 + LOG(("SpdySession3::HandleRstStream %p lookup streamID for RST Frame " 1.1298 + "0x%X failed reason = %d :: VerifyStream Failed\n", self, streamID, 1.1299 + self->mDownstreamRstReason)); 1.1300 + 1.1301 + LOG3(("SpdySession3::HandleRstStream %p lookup streamID for RST Frame " 1.1302 + "0x%X failed reason = %d", self, streamID, 1.1303 + self->mDownstreamRstReason)); 1.1304 + return NS_ERROR_ILLEGAL_VALUE; 1.1305 + } 1.1306 + 1.1307 + self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM); 1.1308 + return NS_OK; 1.1309 +} 1.1310 + 1.1311 +PLDHashOperator 1.1312 +SpdySession3::UpdateServerRwinEnumerator(nsAHttpTransaction *key, 1.1313 + nsAutoPtr<SpdyStream3> &stream, 1.1314 + void *closure) 1.1315 +{ 1.1316 + int32_t delta = *(static_cast<int32_t *>(closure)); 1.1317 + stream->UpdateRemoteWindow(delta); 1.1318 + return PL_DHASH_NEXT; 1.1319 +} 1.1320 + 1.1321 +nsresult 1.1322 +SpdySession3::HandleSettings(SpdySession3 *self) 1.1323 +{ 1.1324 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SETTINGS); 1.1325 + 1.1326 + if (self->mInputFrameDataSize < 4) { 1.1327 + LOG3(("SpdySession3::HandleSettings %p SETTINGS wrong length data=%d", 1.1328 + self, self->mInputFrameDataSize)); 1.1329 + return NS_ERROR_ILLEGAL_VALUE; 1.1330 + } 1.1331 + 1.1332 + uint32_t numEntries = 1.1333 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1334 + 1.1335 + // Ensure frame is large enough for supplied number of entries 1.1336 + // Each entry is 8 bytes, frame data is reduced by 4 to account for 1.1337 + // the NumEntries value. 1.1338 + if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) { 1.1339 + LOG3(("SpdySession3::HandleSettings %p SETTINGS wrong length data=%d", 1.1340 + self, self->mInputFrameDataSize)); 1.1341 + return NS_ERROR_ILLEGAL_VALUE; 1.1342 + } 1.1343 + 1.1344 + LOG3(("SpdySession3::HandleSettings %p SETTINGS Control Frame with %d entries", 1.1345 + self, numEntries)); 1.1346 + 1.1347 + for (uint32_t index = 0; index < numEntries; ++index) { 1.1348 + unsigned char *setting = reinterpret_cast<unsigned char *> 1.1349 + (self->mInputFrameBuffer.get()) + 12 + index * 8; 1.1350 + 1.1351 + uint32_t flags = setting[0]; 1.1352 + uint32_t id = NetworkEndian::readUint32(setting) & 0xffffff; 1.1353 + uint32_t value = NetworkEndian::readUint32(setting + 1 * sizeof(uint32_t)); 1.1354 + 1.1355 + LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value)); 1.1356 + 1.1357 + switch (id) 1.1358 + { 1.1359 + case SETTINGS_TYPE_UPLOAD_BW: 1.1360 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value); 1.1361 + break; 1.1362 + 1.1363 + case SETTINGS_TYPE_DOWNLOAD_BW: 1.1364 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value); 1.1365 + break; 1.1366 + 1.1367 + case SETTINGS_TYPE_RTT: 1.1368 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value); 1.1369 + break; 1.1370 + 1.1371 + case SETTINGS_TYPE_MAX_CONCURRENT: 1.1372 + self->mMaxConcurrent = value; 1.1373 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value); 1.1374 + break; 1.1375 + 1.1376 + case SETTINGS_TYPE_CWND: 1.1377 + if (flags & PERSIST_VALUE) 1.1378 + { 1.1379 + nsRefPtr<nsHttpConnectionInfo> ci; 1.1380 + self->GetConnectionInfo(getter_AddRefs(ci)); 1.1381 + if (ci) 1.1382 + gHttpHandler->ConnMgr()->ReportSpdyCWNDSetting(ci, value); 1.1383 + } 1.1384 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value); 1.1385 + break; 1.1386 + 1.1387 + case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE: 1.1388 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value); 1.1389 + break; 1.1390 + 1.1391 + case SETTINGS_TYPE_INITIAL_WINDOW: 1.1392 + Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10); 1.1393 + { 1.1394 + int32_t delta = value - self->mServerInitialWindow; 1.1395 + self->mServerInitialWindow = value; 1.1396 + 1.1397 + // we need to add the delta to all open streams (delta can be negative) 1.1398 + self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator, 1.1399 + &delta); 1.1400 + } 1.1401 + break; 1.1402 + 1.1403 + default: 1.1404 + break; 1.1405 + } 1.1406 + 1.1407 + } 1.1408 + 1.1409 + self->ResetDownstreamState(); 1.1410 + return NS_OK; 1.1411 +} 1.1412 + 1.1413 +nsresult 1.1414 +SpdySession3::HandleNoop(SpdySession3 *self) 1.1415 +{ 1.1416 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_NOOP); 1.1417 + 1.1418 + // Should not be receiving noop frames in spdy/3, so we'll just 1.1419 + // make a log and ignore it 1.1420 + 1.1421 + LOG3(("SpdySession3::HandleNoop %p NOP.", self)); 1.1422 + 1.1423 + self->ResetDownstreamState(); 1.1424 + return NS_OK; 1.1425 +} 1.1426 + 1.1427 +nsresult 1.1428 +SpdySession3::HandlePing(SpdySession3 *self) 1.1429 +{ 1.1430 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_PING); 1.1431 + 1.1432 + if (self->mInputFrameDataSize != 4) { 1.1433 + LOG3(("SpdySession3::HandlePing %p PING had wrong amount of data %d", 1.1434 + self, self->mInputFrameDataSize)); 1.1435 + return NS_ERROR_ILLEGAL_VALUE; 1.1436 + } 1.1437 + 1.1438 + uint32_t pingID = 1.1439 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1440 + 1.1441 + LOG3(("SpdySession3::HandlePing %p PING ID 0x%X.", self, pingID)); 1.1442 + 1.1443 + if (pingID & 0x01) { 1.1444 + // presumably a reply to our timeout ping 1.1445 + self->mPingSentEpoch = 0; 1.1446 + } 1.1447 + else { 1.1448 + // Servers initiate even numbered pings, go ahead and echo it back 1.1449 + self->GeneratePing(pingID); 1.1450 + } 1.1451 + 1.1452 + self->ResetDownstreamState(); 1.1453 + return NS_OK; 1.1454 +} 1.1455 + 1.1456 +nsresult 1.1457 +SpdySession3::HandleGoAway(SpdySession3 *self) 1.1458 +{ 1.1459 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_GOAWAY); 1.1460 + 1.1461 + if (self->mInputFrameDataSize != 8) { 1.1462 + LOG3(("SpdySession3::HandleGoAway %p GOAWAY had wrong amount of data %d", 1.1463 + self, self->mInputFrameDataSize)); 1.1464 + return NS_ERROR_ILLEGAL_VALUE; 1.1465 + } 1.1466 + 1.1467 + self->mShouldGoAway = true; 1.1468 + self->mGoAwayID = 1.1469 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1470 + self->mCleanShutdown = true; 1.1471 + 1.1472 + // Find streams greater than the last-good ID and mark them for deletion 1.1473 + // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The 1.1474 + // underlying transaction can be restarted. 1.1475 + self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self); 1.1476 + 1.1477 + // Process the streams marked for deletion and restart. 1.1478 + uint32_t size = self->mGoAwayStreamsToRestart.GetSize(); 1.1479 + for (uint32_t count = 0; count < size; ++count) { 1.1480 + SpdyStream3 *stream = 1.1481 + static_cast<SpdyStream3 *>(self->mGoAwayStreamsToRestart.PopFront()); 1.1482 + 1.1483 + self->CloseStream(stream, NS_ERROR_NET_RESET); 1.1484 + if (stream->HasRegisteredID()) 1.1485 + self->mStreamIDHash.Remove(stream->StreamID()); 1.1486 + self->mStreamTransactionHash.Remove(stream->Transaction()); 1.1487 + } 1.1488 + 1.1489 + // Queued streams can also be deleted from this session and restarted 1.1490 + // in another one. (they were never sent on the network so they implicitly 1.1491 + // are not covered by the last-good id. 1.1492 + size = self->mQueuedStreams.GetSize(); 1.1493 + for (uint32_t count = 0; count < size; ++count) { 1.1494 + SpdyStream3 *stream = 1.1495 + static_cast<SpdyStream3 *>(self->mQueuedStreams.PopFront()); 1.1496 + self->CloseStream(stream, NS_ERROR_NET_RESET); 1.1497 + self->mStreamTransactionHash.Remove(stream->Transaction()); 1.1498 + } 1.1499 + 1.1500 + LOG3(("SpdySession3::HandleGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X " 1.1501 + "live streams=%d\n", self, self->mGoAwayID, 1.1502 + NetworkEndian::readUint32(self->mInputFrameBuffer + 1.1503 + 3 * sizeof(uint32_t)), 1.1504 + self->mStreamTransactionHash.Count())); 1.1505 + 1.1506 + self->ResetDownstreamState(); 1.1507 + return NS_OK; 1.1508 +} 1.1509 + 1.1510 +nsresult 1.1511 +SpdySession3::HandleHeaders(SpdySession3 *self) 1.1512 +{ 1.1513 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_HEADERS); 1.1514 + 1.1515 + if (self->mInputFrameDataSize < 4) { 1.1516 + LOG3(("SpdySession3::HandleHeaders %p HEADERS had wrong amount of data %d", 1.1517 + self, self->mInputFrameDataSize)); 1.1518 + return NS_ERROR_ILLEGAL_VALUE; 1.1519 + } 1.1520 + 1.1521 + uint32_t streamID = 1.1522 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1523 + LOG3(("SpdySession3::HandleHeaders %p HEADERS for Stream 0x%X.\n", 1.1524 + self, streamID)); 1.1525 + nsresult rv = self->SetInputFrameDataStream(streamID); 1.1526 + if (NS_FAILED(rv)) 1.1527 + return rv; 1.1528 + 1.1529 + if (!self->mInputFrameDataStream) { 1.1530 + LOG3(("SpdySession3::HandleHeaders %p lookup streamID 0x%X failed.\n", 1.1531 + self, streamID)); 1.1532 + if (streamID >= self->mNextStreamID) 1.1533 + self->GenerateRstStream(RST_INVALID_STREAM, streamID); 1.1534 + 1.1535 + rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4); 1.1536 + if (NS_FAILED(rv)) { 1.1537 + LOG(("SpdySession3::HandleHeaders uncompress failed\n")); 1.1538 + // this is fatal to the session 1.1539 + return rv; 1.1540 + } 1.1541 + self->ResetDownstreamState(); 1.1542 + return NS_OK; 1.1543 + } 1.1544 + 1.1545 + // Uncompress the headers into local buffers in the SpdyStream, leaving 1.1546 + // them in spdy format for the time being. Make certain to do this 1.1547 + // step before any error handling that might abort the stream but not 1.1548 + // the session becuase the session compression context will become 1.1549 + // inconsistent if all of the compressed data is not processed. 1.1550 + rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib, 1.1551 + self->mInputFrameBuffer + 12, 1.1552 + self->mInputFrameDataSize - 4); 1.1553 + if (NS_FAILED(rv)) { 1.1554 + LOG(("SpdySession3::HandleHeaders uncompress failed\n")); 1.1555 + return rv; 1.1556 + } 1.1557 + 1.1558 + self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN; 1.1559 + self->mInputFrameDataStream-> 1.1560 + UpdateTransportReadEvents(self->mInputFrameDataSize); 1.1561 + self->mLastDataReadEpoch = self->mLastReadEpoch; 1.1562 + 1.1563 + if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) { 1.1564 + LOG3(("Headers %p had undefined flag set 0x%X\n", self, streamID)); 1.1565 + self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE, 1.1566 + RST_PROTOCOL_ERROR); 1.1567 + self->ResetDownstreamState(); 1.1568 + return NS_OK; 1.1569 + } 1.1570 + 1.1571 + if (!self->mInputFrameDataLast) { 1.1572 + // don't process the headers yet as there could be more HEADERS frames 1.1573 + self->ResetDownstreamState(); 1.1574 + return NS_OK; 1.1575 + } 1.1576 + 1.1577 + rv = self->ResponseHeadersComplete(); 1.1578 + if (rv == NS_ERROR_ILLEGAL_VALUE) { 1.1579 + LOG3(("SpdySession3::HanndleHeaders %p PROTOCOL_ERROR detected 0x%X\n", 1.1580 + self, streamID)); 1.1581 + self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR); 1.1582 + self->ResetDownstreamState(); 1.1583 + rv = NS_OK; 1.1584 + } 1.1585 + return rv; 1.1586 +} 1.1587 + 1.1588 +nsresult 1.1589 +SpdySession3::HandleWindowUpdate(SpdySession3 *self) 1.1590 +{ 1.1591 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE); 1.1592 + 1.1593 + if (self->mInputFrameDataSize < 8) { 1.1594 + LOG3(("SpdySession3::HandleWindowUpdate %p Window Update wrong length %d\n", 1.1595 + self, self->mInputFrameDataSize)); 1.1596 + return NS_ERROR_ILLEGAL_VALUE; 1.1597 + } 1.1598 + 1.1599 + uint32_t delta = 1.1600 + NetworkEndian::readUint32(self->mInputFrameBuffer + 3 * sizeof(uint32_t)); 1.1601 + delta &= 0x7fffffff; 1.1602 + uint32_t streamID = 1.1603 + NetworkEndian::readUint32(self->mInputFrameBuffer + 2 * sizeof(uint32_t)); 1.1604 + streamID &= 0x7fffffff; 1.1605 + 1.1606 + LOG3(("SpdySession3::HandleWindowUpdate %p len=%d for Stream 0x%X.\n", 1.1607 + self, delta, streamID)); 1.1608 + nsresult rv = self->SetInputFrameDataStream(streamID); 1.1609 + if (NS_FAILED(rv)) 1.1610 + return rv; 1.1611 + 1.1612 + if (!self->mInputFrameDataStream) { 1.1613 + LOG3(("SpdySession3::HandleWindowUpdate %p lookup streamID 0x%X failed.\n", 1.1614 + self, streamID)); 1.1615 + if (streamID >= self->mNextStreamID) 1.1616 + self->GenerateRstStream(RST_INVALID_STREAM, streamID); 1.1617 + self->ResetDownstreamState(); 1.1618 + return NS_OK; 1.1619 + } 1.1620 + 1.1621 + self->mInputFrameDataStream->UpdateRemoteWindow(delta); 1.1622 + self->ResetDownstreamState(); 1.1623 + return NS_OK; 1.1624 +} 1.1625 + 1.1626 +nsresult 1.1627 +SpdySession3::HandleCredential(SpdySession3 *self) 1.1628 +{ 1.1629 + MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_CREDENTIAL); 1.1630 + 1.1631 + // These aren't used yet. Just ignore the frame. 1.1632 + 1.1633 + LOG3(("SpdySession3::HandleCredential %p NOP.", self)); 1.1634 + 1.1635 + self->ResetDownstreamState(); 1.1636 + return NS_OK; 1.1637 +} 1.1638 + 1.1639 +//----------------------------------------------------------------------------- 1.1640 +// nsAHttpTransaction. It is expected that nsHttpConnection is the caller 1.1641 +// of these methods 1.1642 +//----------------------------------------------------------------------------- 1.1643 + 1.1644 +void 1.1645 +SpdySession3::OnTransportStatus(nsITransport* aTransport, 1.1646 + nsresult aStatus, 1.1647 + uint64_t aProgress) 1.1648 +{ 1.1649 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1650 + 1.1651 + switch (aStatus) { 1.1652 + // These should appear only once, deliver to the first 1.1653 + // transaction on the session. 1.1654 + case NS_NET_STATUS_RESOLVING_HOST: 1.1655 + case NS_NET_STATUS_RESOLVED_HOST: 1.1656 + case NS_NET_STATUS_CONNECTING_TO: 1.1657 + case NS_NET_STATUS_CONNECTED_TO: 1.1658 + { 1.1659 + SpdyStream3 *target = mStreamIDHash.Get(1); 1.1660 + nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr; 1.1661 + if (transaction) 1.1662 + transaction->OnTransportStatus(aTransport, aStatus, aProgress); 1.1663 + break; 1.1664 + } 1.1665 + 1.1666 + default: 1.1667 + // The other transport events are ignored here because there is no good 1.1668 + // way to map them to the right transaction in spdy. Instead, the events 1.1669 + // are generated again from the spdy code and passed directly to the 1.1670 + // correct transaction. 1.1671 + 1.1672 + // NS_NET_STATUS_SENDING_TO: 1.1673 + // This is generated by the socket transport when (part) of 1.1674 + // a transaction is written out 1.1675 + // 1.1676 + // There is no good way to map it to the right transaction in spdy, 1.1677 + // so it is ignored here and generated separately when the SYN_STREAM 1.1678 + // is sent from SpdyStream3::TransmitFrame 1.1679 + 1.1680 + // NS_NET_STATUS_WAITING_FOR: 1.1681 + // Created by nsHttpConnection when the request has been totally sent. 1.1682 + // There is no good way to map it to the right transaction in spdy, 1.1683 + // so it is ignored here and generated separately when the same 1.1684 + // condition is complete in SpdyStream3 when there is no more 1.1685 + // request body left to be transmitted. 1.1686 + 1.1687 + // NS_NET_STATUS_RECEIVING_FROM 1.1688 + // Generated in spdysession whenever we read a data frame or a syn_reply 1.1689 + // that can be attributed to a particular stream/transaction 1.1690 + 1.1691 + break; 1.1692 + } 1.1693 +} 1.1694 + 1.1695 +// ReadSegments() is used to write data to the network. Generally, HTTP 1.1696 +// request data is pulled from the approriate transaction and 1.1697 +// converted to SPDY data. Sometimes control data like window-update are 1.1698 +// generated instead. 1.1699 + 1.1700 +nsresult 1.1701 +SpdySession3::ReadSegments(nsAHttpSegmentReader *reader, 1.1702 + uint32_t count, 1.1703 + uint32_t *countRead) 1.1704 +{ 1.1705 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1706 + 1.1707 + MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader), 1.1708 + "Inconsistent Write Function Callback"); 1.1709 + 1.1710 + if (reader) 1.1711 + mSegmentReader = reader; 1.1712 + 1.1713 + nsresult rv; 1.1714 + *countRead = 0; 1.1715 + 1.1716 + LOG3(("SpdySession3::ReadSegments %p", this)); 1.1717 + 1.1718 + SpdyStream3 *stream = static_cast<SpdyStream3 *>(mReadyForWrite.PopFront()); 1.1719 + if (!stream) { 1.1720 + LOG3(("SpdySession3 %p could not identify a stream to write; suspending.", 1.1721 + this)); 1.1722 + FlushOutputQueue(); 1.1723 + SetWriteCallbacks(); 1.1724 + return NS_BASE_STREAM_WOULD_BLOCK; 1.1725 + } 1.1726 + 1.1727 + LOG3(("SpdySession3 %p will write from SpdyStream3 %p 0x%X " 1.1728 + "block-input=%d block-output=%d\n", this, stream, stream->StreamID(), 1.1729 + stream->RequestBlockedOnRead(), stream->BlockedOnRwin())); 1.1730 + 1.1731 + rv = stream->ReadSegments(this, count, countRead); 1.1732 + 1.1733 + // Not every permutation of stream->ReadSegents produces data (and therefore 1.1734 + // tries to flush the output queue) - SENDING_FIN_STREAM can be an example 1.1735 + // of that. But we might still have old data buffered that would be good 1.1736 + // to flush. 1.1737 + FlushOutputQueue(); 1.1738 + 1.1739 + // Allow new server reads - that might be data or control information 1.1740 + // (e.g. window updates or http replies) that are responses to these writes 1.1741 + ResumeRecv(); 1.1742 + 1.1743 + if (stream->RequestBlockedOnRead()) { 1.1744 + 1.1745 + // We are blocked waiting for input - either more http headers or 1.1746 + // any request body data. When more data from the request stream 1.1747 + // becomes available the httptransaction will call conn->ResumeSend(). 1.1748 + 1.1749 + LOG3(("SpdySession3::ReadSegments %p dealing with block on read", this)); 1.1750 + 1.1751 + // call readsegments again if there are other streams ready 1.1752 + // to run in this session 1.1753 + if (GetWriteQueueSize()) 1.1754 + rv = NS_OK; 1.1755 + else 1.1756 + rv = NS_BASE_STREAM_WOULD_BLOCK; 1.1757 + SetWriteCallbacks(); 1.1758 + return rv; 1.1759 + } 1.1760 + 1.1761 + if (NS_FAILED(rv)) { 1.1762 + LOG3(("SpdySession3::ReadSegments %p returning FAIL code %X", 1.1763 + this, rv)); 1.1764 + if (rv != NS_BASE_STREAM_WOULD_BLOCK) 1.1765 + CleanupStream(stream, rv, RST_CANCEL); 1.1766 + return rv; 1.1767 + } 1.1768 + 1.1769 + if (*countRead > 0) { 1.1770 + LOG3(("SpdySession3::ReadSegments %p stream=%p countread=%d", 1.1771 + this, stream, *countRead)); 1.1772 + mReadyForWrite.Push(stream); 1.1773 + SetWriteCallbacks(); 1.1774 + return rv; 1.1775 + } 1.1776 + 1.1777 + if (stream->BlockedOnRwin()) { 1.1778 + LOG3(("SpdySession3 %p will stream %p 0x%X suspended for flow control\n", 1.1779 + this, stream, stream->StreamID())); 1.1780 + return NS_BASE_STREAM_WOULD_BLOCK; 1.1781 + } 1.1782 + 1.1783 + LOG3(("SpdySession3::ReadSegments %p stream=%p stream send complete", 1.1784 + this, stream)); 1.1785 + 1.1786 + // call readsegments again if there are other streams ready 1.1787 + // to go in this session 1.1788 + SetWriteCallbacks(); 1.1789 + 1.1790 + return rv; 1.1791 +} 1.1792 + 1.1793 +// WriteSegments() is used to read data off the socket. Generally this is 1.1794 +// just the SPDY frame header and from there the appropriate SPDYStream 1.1795 +// is identified from the Stream-ID. The http transaction associated with 1.1796 +// that read then pulls in the data directly, which it will feed to 1.1797 +// OnWriteSegment(). That function will gateway it into http and feed 1.1798 +// it to the appropriate transaction. 1.1799 + 1.1800 +// we call writer->OnWriteSegment via NetworkRead() to get a spdy header.. 1.1801 +// and decide if it is data or control.. if it is control, just deal with it. 1.1802 +// if it is data, identify the spdy stream 1.1803 +// call stream->WriteSegments which can call this::OnWriteSegment to get the 1.1804 +// data. It always gets full frames if they are part of the stream 1.1805 + 1.1806 +nsresult 1.1807 +SpdySession3::WriteSegments(nsAHttpSegmentWriter *writer, 1.1808 + uint32_t count, 1.1809 + uint32_t *countWritten) 1.1810 +{ 1.1811 + typedef nsresult (*Control_FX) (SpdySession3 *self); 1.1812 + static const Control_FX sControlFunctions[] = 1.1813 + { 1.1814 + nullptr, 1.1815 + SpdySession3::HandleSynStream, 1.1816 + SpdySession3::HandleSynReply, 1.1817 + SpdySession3::HandleRstStream, 1.1818 + SpdySession3::HandleSettings, 1.1819 + SpdySession3::HandleNoop, 1.1820 + SpdySession3::HandlePing, 1.1821 + SpdySession3::HandleGoAway, 1.1822 + SpdySession3::HandleHeaders, 1.1823 + SpdySession3::HandleWindowUpdate, 1.1824 + SpdySession3::HandleCredential 1.1825 + }; 1.1826 + 1.1827 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.1828 + 1.1829 + nsresult rv; 1.1830 + *countWritten = 0; 1.1831 + 1.1832 + if (mClosed) 1.1833 + return NS_ERROR_FAILURE; 1.1834 + 1.1835 + SetWriteCallbacks(); 1.1836 + 1.1837 + // If there are http transactions attached to a push stream with filled buffers 1.1838 + // trigger that data pump here. This only reads from buffers (not the network) 1.1839 + // so mDownstreamState doesn't matter. 1.1840 + SpdyStream3 *pushConnectedStream = 1.1841 + static_cast<SpdyStream3 *>(mReadyForRead.PopFront()); 1.1842 + if (pushConnectedStream) { 1.1843 + LOG3(("SpdySession3::WriteSegments %p processing pushed stream 0x%X\n", 1.1844 + this, pushConnectedStream->StreamID())); 1.1845 + mSegmentWriter = writer; 1.1846 + rv = pushConnectedStream->WriteSegments(this, count, countWritten); 1.1847 + mSegmentWriter = nullptr; 1.1848 + 1.1849 + // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK 1.1850 + // so we need this check to determine the truth. 1.1851 + if (NS_SUCCEEDED(rv) && !*countWritten && 1.1852 + pushConnectedStream->PushSource() && 1.1853 + pushConnectedStream->PushSource()->GetPushComplete()) { 1.1854 + rv = NS_BASE_STREAM_CLOSED; 1.1855 + } 1.1856 + 1.1857 + if (rv == NS_BASE_STREAM_CLOSED) { 1.1858 + CleanupStream(pushConnectedStream, NS_OK, RST_CANCEL); 1.1859 + rv = NS_OK; 1.1860 + } 1.1861 + 1.1862 + // if we return OK to nsHttpConnection it will use mSocketInCondition 1.1863 + // to determine whether to schedule more reads, incorrectly 1.1864 + // assuming that nsHttpConnection::OnSocketWrite() was called. 1.1865 + if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) { 1.1866 + rv = NS_BASE_STREAM_WOULD_BLOCK; 1.1867 + ResumeRecv(); 1.1868 + } 1.1869 + 1.1870 + return rv; 1.1871 + } 1.1872 + 1.1873 + // We buffer all control frames and act on them in this layer. 1.1874 + // We buffer the first 8 bytes of data frames (the header) but 1.1875 + // the actual data is passed through unprocessed. 1.1876 + 1.1877 + if (mDownstreamState == BUFFERING_FRAME_HEADER) { 1.1878 + // The first 8 bytes of every frame is header information that 1.1879 + // we are going to want to strip before passing to http. That is 1.1880 + // true of both control and data packets. 1.1881 + 1.1882 + MOZ_ASSERT(mInputFrameBufferUsed < 8, 1.1883 + "Frame Buffer Used Too Large for State"); 1.1884 + 1.1885 + rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed, 1.1886 + 8 - mInputFrameBufferUsed, countWritten); 1.1887 + 1.1888 + if (NS_FAILED(rv)) { 1.1889 + LOG3(("SpdySession3 %p buffering frame header read failure %x\n", 1.1890 + this, rv)); 1.1891 + // maybe just blocked reading from network 1.1892 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.1893 + rv = NS_OK; 1.1894 + return rv; 1.1895 + } 1.1896 + 1.1897 + LogIO(this, nullptr, "Reading Frame Header", 1.1898 + mInputFrameBuffer + mInputFrameBufferUsed, *countWritten); 1.1899 + 1.1900 + mInputFrameBufferUsed += *countWritten; 1.1901 + 1.1902 + if (mInputFrameBufferUsed < 8) 1.1903 + { 1.1904 + LOG3(("SpdySession3::WriteSegments %p " 1.1905 + "BUFFERING FRAME HEADER incomplete size=%d", 1.1906 + this, mInputFrameBufferUsed)); 1.1907 + return rv; 1.1908 + } 1.1909 + 1.1910 + // For both control and data frames the second 32 bit word of the header 1.1911 + // is 8-flags, 24-length. (network byte order) 1.1912 + mInputFrameDataSize = 1.1913 + NetworkEndian::readUint32(mInputFrameBuffer + 1 * sizeof(uint32_t)); 1.1914 + mInputFrameDataSize &= 0x00ffffff; 1.1915 + mInputFrameDataRead = 0; 1.1916 + 1.1917 + if (mInputFrameBuffer[0] & kFlag_Control) { 1.1918 + EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8, 1.1919 + mInputFrameBufferSize); 1.1920 + ChangeDownstreamState(BUFFERING_CONTROL_FRAME); 1.1921 + 1.1922 + // The first 32 bit word of the header is 1.1923 + // 1 ctrl - 15 version - 16 type 1.1924 + uint16_t version = NetworkEndian::readUint16(mInputFrameBuffer); 1.1925 + version &= 0x7fff; 1.1926 + 1.1927 + mFrameControlType = 1.1928 + NetworkEndian::readUint16(mInputFrameBuffer + sizeof(uint16_t)); 1.1929 + 1.1930 + LOG3(("SpdySession3::WriteSegments %p - Control Frame Identified " 1.1931 + "type %d version %d data len %d", 1.1932 + this, mFrameControlType, version, mInputFrameDataSize)); 1.1933 + 1.1934 + if (mFrameControlType >= CONTROL_TYPE_LAST || 1.1935 + mFrameControlType <= CONTROL_TYPE_FIRST) 1.1936 + return NS_ERROR_ILLEGAL_VALUE; 1.1937 + 1.1938 + if (version != kVersion) 1.1939 + return NS_ERROR_ILLEGAL_VALUE; 1.1940 + } 1.1941 + else { 1.1942 + ChangeDownstreamState(PROCESSING_DATA_FRAME); 1.1943 + 1.1944 + Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD, 1.1945 + mInputFrameDataSize >> 10); 1.1946 + mLastDataReadEpoch = mLastReadEpoch; 1.1947 + 1.1948 + uint32_t streamID = NetworkEndian::readUint32(mInputFrameBuffer); 1.1949 + rv = SetInputFrameDataStream(streamID); 1.1950 + if (NS_FAILED(rv)) { 1.1951 + LOG(("SpdySession3::WriteSegments %p lookup streamID 0x%X failed. " 1.1952 + "probably due to verification.\n", this, streamID)); 1.1953 + return rv; 1.1954 + } 1.1955 + if (!mInputFrameDataStream) { 1.1956 + LOG3(("SpdySession3::WriteSegments %p lookup streamID 0x%X failed. " 1.1957 + "Next = 0x%X", this, streamID, mNextStreamID)); 1.1958 + if (streamID >= mNextStreamID) 1.1959 + GenerateRstStream(RST_INVALID_STREAM, streamID); 1.1960 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.1961 + } 1.1962 + else if (mInputFrameDataStream->RecvdFin()) { 1.1963 + LOG3(("SpdySession3::WriteSegments %p streamID 0x%X " 1.1964 + "Data arrived for already server closed stream.\n", 1.1965 + this, streamID)); 1.1966 + GenerateRstStream(RST_STREAM_ALREADY_CLOSED, streamID); 1.1967 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.1968 + } 1.1969 + else if (!mInputFrameDataStream->RecvdData()) { 1.1970 + LOG3(("SpdySession3 %p First Data Frame Flushes Headers stream 0x%X\n", 1.1971 + this, streamID)); 1.1972 + 1.1973 + mInputFrameDataStream->SetRecvdData(true); 1.1974 + rv = ResponseHeadersComplete(); 1.1975 + if (rv == NS_ERROR_ILLEGAL_VALUE) { 1.1976 + LOG3(("SpdySession3 %p PROTOCOL_ERROR detected 0x%X\n", 1.1977 + this, streamID)); 1.1978 + CleanupStream(mInputFrameDataStream, rv, RST_PROTOCOL_ERROR); 1.1979 + ChangeDownstreamState(DISCARDING_DATA_FRAME); 1.1980 + } 1.1981 + else { 1.1982 + mDataPending = true; 1.1983 + } 1.1984 + } 1.1985 + 1.1986 + mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN); 1.1987 + LOG3(("Start Processing Data Frame. " 1.1988 + "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d", 1.1989 + this, streamID, mInputFrameDataStream, mInputFrameDataLast, 1.1990 + mInputFrameDataSize)); 1.1991 + UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize); 1.1992 + } 1.1993 + } 1.1994 + 1.1995 + if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) { 1.1996 + if (mDownstreamRstReason == RST_REFUSED_STREAM) 1.1997 + rv = NS_ERROR_NET_RESET; //we can retry this 100% safely 1.1998 + else if (mDownstreamRstReason == RST_CANCEL || 1.1999 + mDownstreamRstReason == RST_PROTOCOL_ERROR || 1.2000 + mDownstreamRstReason == RST_INTERNAL_ERROR || 1.2001 + mDownstreamRstReason == RST_UNSUPPORTED_VERSION) 1.2002 + rv = NS_ERROR_NET_INTERRUPT; 1.2003 + else if (mDownstreamRstReason == RST_FRAME_TOO_LARGE) 1.2004 + rv = NS_ERROR_FILE_TOO_BIG; 1.2005 + else 1.2006 + rv = NS_ERROR_ILLEGAL_VALUE; 1.2007 + 1.2008 + if (mDownstreamRstReason != RST_REFUSED_STREAM && 1.2009 + mDownstreamRstReason != RST_CANCEL) 1.2010 + mShouldGoAway = true; 1.2011 + 1.2012 + // mInputFrameDataStream is reset by ChangeDownstreamState 1.2013 + SpdyStream3 *stream = mInputFrameDataStream; 1.2014 + ResetDownstreamState(); 1.2015 + LOG3(("SpdySession3::WriteSegments cleanup stream on recv of rst " 1.2016 + "session=%p stream=%p 0x%X\n", this, stream, 1.2017 + stream ? stream->StreamID() : 0)); 1.2018 + CleanupStream(stream, rv, RST_CANCEL); 1.2019 + return NS_OK; 1.2020 + } 1.2021 + 1.2022 + if (mDownstreamState == PROCESSING_DATA_FRAME || 1.2023 + mDownstreamState == PROCESSING_COMPLETE_HEADERS) { 1.2024 + 1.2025 + // The cleanup stream should only be set while stream->WriteSegments is 1.2026 + // on the stack and then cleaned up in this code block afterwards. 1.2027 + MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly"); 1.2028 + mNeedsCleanup = nullptr; /* just in case */ 1.2029 + 1.2030 + mSegmentWriter = writer; 1.2031 + rv = mInputFrameDataStream->WriteSegments(this, count, countWritten); 1.2032 + mSegmentWriter = nullptr; 1.2033 + 1.2034 + mLastDataReadEpoch = mLastReadEpoch; 1.2035 + 1.2036 + if (SoftStreamError(rv)) { 1.2037 + // This will happen when the transaction figures out it is EOF, generally 1.2038 + // due to a content-length match being made. Return OK from this function 1.2039 + // otherwise the whole session would be torn down. 1.2040 + SpdyStream3 *stream = mInputFrameDataStream; 1.2041 + 1.2042 + // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state 1.2043 + // back to PROCESSING_DATA_FRAME where we came from 1.2044 + mDownstreamState = PROCESSING_DATA_FRAME; 1.2045 + 1.2046 + if (mInputFrameDataRead == mInputFrameDataSize) 1.2047 + ResetDownstreamState(); 1.2048 + LOG3(("SpdySession3::WriteSegments session=%p stream=%p 0x%X " 1.2049 + "needscleanup=%p. cleanup stream based on " 1.2050 + "stream->writeSegments returning code %X\n", 1.2051 + this, stream, stream ? stream->StreamID() : 0, 1.2052 + mNeedsCleanup, rv)); 1.2053 + CleanupStream(stream, NS_OK, RST_CANCEL); 1.2054 + MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame"); 1.2055 + mNeedsCleanup = nullptr; /* just in case */ 1.2056 + return NS_OK; 1.2057 + } 1.2058 + 1.2059 + if (mNeedsCleanup) { 1.2060 + LOG3(("SpdySession3::WriteSegments session=%p stream=%p 0x%X " 1.2061 + "cleanup stream based on mNeedsCleanup.\n", 1.2062 + this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0)); 1.2063 + CleanupStream(mNeedsCleanup, NS_OK, RST_CANCEL); 1.2064 + mNeedsCleanup = nullptr; 1.2065 + } 1.2066 + 1.2067 + if (NS_FAILED(rv)) { 1.2068 + LOG3(("SpdySession3 %p data frame read failure %x\n", this, rv)); 1.2069 + // maybe just blocked reading from network 1.2070 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2071 + rv = NS_OK; 1.2072 + } 1.2073 + 1.2074 + return rv; 1.2075 + } 1.2076 + 1.2077 + if (mDownstreamState == DISCARDING_DATA_FRAME) { 1.2078 + char trash[4096]; 1.2079 + uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead); 1.2080 + 1.2081 + if (!count) { 1.2082 + ResetDownstreamState(); 1.2083 + ResumeRecv(); 1.2084 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2085 + } 1.2086 + 1.2087 + rv = NetworkRead(writer, trash, count, countWritten); 1.2088 + 1.2089 + if (NS_FAILED(rv)) { 1.2090 + LOG3(("SpdySession3 %p discard frame read failure %x\n", this, rv)); 1.2091 + // maybe just blocked reading from network 1.2092 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2093 + rv = NS_OK; 1.2094 + return rv; 1.2095 + } 1.2096 + 1.2097 + LogIO(this, nullptr, "Discarding Frame", trash, *countWritten); 1.2098 + 1.2099 + mInputFrameDataRead += *countWritten; 1.2100 + 1.2101 + if (mInputFrameDataRead == mInputFrameDataSize) 1.2102 + ResetDownstreamState(); 1.2103 + return rv; 1.2104 + } 1.2105 + 1.2106 + MOZ_ASSERT(mDownstreamState == BUFFERING_CONTROL_FRAME); 1.2107 + if (mDownstreamState != BUFFERING_CONTROL_FRAME) { 1.2108 + // this cannot happen 1.2109 + return NS_ERROR_UNEXPECTED; 1.2110 + } 1.2111 + 1.2112 + MOZ_ASSERT(mInputFrameBufferUsed == 8, 1.2113 + "Frame Buffer Header Not Present"); 1.2114 + 1.2115 + rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead, 1.2116 + mInputFrameDataSize - mInputFrameDataRead, countWritten); 1.2117 + 1.2118 + if (NS_FAILED(rv)) { 1.2119 + LOG3(("SpdySession3 %p buffering control frame read failure %x\n", 1.2120 + this, rv)); 1.2121 + // maybe just blocked reading from network 1.2122 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2123 + rv = NS_OK; 1.2124 + return rv; 1.2125 + } 1.2126 + 1.2127 + LogIO(this, nullptr, "Reading Control Frame", 1.2128 + mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten); 1.2129 + 1.2130 + mInputFrameDataRead += *countWritten; 1.2131 + 1.2132 + if (mInputFrameDataRead != mInputFrameDataSize) 1.2133 + return NS_OK; 1.2134 + 1.2135 + // This check is actually redundant, the control type was previously 1.2136 + // checked to make sure it was in range, but we will check it again 1.2137 + // at time of use to make sure a regression doesn't creep in. 1.2138 + if (mFrameControlType >= CONTROL_TYPE_LAST || 1.2139 + mFrameControlType <= CONTROL_TYPE_FIRST) 1.2140 + { 1.2141 + MOZ_ASSERT(false, "control type out of range"); 1.2142 + return NS_ERROR_ILLEGAL_VALUE; 1.2143 + } 1.2144 + rv = sControlFunctions[mFrameControlType](this); 1.2145 + 1.2146 + MOZ_ASSERT(NS_FAILED(rv) || 1.2147 + mDownstreamState != BUFFERING_CONTROL_FRAME, 1.2148 + "Control Handler returned OK but did not change state"); 1.2149 + 1.2150 + if (mShouldGoAway && !mStreamTransactionHash.Count()) 1.2151 + Close(NS_OK); 1.2152 + return rv; 1.2153 +} 1.2154 + 1.2155 +void 1.2156 +SpdySession3::UpdateLocalRwin(SpdyStream3 *stream, 1.2157 + uint32_t bytes) 1.2158 +{ 1.2159 + // If this data packet was not for a valid or live stream then there 1.2160 + // is no reason to mess with the flow control 1.2161 + if (!stream || stream->RecvdFin()) 1.2162 + return; 1.2163 + 1.2164 + stream->DecrementLocalWindow(bytes); 1.2165 + 1.2166 + // Don't necessarily ack every data packet. Only do it 1.2167 + // after a significant amount of data. 1.2168 + uint64_t unacked = stream->LocalUnAcked(); 1.2169 + int64_t localWindow = stream->LocalWindow(); 1.2170 + 1.2171 + LOG3(("SpdySession3::UpdateLocalRwin this=%p id=0x%X newbytes=%u " 1.2172 + "unacked=%llu localWindow=%lld\n", 1.2173 + this, stream->StreamID(), bytes, unacked, localWindow)); 1.2174 + 1.2175 + if (!unacked) 1.2176 + return; 1.2177 + 1.2178 + if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold)) 1.2179 + return; 1.2180 + 1.2181 + if (!stream->HasSink()) { 1.2182 + LOG3(("SpdySession3::UpdateLocalRwin %p 0x%X Pushed Stream Has No Sink\n", 1.2183 + this, stream->StreamID())); 1.2184 + return; 1.2185 + } 1.2186 + 1.2187 + // Generate window updates directly out of spdysession instead of the stream 1.2188 + // in order to avoid queue delays in getting the 'ACK' out. 1.2189 + uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU; 1.2190 + 1.2191 + LOG3(("SpdySession3::UpdateLocalRwin Ack this=%p id=0x%X acksize=%d\n", 1.2192 + this, stream->StreamID(), toack)); 1.2193 + stream->IncrementLocalWindow(toack); 1.2194 + 1.2195 + static const uint32_t dataLen = 8; 1.2196 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + dataLen, 1.2197 + mOutputQueueUsed, mOutputQueueSize); 1.2198 + char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed; 1.2199 + mOutputQueueUsed += 8 + dataLen; 1.2200 + 1.2201 + memset(packet, 0, 8 + dataLen); 1.2202 + packet[0] = kFlag_Control; 1.2203 + packet[1] = kVersion; 1.2204 + packet[3] = CONTROL_TYPE_WINDOW_UPDATE; 1.2205 + packet[7] = dataLen; 1.2206 + 1.2207 + NetworkEndian::writeUint32(packet + 8, stream->StreamID()); 1.2208 + NetworkEndian::writeUint32(packet + 12, toack); 1.2209 + 1.2210 + LogIO(this, stream, "Window Update", packet, 8 + dataLen); 1.2211 + FlushOutputQueue(); 1.2212 +} 1.2213 + 1.2214 +void 1.2215 +SpdySession3::Close(nsresult aReason) 1.2216 +{ 1.2217 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2218 + 1.2219 + if (mClosed) 1.2220 + return; 1.2221 + 1.2222 + LOG3(("SpdySession3::Close %p %X", this, aReason)); 1.2223 + 1.2224 + mClosed = true; 1.2225 + 1.2226 + mStreamTransactionHash.Enumerate(ShutdownEnumerator, this); 1.2227 + mStreamIDHash.Clear(); 1.2228 + mStreamTransactionHash.Clear(); 1.2229 + 1.2230 + uint32_t goAwayReason; 1.2231 + if (NS_SUCCEEDED(aReason)) { 1.2232 + goAwayReason = OK; 1.2233 + } else if (aReason == NS_ERROR_ILLEGAL_VALUE) { 1.2234 + goAwayReason = PROTOCOL_ERROR; 1.2235 + } else { 1.2236 + goAwayReason = INTERNAL_ERROR; 1.2237 + } 1.2238 + GenerateGoAway(goAwayReason); 1.2239 + mConnection = nullptr; 1.2240 + mSegmentReader = nullptr; 1.2241 + mSegmentWriter = nullptr; 1.2242 +} 1.2243 + 1.2244 +void 1.2245 +SpdySession3::CloseTransaction(nsAHttpTransaction *aTransaction, 1.2246 + nsresult aResult) 1.2247 +{ 1.2248 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2249 + LOG3(("SpdySession3::CloseTransaction %p %p %x", this, aTransaction, aResult)); 1.2250 + 1.2251 + // Generally this arrives as a cancel event from the connection manager. 1.2252 + 1.2253 + // need to find the stream and call CleanupStream() on it. 1.2254 + SpdyStream3 *stream = mStreamTransactionHash.Get(aTransaction); 1.2255 + if (!stream) { 1.2256 + LOG3(("SpdySession3::CloseTransaction %p %p %x - not found.", 1.2257 + this, aTransaction, aResult)); 1.2258 + return; 1.2259 + } 1.2260 + LOG3(("SpdySession3::CloseTranscation probably a cancel. " 1.2261 + "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p", 1.2262 + this, aTransaction, aResult, stream->StreamID(), stream)); 1.2263 + CleanupStream(stream, aResult, RST_CANCEL); 1.2264 + ResumeRecv(); 1.2265 +} 1.2266 + 1.2267 + 1.2268 +//----------------------------------------------------------------------------- 1.2269 +// nsAHttpSegmentReader 1.2270 +//----------------------------------------------------------------------------- 1.2271 + 1.2272 +nsresult 1.2273 +SpdySession3::OnReadSegment(const char *buf, 1.2274 + uint32_t count, 1.2275 + uint32_t *countRead) 1.2276 +{ 1.2277 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2278 + 1.2279 + nsresult rv; 1.2280 + 1.2281 + // If we can release old queued data then we can try and write the new 1.2282 + // data directly to the network without using the output queue at all 1.2283 + if (mOutputQueueUsed) 1.2284 + FlushOutputQueue(); 1.2285 + 1.2286 + if (!mOutputQueueUsed && mSegmentReader) { 1.2287 + // try and write directly without output queue 1.2288 + rv = mSegmentReader->OnReadSegment(buf, count, countRead); 1.2289 + 1.2290 + if (rv == NS_BASE_STREAM_WOULD_BLOCK) 1.2291 + *countRead = 0; 1.2292 + else if (NS_FAILED(rv)) 1.2293 + return rv; 1.2294 + 1.2295 + if (*countRead < count) { 1.2296 + uint32_t required = count - *countRead; 1.2297 + // assuming a commitment() happened, this ensurebuffer is a nop 1.2298 + // but just in case the queuesize is too small for the required data 1.2299 + // call ensurebuffer(). 1.2300 + EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize); 1.2301 + memcpy(mOutputQueueBuffer.get(), buf + *countRead, required); 1.2302 + mOutputQueueUsed = required; 1.2303 + } 1.2304 + 1.2305 + *countRead = count; 1.2306 + return NS_OK; 1.2307 + } 1.2308 + 1.2309 + // At this point we are going to buffer the new data in the output 1.2310 + // queue if it fits. By coalescing multiple small submissions into one larger 1.2311 + // buffer we can get larger writes out to the network later on. 1.2312 + 1.2313 + // This routine should not be allowed to fill up the output queue 1.2314 + // all on its own - at least kQueueReserved bytes are always left 1.2315 + // for other routines to use - but this is an all-or-nothing function, 1.2316 + // so if it will not all fit just return WOULD_BLOCK 1.2317 + 1.2318 + if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved)) 1.2319 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2320 + 1.2321 + memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count); 1.2322 + mOutputQueueUsed += count; 1.2323 + *countRead = count; 1.2324 + 1.2325 + FlushOutputQueue(); 1.2326 + 1.2327 + return NS_OK; 1.2328 +} 1.2329 + 1.2330 +nsresult 1.2331 +SpdySession3::CommitToSegmentSize(uint32_t count, bool forceCommitment) 1.2332 +{ 1.2333 + if (mOutputQueueUsed) 1.2334 + FlushOutputQueue(); 1.2335 + 1.2336 + // would there be enough room to buffer this if needed? 1.2337 + if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved)) 1.2338 + return NS_OK; 1.2339 + 1.2340 + // if we are using part of our buffers already, try again later unless 1.2341 + // forceCommitment is set. 1.2342 + if (mOutputQueueUsed && !forceCommitment) 1.2343 + return NS_BASE_STREAM_WOULD_BLOCK; 1.2344 + 1.2345 + if (mOutputQueueUsed) { 1.2346 + // normally we avoid the memmove of RealignOutputQueue, but we'll try 1.2347 + // it if forceCommitment is set before growing the buffer. 1.2348 + RealignOutputQueue(); 1.2349 + 1.2350 + // is there enough room now? 1.2351 + if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved)) 1.2352 + return NS_OK; 1.2353 + } 1.2354 + 1.2355 + // resize the buffers as needed 1.2356 + EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + count + kQueueReserved, 1.2357 + mOutputQueueUsed, mOutputQueueSize); 1.2358 + 1.2359 + MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved), 1.2360 + "buffer not as large as expected"); 1.2361 + 1.2362 + return NS_OK; 1.2363 +} 1.2364 + 1.2365 +//----------------------------------------------------------------------------- 1.2366 +// nsAHttpSegmentWriter 1.2367 +//----------------------------------------------------------------------------- 1.2368 + 1.2369 +nsresult 1.2370 +SpdySession3::OnWriteSegment(char *buf, 1.2371 + uint32_t count, 1.2372 + uint32_t *countWritten) 1.2373 +{ 1.2374 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2375 + nsresult rv; 1.2376 + 1.2377 + if (!mSegmentWriter) { 1.2378 + // the only way this could happen would be if Close() were called on the 1.2379 + // stack with WriteSegments() 1.2380 + return NS_ERROR_FAILURE; 1.2381 + } 1.2382 + 1.2383 + if (mDownstreamState == PROCESSING_DATA_FRAME) { 1.2384 + 1.2385 + if (mInputFrameDataLast && 1.2386 + mInputFrameDataRead == mInputFrameDataSize) { 1.2387 + *countWritten = 0; 1.2388 + SetNeedsCleanup(); 1.2389 + return NS_BASE_STREAM_CLOSED; 1.2390 + } 1.2391 + 1.2392 + count = std::min(count, mInputFrameDataSize - mInputFrameDataRead); 1.2393 + rv = NetworkRead(mSegmentWriter, buf, count, countWritten); 1.2394 + if (NS_FAILED(rv)) 1.2395 + return rv; 1.2396 + 1.2397 + LogIO(this, mInputFrameDataStream, "Reading Data Frame", 1.2398 + buf, *countWritten); 1.2399 + 1.2400 + mInputFrameDataRead += *countWritten; 1.2401 + 1.2402 + mInputFrameDataStream->UpdateTransportReadEvents(*countWritten); 1.2403 + if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast) 1.2404 + ResetDownstreamState(); 1.2405 + 1.2406 + return rv; 1.2407 + } 1.2408 + 1.2409 + if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) { 1.2410 + 1.2411 + if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut && 1.2412 + mInputFrameDataLast) { 1.2413 + *countWritten = 0; 1.2414 + SetNeedsCleanup(); 1.2415 + return NS_BASE_STREAM_CLOSED; 1.2416 + } 1.2417 + 1.2418 + count = std::min(count, 1.2419 + mFlatHTTPResponseHeaders.Length() - 1.2420 + mFlatHTTPResponseHeadersOut); 1.2421 + memcpy(buf, 1.2422 + mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut, 1.2423 + count); 1.2424 + mFlatHTTPResponseHeadersOut += count; 1.2425 + *countWritten = count; 1.2426 + 1.2427 + if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) { 1.2428 + if (mDataPending) { 1.2429 + // Now ready to process data frames - pop PROCESING_DATA_FRAME back onto 1.2430 + // the stack because receipt of that first data frame triggered the 1.2431 + // response header processing 1.2432 + mDataPending = false; 1.2433 + ChangeDownstreamState(PROCESSING_DATA_FRAME); 1.2434 + } 1.2435 + else if (!mInputFrameDataLast) { 1.2436 + // If more frames are expected in this stream, then reset the state so they can be 1.2437 + // handled. Otherwise (e.g. a 0 length response with the fin on the SYN_REPLY) 1.2438 + // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can 1.2439 + // cleanup the stream. 1.2440 + ResetDownstreamState(); 1.2441 + } 1.2442 + } 1.2443 + 1.2444 + return NS_OK; 1.2445 + } 1.2446 + 1.2447 + return NS_ERROR_UNEXPECTED; 1.2448 +} 1.2449 + 1.2450 +void 1.2451 +SpdySession3::SetNeedsCleanup() 1.2452 +{ 1.2453 + LOG3(("SpdySession3::SetNeedsCleanup %p - recorded downstream fin of " 1.2454 + "stream %p 0x%X", this, mInputFrameDataStream, 1.2455 + mInputFrameDataStream->StreamID())); 1.2456 + 1.2457 + // This will result in Close() being called 1.2458 + MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set"); 1.2459 + mNeedsCleanup = mInputFrameDataStream; 1.2460 + ResetDownstreamState(); 1.2461 +} 1.2462 + 1.2463 +void 1.2464 +SpdySession3::ConnectPushedStream(SpdyStream3 *stream) 1.2465 +{ 1.2466 + mReadyForRead.Push(stream); 1.2467 + ForceRecv(); 1.2468 +} 1.2469 + 1.2470 +//----------------------------------------------------------------------------- 1.2471 +// Modified methods of nsAHttpConnection 1.2472 +//----------------------------------------------------------------------------- 1.2473 + 1.2474 +void 1.2475 +SpdySession3::TransactionHasDataToWrite(nsAHttpTransaction *caller) 1.2476 +{ 1.2477 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2478 + LOG3(("SpdySession3::TransactionHasDataToWrite %p trans=%p", this, caller)); 1.2479 + 1.2480 + // a trapped signal from the http transaction to the connection that 1.2481 + // it is no longer blocked on read. 1.2482 + 1.2483 + SpdyStream3 *stream = mStreamTransactionHash.Get(caller); 1.2484 + if (!stream || !VerifyStream(stream)) { 1.2485 + LOG3(("SpdySession3::TransactionHasDataToWrite %p caller %p not found", 1.2486 + this, caller)); 1.2487 + return; 1.2488 + } 1.2489 + 1.2490 + LOG3(("SpdySession3::TransactionHasDataToWrite %p ID is 0x%X\n", 1.2491 + this, stream->StreamID())); 1.2492 + 1.2493 + mReadyForWrite.Push(stream); 1.2494 +} 1.2495 + 1.2496 +void 1.2497 +SpdySession3::TransactionHasDataToWrite(SpdyStream3 *stream) 1.2498 +{ 1.2499 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2500 + LOG3(("SpdySession3::TransactionHasDataToWrite %p stream=%p ID=%x", 1.2501 + this, stream, stream->StreamID())); 1.2502 + 1.2503 + mReadyForWrite.Push(stream); 1.2504 + SetWriteCallbacks(); 1.2505 +} 1.2506 + 1.2507 +bool 1.2508 +SpdySession3::IsPersistent() 1.2509 +{ 1.2510 + return true; 1.2511 +} 1.2512 + 1.2513 +nsresult 1.2514 +SpdySession3::TakeTransport(nsISocketTransport **, 1.2515 + nsIAsyncInputStream **, 1.2516 + nsIAsyncOutputStream **) 1.2517 +{ 1.2518 + MOZ_ASSERT(false, "TakeTransport of SpdySession3"); 1.2519 + return NS_ERROR_UNEXPECTED; 1.2520 +} 1.2521 + 1.2522 +nsHttpConnection * 1.2523 +SpdySession3::TakeHttpConnection() 1.2524 +{ 1.2525 + MOZ_ASSERT(false, "TakeHttpConnection of SpdySession3"); 1.2526 + return nullptr; 1.2527 +} 1.2528 + 1.2529 +uint32_t 1.2530 +SpdySession3::CancelPipeline(nsresult reason) 1.2531 +{ 1.2532 + // we don't pipeline inside spdy, so this isn't an issue 1.2533 + return 0; 1.2534 +} 1.2535 + 1.2536 +nsAHttpTransaction::Classifier 1.2537 +SpdySession3::Classification() 1.2538 +{ 1.2539 + if (!mConnection) 1.2540 + return nsAHttpTransaction::CLASS_GENERAL; 1.2541 + return mConnection->Classification(); 1.2542 +} 1.2543 + 1.2544 +//----------------------------------------------------------------------------- 1.2545 +// unused methods of nsAHttpTransaction 1.2546 +// We can be sure of this because SpdySession3 is only constructed in 1.2547 +// nsHttpConnection and is never passed out of that object 1.2548 +//----------------------------------------------------------------------------- 1.2549 + 1.2550 +void 1.2551 +SpdySession3::SetConnection(nsAHttpConnection *) 1.2552 +{ 1.2553 + // This is unexpected 1.2554 + MOZ_ASSERT(false, "SpdySession3::SetConnection()"); 1.2555 +} 1.2556 + 1.2557 +void 1.2558 +SpdySession3::GetSecurityCallbacks(nsIInterfaceRequestor **) 1.2559 +{ 1.2560 + // This is unexpected 1.2561 + MOZ_ASSERT(false, "SpdySession3::GetSecurityCallbacks()"); 1.2562 +} 1.2563 + 1.2564 +void 1.2565 +SpdySession3::SetProxyConnectFailed() 1.2566 +{ 1.2567 + MOZ_ASSERT(false, "SpdySession3::SetProxyConnectFailed()"); 1.2568 +} 1.2569 + 1.2570 +bool 1.2571 +SpdySession3::IsDone() 1.2572 +{ 1.2573 + return !mStreamTransactionHash.Count(); 1.2574 +} 1.2575 + 1.2576 +nsresult 1.2577 +SpdySession3::Status() 1.2578 +{ 1.2579 + MOZ_ASSERT(false, "SpdySession3::Status()"); 1.2580 + return NS_ERROR_UNEXPECTED; 1.2581 +} 1.2582 + 1.2583 +uint32_t 1.2584 +SpdySession3::Caps() 1.2585 +{ 1.2586 + MOZ_ASSERT(false, "SpdySession3::Caps()"); 1.2587 + return 0; 1.2588 +} 1.2589 + 1.2590 +void 1.2591 +SpdySession3::SetDNSWasRefreshed() 1.2592 +{ 1.2593 +} 1.2594 + 1.2595 +uint64_t 1.2596 +SpdySession3::Available() 1.2597 +{ 1.2598 + MOZ_ASSERT(false, "SpdySession3::Available()"); 1.2599 + return 0; 1.2600 +} 1.2601 + 1.2602 +nsHttpRequestHead * 1.2603 +SpdySession3::RequestHead() 1.2604 +{ 1.2605 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2606 + MOZ_ASSERT(false, 1.2607 + "SpdySession3::RequestHead() " 1.2608 + "should not be called after SPDY is setup"); 1.2609 + return nullptr; 1.2610 +} 1.2611 + 1.2612 +uint32_t 1.2613 +SpdySession3::Http1xTransactionCount() 1.2614 +{ 1.2615 + return 0; 1.2616 +} 1.2617 + 1.2618 +// used as an enumerator by TakeSubTransactions() 1.2619 +static PLDHashOperator 1.2620 +TakeStream(nsAHttpTransaction *key, 1.2621 + nsAutoPtr<SpdyStream3> &stream, 1.2622 + void *closure) 1.2623 +{ 1.2624 + nsTArray<nsRefPtr<nsAHttpTransaction> > *list = 1.2625 + static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure); 1.2626 + 1.2627 + list->AppendElement(key); 1.2628 + 1.2629 + // removing the stream from the hash will delete the stream 1.2630 + // and drop the transaction reference the hash held 1.2631 + return PL_DHASH_REMOVE; 1.2632 +} 1.2633 + 1.2634 +nsresult 1.2635 +SpdySession3::TakeSubTransactions( 1.2636 + nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions) 1.2637 +{ 1.2638 + // Generally this cannot be done with spdy as transactions are 1.2639 + // started right away. 1.2640 + 1.2641 + LOG3(("SpdySession3::TakeSubTransactions %p\n", this)); 1.2642 + 1.2643 + if (mConcurrentHighWater > 0) 1.2644 + return NS_ERROR_ALREADY_OPENED; 1.2645 + 1.2646 + LOG3((" taking %d\n", mStreamTransactionHash.Count())); 1.2647 + 1.2648 + mStreamTransactionHash.Enumerate(TakeStream, &outTransactions); 1.2649 + return NS_OK; 1.2650 +} 1.2651 + 1.2652 +nsresult 1.2653 +SpdySession3::AddTransaction(nsAHttpTransaction *) 1.2654 +{ 1.2655 + // This API is meant for pipelining, SpdySession3's should be 1.2656 + // extended with AddStream() 1.2657 + 1.2658 + MOZ_ASSERT(false, 1.2659 + "SpdySession3::AddTransaction() should not be called"); 1.2660 + 1.2661 + return NS_ERROR_NOT_IMPLEMENTED; 1.2662 +} 1.2663 + 1.2664 +uint32_t 1.2665 +SpdySession3::PipelineDepth() 1.2666 +{ 1.2667 + return IsDone() ? 0 : 1; 1.2668 +} 1.2669 + 1.2670 +nsresult 1.2671 +SpdySession3::SetPipelinePosition(int32_t position) 1.2672 +{ 1.2673 + // This API is meant for pipelining, SpdySession3's should be 1.2674 + // extended with AddStream() 1.2675 + 1.2676 + MOZ_ASSERT(false, 1.2677 + "SpdySession3::SetPipelinePosition() should not be called"); 1.2678 + 1.2679 + return NS_ERROR_NOT_IMPLEMENTED; 1.2680 +} 1.2681 + 1.2682 +int32_t 1.2683 +SpdySession3::PipelinePosition() 1.2684 +{ 1.2685 + return 0; 1.2686 +} 1.2687 + 1.2688 +//----------------------------------------------------------------------------- 1.2689 +// Pass through methods of nsAHttpConnection 1.2690 +//----------------------------------------------------------------------------- 1.2691 + 1.2692 +nsAHttpConnection * 1.2693 +SpdySession3::Connection() 1.2694 +{ 1.2695 + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); 1.2696 + return mConnection; 1.2697 +} 1.2698 + 1.2699 +nsresult 1.2700 +SpdySession3::OnHeadersAvailable(nsAHttpTransaction *transaction, 1.2701 + nsHttpRequestHead *requestHead, 1.2702 + nsHttpResponseHead *responseHead, 1.2703 + bool *reset) 1.2704 +{ 1.2705 + return mConnection->OnHeadersAvailable(transaction, 1.2706 + requestHead, 1.2707 + responseHead, 1.2708 + reset); 1.2709 +} 1.2710 + 1.2711 +bool 1.2712 +SpdySession3::IsReused() 1.2713 +{ 1.2714 + return mConnection->IsReused(); 1.2715 +} 1.2716 + 1.2717 +nsresult 1.2718 +SpdySession3::PushBack(const char *buf, uint32_t len) 1.2719 +{ 1.2720 + return mConnection->PushBack(buf, len); 1.2721 +} 1.2722 + 1.2723 +} // namespace mozilla::net 1.2724 +} // namespace mozilla