netwerk/protocol/http/SpdySession31.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set sw=2 ts=8 et tw=80 : */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // HttpLog.h should generally be included first
michael@0 8 #include "HttpLog.h"
michael@0 9
michael@0 10 // Log on level :5, instead of default :4.
michael@0 11 #undef LOG
michael@0 12 #define LOG(args) LOG5(args)
michael@0 13 #undef LOG_ENABLED
michael@0 14 #define LOG_ENABLED() LOG5_ENABLED()
michael@0 15
michael@0 16 #include "mozilla/Telemetry.h"
michael@0 17 #include "mozilla/Preferences.h"
michael@0 18 #include "nsHttp.h"
michael@0 19 #include "nsHttpHandler.h"
michael@0 20 #include "nsHttpConnection.h"
michael@0 21 #include "nsILoadGroup.h"
michael@0 22 #include "prprf.h"
michael@0 23 #include "prnetdb.h"
michael@0 24 #include "SpdyPush31.h"
michael@0 25 #include "SpdySession31.h"
michael@0 26 #include "SpdyStream31.h"
michael@0 27 #include "SpdyZlibReporter.h"
michael@0 28
michael@0 29 #include <algorithm>
michael@0 30
michael@0 31 #ifdef DEBUG
michael@0 32 // defined by the socket transport service while active
michael@0 33 extern PRThread *gSocketThread;
michael@0 34 #endif
michael@0 35
michael@0 36 namespace mozilla {
michael@0 37 namespace net {
michael@0 38
michael@0 39 // SpdySession31 has multiple inheritance of things that implement
michael@0 40 // nsISupports, so this magic is taken from nsHttpPipeline that
michael@0 41 // implements some of the same abstract classes.
michael@0 42 NS_IMPL_ADDREF(SpdySession31)
michael@0 43 NS_IMPL_RELEASE(SpdySession31)
michael@0 44 NS_INTERFACE_MAP_BEGIN(SpdySession31)
michael@0 45 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
michael@0 46 NS_INTERFACE_MAP_END
michael@0 47
michael@0 48 SpdySession31::SpdySession31(nsAHttpTransaction *aHttpTransaction,
michael@0 49 nsISocketTransport *aSocketTransport,
michael@0 50 int32_t firstPriority)
michael@0 51 : mSocketTransport(aSocketTransport),
michael@0 52 mSegmentReader(nullptr),
michael@0 53 mSegmentWriter(nullptr),
michael@0 54 mNextStreamID(1),
michael@0 55 mConcurrentHighWater(0),
michael@0 56 mDownstreamState(BUFFERING_FRAME_HEADER),
michael@0 57 mInputFrameBufferSize(kDefaultBufferSize),
michael@0 58 mInputFrameBufferUsed(0),
michael@0 59 mInputFrameDataLast(false),
michael@0 60 mInputFrameDataStream(nullptr),
michael@0 61 mNeedsCleanup(nullptr),
michael@0 62 mShouldGoAway(false),
michael@0 63 mClosed(false),
michael@0 64 mCleanShutdown(false),
michael@0 65 mDataPending(false),
michael@0 66 mGoAwayID(0),
michael@0 67 mMaxConcurrent(kDefaultMaxConcurrent),
michael@0 68 mConcurrent(0),
michael@0 69 mServerPushedResources(0),
michael@0 70 mServerInitialStreamWindow(kDefaultRwin),
michael@0 71 mLocalSessionWindow(kDefaultRwin),
michael@0 72 mRemoteSessionWindow(kDefaultRwin),
michael@0 73 mOutputQueueSize(kDefaultQueueSize),
michael@0 74 mOutputQueueUsed(0),
michael@0 75 mOutputQueueSent(0),
michael@0 76 mLastReadEpoch(PR_IntervalNow()),
michael@0 77 mPingSentEpoch(0),
michael@0 78 mNextPingID(1)
michael@0 79 {
michael@0 80 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 81
michael@0 82 static uint64_t sSerial;
michael@0 83 mSerial = ++sSerial;
michael@0 84
michael@0 85 LOG3(("SpdySession31::SpdySession31 %p transaction 1 = %p serial=0x%X\n",
michael@0 86 this, aHttpTransaction, mSerial));
michael@0 87
michael@0 88 mConnection = aHttpTransaction->Connection();
michael@0 89 mInputFrameBuffer = new char[mInputFrameBufferSize];
michael@0 90 mOutputQueueBuffer = new char[mOutputQueueSize];
michael@0 91 zlibInit();
michael@0 92
michael@0 93 mPushAllowance = gHttpHandler->SpdyPushAllowance();
michael@0 94
michael@0 95 mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
michael@0 96 GenerateSettings();
michael@0 97
michael@0 98 if (!aHttpTransaction->IsNullTransaction())
michael@0 99 AddStream(aHttpTransaction, firstPriority);
michael@0 100 mLastDataReadEpoch = mLastReadEpoch;
michael@0 101
michael@0 102 mPingThreshold = gHttpHandler->SpdyPingThreshold();
michael@0 103 }
michael@0 104
michael@0 105 PLDHashOperator
michael@0 106 SpdySession31::ShutdownEnumerator(nsAHttpTransaction *key,
michael@0 107 nsAutoPtr<SpdyStream31> &stream,
michael@0 108 void *closure)
michael@0 109 {
michael@0 110 SpdySession31 *self = static_cast<SpdySession31 *>(closure);
michael@0 111
michael@0 112 // On a clean server hangup the server sets the GoAwayID to be the ID of
michael@0 113 // the last transaction it processed. If the ID of stream in the
michael@0 114 // local stream is greater than that it can safely be restarted because the
michael@0 115 // server guarantees it was not partially processed. Streams that have not
michael@0 116 // registered an ID haven't actually been sent yet so they can always be
michael@0 117 // restarted.
michael@0 118 if (self->mCleanShutdown &&
michael@0 119 (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID()))
michael@0 120 self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted
michael@0 121 else
michael@0 122 self->CloseStream(stream, NS_ERROR_ABORT);
michael@0 123
michael@0 124 return PL_DHASH_NEXT;
michael@0 125 }
michael@0 126
michael@0 127 PLDHashOperator
michael@0 128 SpdySession31::GoAwayEnumerator(nsAHttpTransaction *key,
michael@0 129 nsAutoPtr<SpdyStream31> &stream,
michael@0 130 void *closure)
michael@0 131 {
michael@0 132 SpdySession31 *self = static_cast<SpdySession31 *>(closure);
michael@0 133
michael@0 134 // these streams were not processed by the server and can be restarted.
michael@0 135 // Do that after the enumerator completes to avoid the risk of
michael@0 136 // a restart event re-entrantly modifying this hash. Be sure not to restart
michael@0 137 // a pushed (even numbered) stream
michael@0 138 if ((stream->StreamID() > self->mGoAwayID && (stream->StreamID() & 1)) ||
michael@0 139 !stream->HasRegisteredID()) {
michael@0 140 self->mGoAwayStreamsToRestart.Push(stream);
michael@0 141 }
michael@0 142
michael@0 143 return PL_DHASH_NEXT;
michael@0 144 }
michael@0 145
michael@0 146 SpdySession31::~SpdySession31()
michael@0 147 {
michael@0 148 LOG3(("SpdySession31::~SpdySession31 %p mDownstreamState=%X",
michael@0 149 this, mDownstreamState));
michael@0 150
michael@0 151 inflateEnd(&mDownstreamZlib);
michael@0 152 deflateEnd(&mUpstreamZlib);
michael@0 153
michael@0 154 mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
michael@0 155 Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
michael@0 156 Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
michael@0 157 Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
michael@0 158 mServerPushedResources);
michael@0 159 }
michael@0 160
michael@0 161 void
michael@0 162 SpdySession31::LogIO(SpdySession31 *self, SpdyStream31 *stream, const char *label,
michael@0 163 const char *data, uint32_t datalen)
michael@0 164 {
michael@0 165 if (!LOG4_ENABLED())
michael@0 166 return;
michael@0 167
michael@0 168 LOG4(("SpdySession31::LogIO %p stream=%p id=0x%X [%s]",
michael@0 169 self, stream, stream ? stream->StreamID() : 0, label));
michael@0 170
michael@0 171 // Max line is (16 * 3) + 10(prefix) + newline + null
michael@0 172 char linebuf[128];
michael@0 173 uint32_t index;
michael@0 174 char *line = linebuf;
michael@0 175
michael@0 176 linebuf[127] = 0;
michael@0 177
michael@0 178 for (index = 0; index < datalen; ++index) {
michael@0 179 if (!(index % 16)) {
michael@0 180 if (index) {
michael@0 181 *line = 0;
michael@0 182 LOG4(("%s", linebuf));
michael@0 183 }
michael@0 184 line = linebuf;
michael@0 185 PR_snprintf(line, 128, "%08X: ", index);
michael@0 186 line += 10;
michael@0 187 }
michael@0 188 PR_snprintf(line, 128 - (line - linebuf), "%02X ",
michael@0 189 ((unsigned char *)data)[index]);
michael@0 190 line += 3;
michael@0 191 }
michael@0 192 if (index) {
michael@0 193 *line = 0;
michael@0 194 LOG4(("%s", linebuf));
michael@0 195 }
michael@0 196 }
michael@0 197
michael@0 198 bool
michael@0 199 SpdySession31::RoomForMoreConcurrent()
michael@0 200 {
michael@0 201 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 202
michael@0 203 return (mConcurrent < mMaxConcurrent);
michael@0 204 }
michael@0 205
michael@0 206 bool
michael@0 207 SpdySession31::RoomForMoreStreams()
michael@0 208 {
michael@0 209 if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
michael@0 210 return false;
michael@0 211
michael@0 212 return !mShouldGoAway;
michael@0 213 }
michael@0 214
michael@0 215 PRIntervalTime
michael@0 216 SpdySession31::IdleTime()
michael@0 217 {
michael@0 218 return PR_IntervalNow() - mLastDataReadEpoch;
michael@0 219 }
michael@0 220
michael@0 221 uint32_t
michael@0 222 SpdySession31::ReadTimeoutTick(PRIntervalTime now)
michael@0 223 {
michael@0 224 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 225 MOZ_ASSERT(mNextPingID & 1, "Ping Counter Not Odd");
michael@0 226
michael@0 227 LOG(("SpdySession31::ReadTimeoutTick %p delta since last read %ds\n",
michael@0 228 this, PR_IntervalToSeconds(now - mLastReadEpoch)));
michael@0 229
michael@0 230 if (!mPingThreshold)
michael@0 231 return UINT32_MAX;
michael@0 232
michael@0 233 if ((now - mLastReadEpoch) < mPingThreshold) {
michael@0 234 // recent activity means ping is not an issue
michael@0 235 if (mPingSentEpoch)
michael@0 236 mPingSentEpoch = 0;
michael@0 237
michael@0 238 return PR_IntervalToSeconds(mPingThreshold) -
michael@0 239 PR_IntervalToSeconds(now - mLastReadEpoch);
michael@0 240 }
michael@0 241
michael@0 242 if (mPingSentEpoch) {
michael@0 243 LOG(("SpdySession31::ReadTimeoutTick %p handle outstanding ping\n", this));
michael@0 244 if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
michael@0 245 LOG(("SpdySession31::ReadTimeoutTick %p Ping Timer Exhaustion\n",
michael@0 246 this));
michael@0 247 mPingSentEpoch = 0;
michael@0 248 Close(NS_ERROR_NET_TIMEOUT);
michael@0 249 return UINT32_MAX;
michael@0 250 }
michael@0 251 return 1; // run the tick aggressively while ping is outstanding
michael@0 252 }
michael@0 253
michael@0 254 LOG(("SpdySession31::ReadTimeoutTick %p generating ping 0x%X\n",
michael@0 255 this, mNextPingID));
michael@0 256
michael@0 257 if (mNextPingID == 0xffffffff) {
michael@0 258 LOG(("SpdySession31::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
michael@0 259 this));
michael@0 260 return UINT32_MAX;
michael@0 261 }
michael@0 262
michael@0 263 mPingSentEpoch = PR_IntervalNow();
michael@0 264 if (!mPingSentEpoch)
michael@0 265 mPingSentEpoch = 1; // avoid the 0 sentinel value
michael@0 266 GeneratePing(mNextPingID);
michael@0 267 mNextPingID += 2;
michael@0 268 ResumeRecv(); // read the ping reply
michael@0 269
michael@0 270 // Check for orphaned push streams. This looks expensive, but generally the
michael@0 271 // list is empty.
michael@0 272 SpdyPushedStream31 *deleteMe;
michael@0 273 TimeStamp timestampNow;
michael@0 274 do {
michael@0 275 deleteMe = nullptr;
michael@0 276
michael@0 277 for (uint32_t index = mPushedStreams.Length();
michael@0 278 index > 0 ; --index) {
michael@0 279 SpdyPushedStream31 *pushedStream = mPushedStreams[index - 1];
michael@0 280
michael@0 281 if (timestampNow.IsNull())
michael@0 282 timestampNow = TimeStamp::Now(); // lazy initializer
michael@0 283
michael@0 284 // if spdy finished, but not connected, and its been like that for too long..
michael@0 285 // cleanup the stream..
michael@0 286 if (pushedStream->IsOrphaned(timestampNow))
michael@0 287 {
michael@0 288 LOG3(("SpdySession31 Timeout Pushed Stream %p 0x%X\n",
michael@0 289 this, pushedStream->StreamID()));
michael@0 290 deleteMe = pushedStream;
michael@0 291 break; // don't CleanupStream() while iterating this vector
michael@0 292 }
michael@0 293 }
michael@0 294 if (deleteMe)
michael@0 295 CleanupStream(deleteMe, NS_ERROR_ABORT, RST_CANCEL);
michael@0 296
michael@0 297 } while (deleteMe);
michael@0 298
michael@0 299 if (mNextPingID == 0xffffffff) {
michael@0 300 LOG(("SpdySession31::ReadTimeoutTick %p "
michael@0 301 "ping ids exhausted marking goaway\n", this));
michael@0 302 mShouldGoAway = true;
michael@0 303 }
michael@0 304 return 1; // run the tick aggressively while ping is outstanding
michael@0 305 }
michael@0 306
michael@0 307 uint32_t
michael@0 308 SpdySession31::RegisterStreamID(SpdyStream31 *stream, uint32_t aNewID)
michael@0 309 {
michael@0 310 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 311
michael@0 312 MOZ_ASSERT(mNextStreamID < 0xfffffff0,
michael@0 313 "should have stopped admitting streams");
michael@0 314
michael@0 315 MOZ_ASSERT(!(aNewID & 1),
michael@0 316 "0 for autoassign pull, otherwise explicit even push assignment");
michael@0 317 if (!aNewID) {
michael@0 318 // auto generate a new pull stream ID
michael@0 319 aNewID = mNextStreamID;
michael@0 320 MOZ_ASSERT(aNewID & 1, "pull ID must be odd.");
michael@0 321 mNextStreamID += 2;
michael@0 322 }
michael@0 323
michael@0 324 LOG3(("SpdySession31::RegisterStreamID session=%p stream=%p id=0x%X "
michael@0 325 "concurrent=%d",this, stream, aNewID, mConcurrent));
michael@0 326
michael@0 327 // We've used up plenty of ID's on this session. Start
michael@0 328 // moving to a new one before there is a crunch involving
michael@0 329 // server push streams or concurrent non-registered submits
michael@0 330 if (aNewID >= kMaxStreamID)
michael@0 331 mShouldGoAway = true;
michael@0 332
michael@0 333 // integrity check
michael@0 334 if (mStreamIDHash.Get(aNewID)) {
michael@0 335 LOG3((" New ID already present\n"));
michael@0 336 MOZ_ASSERT(false, "New ID already present in mStreamIDHash");
michael@0 337 mShouldGoAway = true;
michael@0 338 return kDeadStreamID;
michael@0 339 }
michael@0 340
michael@0 341 mStreamIDHash.Put(aNewID, stream);
michael@0 342 return aNewID;
michael@0 343 }
michael@0 344
michael@0 345 bool
michael@0 346 SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction,
michael@0 347 int32_t aPriority)
michael@0 348 {
michael@0 349 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 350
michael@0 351 // integrity check
michael@0 352 if (mStreamTransactionHash.Get(aHttpTransaction)) {
michael@0 353 LOG3((" New transaction already present\n"));
michael@0 354 MOZ_ASSERT(false, "AddStream duplicate transaction pointer");
michael@0 355 return false;
michael@0 356 }
michael@0 357
michael@0 358 aHttpTransaction->SetConnection(this);
michael@0 359 SpdyStream31 *stream = new SpdyStream31(aHttpTransaction, this, aPriority);
michael@0 360
michael@0 361 LOG3(("SpdySession31::AddStream session=%p stream=%p NextID=0x%X (tentative)",
michael@0 362 this, stream, mNextStreamID));
michael@0 363
michael@0 364 mStreamTransactionHash.Put(aHttpTransaction, stream);
michael@0 365
michael@0 366 if (RoomForMoreConcurrent()) {
michael@0 367 LOG3(("SpdySession31::AddStream %p stream %p activated immediately.",
michael@0 368 this, stream));
michael@0 369 ActivateStream(stream);
michael@0 370 }
michael@0 371 else {
michael@0 372 LOG3(("SpdySession31::AddStream %p stream %p queued.", this, stream));
michael@0 373 mQueuedStreams.Push(stream);
michael@0 374 }
michael@0 375
michael@0 376 if (!(aHttpTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE)) {
michael@0 377 LOG3(("SpdySession31::AddStream %p transaction %p forces keep-alive off.\n",
michael@0 378 this, aHttpTransaction));
michael@0 379 DontReuse();
michael@0 380 }
michael@0 381
michael@0 382 return true;
michael@0 383 }
michael@0 384
michael@0 385 void
michael@0 386 SpdySession31::ActivateStream(SpdyStream31 *stream)
michael@0 387 {
michael@0 388 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 389 MOZ_ASSERT(!stream->StreamID() || (stream->StreamID() & 1),
michael@0 390 "Do not activate pushed streams");
michael@0 391
michael@0 392 ++mConcurrent;
michael@0 393 if (mConcurrent > mConcurrentHighWater)
michael@0 394 mConcurrentHighWater = mConcurrent;
michael@0 395 LOG3(("SpdySession31::AddStream %p activating stream %p Currently %d "
michael@0 396 "streams in session, high water mark is %d",
michael@0 397 this, stream, mConcurrent, mConcurrentHighWater));
michael@0 398
michael@0 399 mReadyForWrite.Push(stream);
michael@0 400 SetWriteCallbacks();
michael@0 401
michael@0 402 // Kick off the SYN transmit without waiting for the poll loop
michael@0 403 // This won't work for stream id=1 because there is no segment reader
michael@0 404 // yet.
michael@0 405 if (mSegmentReader) {
michael@0 406 uint32_t countRead;
michael@0 407 ReadSegments(nullptr, kDefaultBufferSize, &countRead);
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 void
michael@0 412 SpdySession31::ProcessPending()
michael@0 413 {
michael@0 414 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 415
michael@0 416 while (RoomForMoreConcurrent()) {
michael@0 417 SpdyStream31 *stream = static_cast<SpdyStream31 *>(mQueuedStreams.PopFront());
michael@0 418 if (!stream)
michael@0 419 return;
michael@0 420 LOG3(("SpdySession31::ProcessPending %p stream %p activated from queue.",
michael@0 421 this, stream));
michael@0 422 ActivateStream(stream);
michael@0 423 }
michael@0 424 }
michael@0 425
michael@0 426 nsresult
michael@0 427 SpdySession31::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
michael@0 428 uint32_t count, uint32_t *countWritten)
michael@0 429 {
michael@0 430 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 431
michael@0 432 if (!count) {
michael@0 433 *countWritten = 0;
michael@0 434 return NS_OK;
michael@0 435 }
michael@0 436
michael@0 437 nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
michael@0 438 if (NS_SUCCEEDED(rv) && *countWritten > 0)
michael@0 439 mLastReadEpoch = PR_IntervalNow();
michael@0 440 return rv;
michael@0 441 }
michael@0 442
michael@0 443 void
michael@0 444 SpdySession31::SetWriteCallbacks()
michael@0 445 {
michael@0 446 if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
michael@0 447 mConnection->ResumeSend();
michael@0 448 }
michael@0 449
michael@0 450 void
michael@0 451 SpdySession31::RealignOutputQueue()
michael@0 452 {
michael@0 453 mOutputQueueUsed -= mOutputQueueSent;
michael@0 454 memmove(mOutputQueueBuffer.get(),
michael@0 455 mOutputQueueBuffer.get() + mOutputQueueSent,
michael@0 456 mOutputQueueUsed);
michael@0 457 mOutputQueueSent = 0;
michael@0 458 }
michael@0 459
michael@0 460 void
michael@0 461 SpdySession31::FlushOutputQueue()
michael@0 462 {
michael@0 463 if (!mSegmentReader || !mOutputQueueUsed)
michael@0 464 return;
michael@0 465
michael@0 466 nsresult rv;
michael@0 467 uint32_t countRead;
michael@0 468 uint32_t avail = mOutputQueueUsed - mOutputQueueSent;
michael@0 469
michael@0 470 rv = mSegmentReader->
michael@0 471 OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
michael@0 472 &countRead);
michael@0 473 LOG3(("SpdySession31::FlushOutputQueue %p sz=%d rv=%x actual=%d",
michael@0 474 this, avail, rv, countRead));
michael@0 475
michael@0 476 // Dont worry about errors on write, we will pick this up as a read error too
michael@0 477 if (NS_FAILED(rv))
michael@0 478 return;
michael@0 479
michael@0 480 if (countRead == avail) {
michael@0 481 mOutputQueueUsed = 0;
michael@0 482 mOutputQueueSent = 0;
michael@0 483 return;
michael@0 484 }
michael@0 485
michael@0 486 mOutputQueueSent += countRead;
michael@0 487
michael@0 488 // If the output queue is close to filling up and we have sent out a good
michael@0 489 // chunk of data from the beginning then realign it.
michael@0 490
michael@0 491 if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
michael@0 492 ((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
michael@0 493 RealignOutputQueue();
michael@0 494 }
michael@0 495 }
michael@0 496
michael@0 497 void
michael@0 498 SpdySession31::DontReuse()
michael@0 499 {
michael@0 500 mShouldGoAway = true;
michael@0 501 if (!mStreamTransactionHash.Count())
michael@0 502 Close(NS_OK);
michael@0 503 }
michael@0 504
michael@0 505 uint32_t
michael@0 506 SpdySession31::GetWriteQueueSize()
michael@0 507 {
michael@0 508 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 509
michael@0 510 return mReadyForWrite.GetSize();
michael@0 511 }
michael@0 512
michael@0 513 void
michael@0 514 SpdySession31::ChangeDownstreamState(enum stateType newState)
michael@0 515 {
michael@0 516 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 517
michael@0 518 LOG3(("SpdyStream31::ChangeDownstreamState() %p from %X to %X",
michael@0 519 this, mDownstreamState, newState));
michael@0 520 mDownstreamState = newState;
michael@0 521 }
michael@0 522
michael@0 523 void
michael@0 524 SpdySession31::ResetDownstreamState()
michael@0 525 {
michael@0 526 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 527
michael@0 528 LOG3(("SpdyStream31::ResetDownstreamState() %p", this));
michael@0 529 ChangeDownstreamState(BUFFERING_FRAME_HEADER);
michael@0 530
michael@0 531 if (mInputFrameDataLast && mInputFrameDataStream) {
michael@0 532 mInputFrameDataLast = false;
michael@0 533 if (!mInputFrameDataStream->RecvdFin()) {
michael@0 534 LOG3((" SetRecvdFin id=0x%x\n", mInputFrameDataStream->StreamID()));
michael@0 535 mInputFrameDataStream->SetRecvdFin(true);
michael@0 536 DecrementConcurrent(mInputFrameDataStream);
michael@0 537 }
michael@0 538 }
michael@0 539 mInputFrameBufferUsed = 0;
michael@0 540 mInputFrameDataStream = nullptr;
michael@0 541 }
michael@0 542
michael@0 543 template<typename T> void
michael@0 544 SpdySession31::EnsureBuffer(nsAutoArrayPtr<T> &buf,
michael@0 545 uint32_t newSize,
michael@0 546 uint32_t preserve,
michael@0 547 uint32_t &objSize)
michael@0 548 {
michael@0 549 if (objSize >= newSize)
michael@0 550 return;
michael@0 551
michael@0 552 // Leave a little slop on the new allocation - add 2KB to
michael@0 553 // what we need and then round the result up to a 4KB (page)
michael@0 554 // boundary.
michael@0 555
michael@0 556 objSize = (newSize + 2048 + 4095) & ~4095;
michael@0 557
michael@0 558 static_assert(sizeof(T) == 1, "sizeof(T) must be 1");
michael@0 559 nsAutoArrayPtr<T> tmp(new T[objSize]);
michael@0 560 memcpy(tmp, buf, preserve);
michael@0 561 buf = tmp;
michael@0 562 }
michael@0 563
michael@0 564 // Instantiate supported templates explicitly.
michael@0 565 template void
michael@0 566 SpdySession31::EnsureBuffer(nsAutoArrayPtr<char> &buf,
michael@0 567 uint32_t newSize,
michael@0 568 uint32_t preserve,
michael@0 569 uint32_t &objSize);
michael@0 570
michael@0 571 template void
michael@0 572 SpdySession31::EnsureBuffer(nsAutoArrayPtr<uint8_t> &buf,
michael@0 573 uint32_t newSize,
michael@0 574 uint32_t preserve,
michael@0 575 uint32_t &objSize);
michael@0 576
michael@0 577 void
michael@0 578 SpdySession31::DecrementConcurrent(SpdyStream31 *aStream)
michael@0 579 {
michael@0 580 uint32_t id = aStream->StreamID();
michael@0 581
michael@0 582 if (id && !(id & 0x1))
michael@0 583 return; // pushed streams aren't counted in concurrent limit
michael@0 584
michael@0 585 MOZ_ASSERT(mConcurrent);
michael@0 586 --mConcurrent;
michael@0 587 LOG3(("DecrementConcurrent %p id=0x%X concurrent=%d\n",
michael@0 588 this, id, mConcurrent));
michael@0 589 ProcessPending();
michael@0 590 }
michael@0 591
michael@0 592 void
michael@0 593 SpdySession31::zlibInit()
michael@0 594 {
michael@0 595 mDownstreamZlib.zalloc = SpdyZlibReporter::Alloc;
michael@0 596 mDownstreamZlib.zfree = SpdyZlibReporter::Free;
michael@0 597 mDownstreamZlib.opaque = Z_NULL;
michael@0 598
michael@0 599 inflateInit(&mDownstreamZlib);
michael@0 600
michael@0 601 mUpstreamZlib.zalloc = SpdyZlibReporter::Alloc;
michael@0 602 mUpstreamZlib.zfree = SpdyZlibReporter::Free;
michael@0 603 mUpstreamZlib.opaque = Z_NULL;
michael@0 604
michael@0 605 // mixing carte blanche compression with tls subjects us to traffic
michael@0 606 // analysis attacks
michael@0 607 deflateInit(&mUpstreamZlib, Z_NO_COMPRESSION);
michael@0 608 deflateSetDictionary(&mUpstreamZlib,
michael@0 609 SpdyStream31::kDictionary,
michael@0 610 sizeof(SpdyStream31::kDictionary));
michael@0 611 }
michael@0 612
michael@0 613 // Need to decompress some data in order to keep the compression
michael@0 614 // context correct, but we really don't care what the result is
michael@0 615 nsresult
michael@0 616 SpdySession31::UncompressAndDiscard(uint32_t offset,
michael@0 617 uint32_t blockLen)
michael@0 618 {
michael@0 619 char *blockStart = mInputFrameBuffer + offset;
michael@0 620 unsigned char trash[2048];
michael@0 621 mDownstreamZlib.avail_in = blockLen;
michael@0 622 mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart);
michael@0 623 bool triedDictionary = false;
michael@0 624
michael@0 625 do {
michael@0 626 mDownstreamZlib.next_out = trash;
michael@0 627 mDownstreamZlib.avail_out = sizeof(trash);
michael@0 628 int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH);
michael@0 629
michael@0 630 if (zlib_rv == Z_NEED_DICT) {
michael@0 631 if (triedDictionary) {
michael@0 632 LOG3(("SpdySession31::UncompressAndDiscard %p Dictionary Error\n", this));
michael@0 633 return NS_ERROR_ILLEGAL_VALUE;
michael@0 634 }
michael@0 635
michael@0 636 triedDictionary = true;
michael@0 637 inflateSetDictionary(&mDownstreamZlib, SpdyStream31::kDictionary,
michael@0 638 sizeof(SpdyStream31::kDictionary));
michael@0 639 }
michael@0 640
michael@0 641 if (zlib_rv == Z_DATA_ERROR)
michael@0 642 return NS_ERROR_ILLEGAL_VALUE;
michael@0 643
michael@0 644 if (zlib_rv == Z_MEM_ERROR)
michael@0 645 return NS_ERROR_FAILURE;
michael@0 646 }
michael@0 647 while (mDownstreamZlib.avail_in);
michael@0 648 return NS_OK;
michael@0 649 }
michael@0 650
michael@0 651 void
michael@0 652 SpdySession31::GeneratePing(uint32_t aID)
michael@0 653 {
michael@0 654 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 655 LOG3(("SpdySession31::GeneratePing %p 0x%X\n", this, aID));
michael@0 656
michael@0 657 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
michael@0 658 mOutputQueueUsed, mOutputQueueSize);
michael@0 659 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 660 mOutputQueueUsed += 12;
michael@0 661
michael@0 662 packet[0] = kFlag_Control;
michael@0 663 packet[1] = kVersion;
michael@0 664 packet[2] = 0;
michael@0 665 packet[3] = CONTROL_TYPE_PING;
michael@0 666 packet[4] = 0; /* flags */
michael@0 667 packet[5] = 0;
michael@0 668 packet[6] = 0;
michael@0 669 packet[7] = 4; /* length */
michael@0 670
michael@0 671 aID = PR_htonl(aID);
michael@0 672 memcpy(packet + 8, &aID, 4);
michael@0 673
michael@0 674 LogIO(this, nullptr, "Generate Ping", packet, 12);
michael@0 675 FlushOutputQueue();
michael@0 676 }
michael@0 677
michael@0 678 void
michael@0 679 SpdySession31::GenerateRstStream(uint32_t aStatusCode, uint32_t aID)
michael@0 680 {
michael@0 681 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 682 LOG3(("SpdySession31::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
michael@0 683
michael@0 684 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
michael@0 685 mOutputQueueUsed, mOutputQueueSize);
michael@0 686 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 687 mOutputQueueUsed += 16;
michael@0 688
michael@0 689 packet[0] = kFlag_Control;
michael@0 690 packet[1] = kVersion;
michael@0 691 packet[2] = 0;
michael@0 692 packet[3] = CONTROL_TYPE_RST_STREAM;
michael@0 693 packet[4] = 0; /* flags */
michael@0 694 packet[5] = 0;
michael@0 695 packet[6] = 0;
michael@0 696 packet[7] = 8; /* length */
michael@0 697
michael@0 698 aID = PR_htonl(aID);
michael@0 699 memcpy(packet + 8, &aID, 4);
michael@0 700 aStatusCode = PR_htonl(aStatusCode);
michael@0 701 memcpy(packet + 12, &aStatusCode, 4);
michael@0 702
michael@0 703 LogIO(this, nullptr, "Generate Reset", packet, 16);
michael@0 704 FlushOutputQueue();
michael@0 705 }
michael@0 706
michael@0 707 void
michael@0 708 SpdySession31::GenerateGoAway(uint32_t aStatusCode)
michael@0 709 {
michael@0 710 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 711 LOG3(("SpdySession31::GenerateGoAway %p code=%X\n", this, aStatusCode));
michael@0 712
michael@0 713 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
michael@0 714 mOutputQueueUsed, mOutputQueueSize);
michael@0 715 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 716 mOutputQueueUsed += 16;
michael@0 717
michael@0 718 memset(packet, 0, 16);
michael@0 719 packet[0] = kFlag_Control;
michael@0 720 packet[1] = kVersion;
michael@0 721 packet[3] = CONTROL_TYPE_GOAWAY;
michael@0 722 packet[7] = 8; /* data length */
michael@0 723
michael@0 724 // last-good-stream-id are bytes 8-11, when we accept server push this will
michael@0 725 // need to be set non zero
michael@0 726
michael@0 727 // bytes 12-15 are the status code.
michael@0 728 aStatusCode = PR_htonl(aStatusCode);
michael@0 729 memcpy(packet + 12, &aStatusCode, 4);
michael@0 730
michael@0 731 LogIO(this, nullptr, "Generate GoAway", packet, 16);
michael@0 732 FlushOutputQueue();
michael@0 733 }
michael@0 734
michael@0 735 void
michael@0 736 SpdySession31::GenerateSettings()
michael@0 737 {
michael@0 738 uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin;
michael@0 739 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 740 LOG3(("SpdySession31::GenerateSettings %p\n", this));
michael@0 741
michael@0 742 // sized for 3 settings and a session window update to follow
michael@0 743 static const uint32_t maxDataLen = 4 + 3 * 8 + 16;
michael@0 744 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 8 + maxDataLen,
michael@0 745 mOutputQueueUsed, mOutputQueueSize);
michael@0 746 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 747
michael@0 748 memset(packet, 0, 8 + maxDataLen);
michael@0 749 packet[0] = kFlag_Control;
michael@0 750 packet[1] = kVersion;
michael@0 751 packet[3] = CONTROL_TYPE_SETTINGS;
michael@0 752
michael@0 753 uint8_t numberOfEntries = 0;
michael@0 754
michael@0 755 // entries need to be listed in order by ID
michael@0 756 // 1st entry is bytes 12 to 19
michael@0 757 // 2nd entry is bytes 20 to 27
michael@0 758 // 3rd entry is bytes 28 to 35
michael@0 759
michael@0 760 if (!gHttpHandler->AllowPush()) {
michael@0 761 // announcing that we accept 0 incoming streams is done to
michael@0 762 // disable server push
michael@0 763 packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_MAX_CONCURRENT;
michael@0 764 // The value portion of the setting pair is already initialized to 0
michael@0 765 numberOfEntries++;
michael@0 766 }
michael@0 767
michael@0 768 nsRefPtr<nsHttpConnectionInfo> ci;
michael@0 769 uint32_t cwnd = 0;
michael@0 770 GetConnectionInfo(getter_AddRefs(ci));
michael@0 771 if (ci)
michael@0 772 cwnd = gHttpHandler->ConnMgr()->GetSpdyCWNDSetting(ci);
michael@0 773 if (cwnd) {
michael@0 774 packet[12 + 8 * numberOfEntries] = PERSISTED_VALUE;
michael@0 775 packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_CWND;
michael@0 776 LOG(("SpdySession31::GenerateSettings %p sending CWND %u\n", this, cwnd));
michael@0 777 cwnd = PR_htonl(cwnd);
michael@0 778 memcpy(packet + 16 + 8 * numberOfEntries, &cwnd, 4);
michael@0 779 numberOfEntries++;
michael@0 780 }
michael@0 781
michael@0 782 // Advertise the Push RWIN and on each client SYN_STREAM pipeline
michael@0 783 // a window update with it in order to use larger initial windows with pulled
michael@0 784 // streams.
michael@0 785 packet[15 + 8 * numberOfEntries] = SETTINGS_TYPE_INITIAL_WINDOW;
michael@0 786 uint32_t rwin = PR_htonl(mPushAllowance);
michael@0 787 memcpy(packet + 16 + 8 * numberOfEntries, &rwin, 4);
michael@0 788 numberOfEntries++;
michael@0 789
michael@0 790 uint32_t dataLen = 4 + 8 * numberOfEntries;
michael@0 791 mOutputQueueUsed += 8 + dataLen;
michael@0 792 packet[7] = dataLen;
michael@0 793 packet[11] = numberOfEntries;
michael@0 794
michael@0 795 LogIO(this, nullptr, "Generate Settings", packet, 8 + dataLen);
michael@0 796
michael@0 797 if (kDefaultRwin >= ASpdySession::kInitialRwin)
michael@0 798 goto generateSettings_complete;
michael@0 799
michael@0 800 // send a window update for the session (Stream 0) for something large
michael@0 801 sessionWindowBump = PR_htonl(sessionWindowBump);
michael@0 802 mLocalSessionWindow = ASpdySession::kInitialRwin;
michael@0 803
michael@0 804 packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 805 mOutputQueueUsed += 16;
michael@0 806
michael@0 807 packet[0] = kFlag_Control;
michael@0 808 packet[1] = kVersion;
michael@0 809 packet[3] = CONTROL_TYPE_WINDOW_UPDATE;
michael@0 810 packet[7] = 8; // 8 data bytes after 8 byte header
michael@0 811
michael@0 812 // 8 to 11 stay 0 bytes for id = 0
michael@0 813 memcpy(packet + 12, &sessionWindowBump, 4);
michael@0 814
michael@0 815 LOG3(("Session Window increase at start of session %p %u\n",
michael@0 816 this, PR_ntohl(sessionWindowBump)));
michael@0 817 LogIO(this, nullptr, "Session Window Bump ", packet, 16);
michael@0 818
michael@0 819 generateSettings_complete:
michael@0 820 FlushOutputQueue();
michael@0 821 }
michael@0 822
michael@0 823 // perform a bunch of integrity checks on the stream.
michael@0 824 // returns true if passed, false (plus LOG and ABORT) if failed.
michael@0 825 bool
michael@0 826 SpdySession31::VerifyStream(SpdyStream31 *aStream, uint32_t aOptionalID = 0)
michael@0 827 {
michael@0 828 // This is annoying, but at least it is O(1)
michael@0 829 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 830
michael@0 831 #ifndef DEBUG
michael@0 832 // Only do the real verification in debug builds
michael@0 833 return true;
michael@0 834 #endif
michael@0 835
michael@0 836 if (!aStream)
michael@0 837 return true;
michael@0 838
michael@0 839 uint32_t test = 0;
michael@0 840
michael@0 841 do {
michael@0 842 if (aStream->StreamID() == kDeadStreamID)
michael@0 843 break;
michael@0 844
michael@0 845 nsAHttpTransaction *trans = aStream->Transaction();
michael@0 846
michael@0 847 test++;
michael@0 848 if (!trans)
michael@0 849 break;
michael@0 850
michael@0 851 test++;
michael@0 852 if (mStreamTransactionHash.Get(trans) != aStream)
michael@0 853 break;
michael@0 854
michael@0 855 if (aStream->StreamID()) {
michael@0 856 SpdyStream31 *idStream = mStreamIDHash.Get(aStream->StreamID());
michael@0 857
michael@0 858 test++;
michael@0 859 if (idStream != aStream)
michael@0 860 break;
michael@0 861
michael@0 862 if (aOptionalID) {
michael@0 863 test++;
michael@0 864 if (idStream->StreamID() != aOptionalID)
michael@0 865 break;
michael@0 866 }
michael@0 867 }
michael@0 868
michael@0 869 // tests passed
michael@0 870 return true;
michael@0 871 } while (0);
michael@0 872
michael@0 873 LOG(("SpdySession31 %p VerifyStream Failure %p stream->id=0x%X "
michael@0 874 "optionalID=0x%X trans=%p test=%d\n",
michael@0 875 this, aStream, aStream->StreamID(),
michael@0 876 aOptionalID, aStream->Transaction(), test));
michael@0 877
michael@0 878 MOZ_ASSERT(false, "VerifyStream");
michael@0 879 return false;
michael@0 880 }
michael@0 881
michael@0 882 void
michael@0 883 SpdySession31::CleanupStream(SpdyStream31 *aStream, nsresult aResult,
michael@0 884 rstReason aResetCode)
michael@0 885 {
michael@0 886 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 887 LOG3(("SpdySession31::CleanupStream %p %p 0x%X %X\n",
michael@0 888 this, aStream, aStream ? aStream->StreamID() : 0, aResult));
michael@0 889 if (!aStream) {
michael@0 890 return;
michael@0 891 }
michael@0 892
michael@0 893 SpdyPushedStream31 *pushSource = nullptr;
michael@0 894
michael@0 895 if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) {
michael@0 896 LOG(("SpdySession31::CleanupStream 0x%X deferred\n", aStream->StreamID()));
michael@0 897 return;
michael@0 898 }
michael@0 899
michael@0 900 if (!VerifyStream(aStream)) {
michael@0 901 LOG(("SpdySession31::CleanupStream failed to verify stream\n"));
michael@0 902 return;
michael@0 903 }
michael@0 904
michael@0 905 pushSource = aStream->PushSource();
michael@0 906
michael@0 907 if (!aStream->RecvdFin() && aStream->StreamID()) {
michael@0 908 LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
michael@0 909 aResetCode));
michael@0 910 GenerateRstStream(aResetCode, aStream->StreamID());
michael@0 911 DecrementConcurrent(aStream);
michael@0 912 }
michael@0 913
michael@0 914 CloseStream(aStream, aResult);
michael@0 915
michael@0 916 // Remove the stream from the ID hash table and, if an even id, the pushed
michael@0 917 // table too.
michael@0 918 uint32_t id = aStream->StreamID();
michael@0 919 if (id > 0) {
michael@0 920 mStreamIDHash.Remove(id);
michael@0 921 if (!(id & 1))
michael@0 922 mPushedStreams.RemoveElement(aStream);
michael@0 923 }
michael@0 924
michael@0 925 RemoveStreamFromQueues(aStream);
michael@0 926
michael@0 927 // removing from the stream transaction hash will
michael@0 928 // delete the SpdyStream31 and drop the reference to
michael@0 929 // its transaction
michael@0 930 mStreamTransactionHash.Remove(aStream->Transaction());
michael@0 931
michael@0 932 if (mShouldGoAway && !mStreamTransactionHash.Count())
michael@0 933 Close(NS_OK);
michael@0 934
michael@0 935 if (pushSource) {
michael@0 936 pushSource->SetDeferCleanupOnSuccess(false);
michael@0 937 CleanupStream(pushSource, aResult, aResetCode);
michael@0 938 }
michael@0 939 }
michael@0 940
michael@0 941 static void RemoveStreamFromQueue(SpdyStream31 *aStream, nsDeque &queue)
michael@0 942 {
michael@0 943 uint32_t size = queue.GetSize();
michael@0 944 for (uint32_t count = 0; count < size; ++count) {
michael@0 945 SpdyStream31 *stream = static_cast<SpdyStream31 *>(queue.PopFront());
michael@0 946 if (stream != aStream)
michael@0 947 queue.Push(stream);
michael@0 948 }
michael@0 949 }
michael@0 950
michael@0 951 void
michael@0 952 SpdySession31::RemoveStreamFromQueues(SpdyStream31 *aStream)
michael@0 953 {
michael@0 954 RemoveStreamFromQueue(aStream, mReadyForWrite);
michael@0 955 RemoveStreamFromQueue(aStream, mQueuedStreams);
michael@0 956 RemoveStreamFromQueue(aStream, mReadyForRead);
michael@0 957 }
michael@0 958
michael@0 959 void
michael@0 960 SpdySession31::CloseStream(SpdyStream31 *aStream, nsresult aResult)
michael@0 961 {
michael@0 962 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 963 LOG3(("SpdySession31::CloseStream %p %p 0x%x %X\n",
michael@0 964 this, aStream, aStream->StreamID(), aResult));
michael@0 965
michael@0 966 // Check if partial frame reader
michael@0 967 if (aStream == mInputFrameDataStream) {
michael@0 968 LOG3(("Stream had active partial read frame on close"));
michael@0 969 ChangeDownstreamState(DISCARDING_DATA_FRAME);
michael@0 970 mInputFrameDataStream = nullptr;
michael@0 971 }
michael@0 972
michael@0 973 RemoveStreamFromQueues(aStream);
michael@0 974
michael@0 975 // Send the stream the close() indication
michael@0 976 aStream->Close(aResult);
michael@0 977 }
michael@0 978
michael@0 979 nsresult
michael@0 980 SpdySession31::HandleSynStream(SpdySession31 *self)
michael@0 981 {
michael@0 982 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM);
michael@0 983
michael@0 984 if (self->mInputFrameDataSize < 18) {
michael@0 985 LOG3(("SpdySession31::HandleSynStream %p SYN_STREAM too short data=%d",
michael@0 986 self, self->mInputFrameDataSize));
michael@0 987 return NS_ERROR_ILLEGAL_VALUE;
michael@0 988 }
michael@0 989
michael@0 990 uint32_t streamID =
michael@0 991 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 992 uint32_t associatedID =
michael@0 993 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
michael@0 994 uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4];
michael@0 995
michael@0 996 LOG3(("SpdySession31::HandleSynStream %p recv SYN_STREAM (push) "
michael@0 997 "for ID 0x%X associated with 0x%X.\n",
michael@0 998 self, streamID, associatedID));
michael@0 999
michael@0 1000 if (streamID & 0x01) { // test for odd stream ID
michael@0 1001 LOG3(("SpdySession31::HandleSynStream %p recvd SYN_STREAM id must be even.",
michael@0 1002 self));
michael@0 1003 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1004 }
michael@0 1005
michael@0 1006 // confirm associated-to
michael@0 1007 nsresult rv = self->SetInputFrameDataStream(associatedID);
michael@0 1008 if (NS_FAILED(rv))
michael@0 1009 return rv;
michael@0 1010 SpdyStream31 *associatedStream = self->mInputFrameDataStream;
michael@0 1011
michael@0 1012 ++(self->mServerPushedResources);
michael@0 1013
michael@0 1014 // Anytime we start using the high bit of stream ID (either client or server)
michael@0 1015 // begin to migrate to a new session.
michael@0 1016 if (streamID >= kMaxStreamID)
michael@0 1017 self->mShouldGoAway = true;
michael@0 1018
michael@0 1019 bool resetStream = true;
michael@0 1020 SpdyPushCache *cache = nullptr;
michael@0 1021
michael@0 1022 if (!(flags & kFlag_Data_UNI)) {
michael@0 1023 // pushed streams require UNIDIRECTIONAL flag
michael@0 1024 LOG3(("SpdySession31::HandleSynStream %p ID %0x%X associated ID 0x%X failed.\n",
michael@0 1025 self, streamID, associatedID));
michael@0 1026 self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
michael@0 1027
michael@0 1028 } else if (!associatedID) {
michael@0 1029 // associated stream 0 will never find a match, but the spec requires a
michael@0 1030 // PROTOCOL_ERROR in this specific case
michael@0 1031 LOG3(("SpdySession31::HandleSynStream %p associated ID of 0 failed.\n", self));
michael@0 1032 self->GenerateRstStream(RST_PROTOCOL_ERROR, streamID);
michael@0 1033
michael@0 1034 } else if (!gHttpHandler->AllowPush()) {
michael@0 1035 // MAX_CONCURRENT_STREAMS of 0 in settings should have disabled push,
michael@0 1036 // but some servers are buggy about that.. or the config could have
michael@0 1037 // been updated after the settings frame was sent. In both cases just
michael@0 1038 // reject the pushed stream as refused
michael@0 1039 LOG3(("SpdySession31::HandleSynStream Push Recevied when Disabled\n"));
michael@0 1040 self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
michael@0 1041
michael@0 1042 } else if (!associatedStream) {
michael@0 1043 LOG3(("SpdySession31::HandleSynStream %p lookup associated ID failed.\n", self));
michael@0 1044 self->GenerateRstStream(RST_INVALID_STREAM, streamID);
michael@0 1045
michael@0 1046 } else {
michael@0 1047 nsILoadGroupConnectionInfo *loadGroupCI = associatedStream->LoadGroupConnectionInfo();
michael@0 1048 if (loadGroupCI) {
michael@0 1049 loadGroupCI->GetSpdyPushCache(&cache);
michael@0 1050 if (!cache) {
michael@0 1051 cache = new SpdyPushCache();
michael@0 1052 if (!cache || NS_FAILED(loadGroupCI->SetSpdyPushCache(cache))) {
michael@0 1053 delete cache;
michael@0 1054 cache = nullptr;
michael@0 1055 }
michael@0 1056 }
michael@0 1057 }
michael@0 1058 if (!cache) {
michael@0 1059 // this is unexpected, but we can handle it just be refusing the push
michael@0 1060 LOG3(("SpdySession31::HandleSynStream Push Recevied without loadgroup cache\n"));
michael@0 1061 self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
michael@0 1062 }
michael@0 1063 else {
michael@0 1064 resetStream = false;
michael@0 1065 }
michael@0 1066 }
michael@0 1067
michael@0 1068 if (resetStream) {
michael@0 1069 // Need to decompress the headers even though we aren't using them yet in
michael@0 1070 // order to keep the compression context consistent for other syn_reply frames
michael@0 1071 rv = self->UncompressAndDiscard(18, self->mInputFrameDataSize - 10);
michael@0 1072 if (NS_FAILED(rv)) {
michael@0 1073 LOG(("SpdySession31::HandleSynStream uncompress failed\n"));
michael@0 1074 return rv;
michael@0 1075 }
michael@0 1076 self->ResetDownstreamState();
michael@0 1077 return NS_OK;
michael@0 1078 }
michael@0 1079
michael@0 1080 // Create the buffering transaction and push stream
michael@0 1081 nsRefPtr<SpdyPush31TransactionBuffer> transactionBuffer =
michael@0 1082 new SpdyPush31TransactionBuffer();
michael@0 1083 transactionBuffer->SetConnection(self);
michael@0 1084 SpdyPushedStream31 *pushedStream =
michael@0 1085 new SpdyPushedStream31(transactionBuffer, self,
michael@0 1086 associatedStream, streamID);
michael@0 1087
michael@0 1088 // ownership of the pushed stream is by the transaction hash, just as it
michael@0 1089 // is for a client initiated stream. Errors that aren't fatal to the
michael@0 1090 // whole session must call cleanupStream() after this point in order
michael@0 1091 // to remove the stream from that hash.
michael@0 1092 self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
michael@0 1093 self->mPushedStreams.AppendElement(pushedStream);
michael@0 1094
michael@0 1095 // The pushed stream is unidirectional so it is fully open immediately
michael@0 1096 pushedStream->SetFullyOpen();
michael@0 1097
michael@0 1098 // Uncompress the response headers into a stream specific buffer, leaving them
michael@0 1099 // in spdy format for the time being.
michael@0 1100 rv = pushedStream->Uncompress(&self->mDownstreamZlib,
michael@0 1101 self->mInputFrameBuffer + 18,
michael@0 1102 self->mInputFrameDataSize - 10);
michael@0 1103 if (NS_FAILED(rv)) {
michael@0 1104 LOG(("SpdySession31::HandleSynStream uncompress failed\n"));
michael@0 1105 return rv;
michael@0 1106 }
michael@0 1107
michael@0 1108 if (self->RegisterStreamID(pushedStream, streamID) == kDeadStreamID) {
michael@0 1109 LOG(("SpdySession31::HandleSynStream registerstreamid failed\n"));
michael@0 1110 return NS_ERROR_FAILURE;
michael@0 1111 }
michael@0 1112
michael@0 1113 // Fake the request side of the pushed HTTP transaction. Sets up hash
michael@0 1114 // key and origin
michael@0 1115 uint32_t notUsed;
michael@0 1116 pushedStream->ReadSegments(nullptr, 1, &notUsed);
michael@0 1117
michael@0 1118 nsAutoCString key;
michael@0 1119 if (!pushedStream->GetHashKey(key)) {
michael@0 1120 LOG(("SpdySession31::HandleSynStream one of :host :scheme :path missing from push\n"));
michael@0 1121 self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
michael@0 1122 self->ResetDownstreamState();
michael@0 1123 return NS_OK;
michael@0 1124 }
michael@0 1125
michael@0 1126 if (!associatedStream->Origin().Equals(pushedStream->Origin())) {
michael@0 1127 LOG(("SpdySession31::HandleSynStream pushed stream mismatched origin\n"));
michael@0 1128 self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
michael@0 1129 self->ResetDownstreamState();
michael@0 1130 return NS_OK;
michael@0 1131 }
michael@0 1132
michael@0 1133 if (!cache->RegisterPushedStreamSpdy31(key, pushedStream)) {
michael@0 1134 LOG(("SpdySession31::HandleSynStream registerPushedStream Failed\n"));
michael@0 1135 self->CleanupStream(pushedStream, NS_ERROR_FAILURE, RST_INVALID_STREAM);
michael@0 1136 self->ResetDownstreamState();
michael@0 1137 return NS_OK;
michael@0 1138 }
michael@0 1139
michael@0 1140 self->ResetDownstreamState();
michael@0 1141 return NS_OK;
michael@0 1142 }
michael@0 1143
michael@0 1144 nsresult
michael@0 1145 SpdySession31::SetInputFrameDataStream(uint32_t streamID)
michael@0 1146 {
michael@0 1147 mInputFrameDataStream = mStreamIDHash.Get(streamID);
michael@0 1148 if (VerifyStream(mInputFrameDataStream, streamID))
michael@0 1149 return NS_OK;
michael@0 1150
michael@0 1151 LOG(("SpdySession31::SetInputFrameDataStream failed to verify 0x%X\n",
michael@0 1152 streamID));
michael@0 1153 mInputFrameDataStream = nullptr;
michael@0 1154 return NS_ERROR_UNEXPECTED;
michael@0 1155 }
michael@0 1156
michael@0 1157 nsresult
michael@0 1158 SpdySession31::HandleSynReply(SpdySession31 *self)
michael@0 1159 {
michael@0 1160 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY);
michael@0 1161
michael@0 1162 if (self->mInputFrameDataSize < 4) {
michael@0 1163 LOG3(("SpdySession31::HandleSynReply %p SYN REPLY too short data=%d",
michael@0 1164 self, self->mInputFrameDataSize));
michael@0 1165 // A framing error is a session wide error that cannot be recovered
michael@0 1166 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1167 }
michael@0 1168
michael@0 1169 LOG3(("SpdySession31::HandleSynReply %p lookup via streamID in syn_reply.\n",
michael@0 1170 self));
michael@0 1171 uint32_t streamID =
michael@0 1172 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1173 nsresult rv = self->SetInputFrameDataStream(streamID);
michael@0 1174 if (NS_FAILED(rv))
michael@0 1175 return rv;
michael@0 1176
michael@0 1177 if (!self->mInputFrameDataStream) {
michael@0 1178 // Cannot find stream. We can continue the SPDY session, but we need to
michael@0 1179 // uncompress the header block to maintain the correct compression context
michael@0 1180
michael@0 1181 LOG3(("SpdySession31::HandleSynReply %p lookup streamID in syn_reply "
michael@0 1182 "0x%X failed. NextStreamID = 0x%X\n",
michael@0 1183 self, streamID, self->mNextStreamID));
michael@0 1184
michael@0 1185 if (streamID >= self->mNextStreamID)
michael@0 1186 self->GenerateRstStream(RST_INVALID_STREAM, streamID);
michael@0 1187
michael@0 1188 rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4);
michael@0 1189 if (NS_FAILED(rv)) {
michael@0 1190 LOG(("SpdySession31::HandleSynReply uncompress failed\n"));
michael@0 1191 // this is fatal to the session
michael@0 1192 return rv;
michael@0 1193 }
michael@0 1194
michael@0 1195 self->ResetDownstreamState();
michael@0 1196 return NS_OK;
michael@0 1197 }
michael@0 1198
michael@0 1199 // Uncompress the headers into a stream specific buffer, leaving them in
michael@0 1200 // spdy format for the time being. Make certain to do this
michael@0 1201 // step before any error handling that might abort the stream but not
michael@0 1202 // the session becuase the session compression context will become
michael@0 1203 // inconsistent if all of the compressed data is not processed.
michael@0 1204 rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib,
michael@0 1205 self->mInputFrameBuffer + 12,
michael@0 1206 self->mInputFrameDataSize - 4);
michael@0 1207
michael@0 1208 if (NS_FAILED(rv)) {
michael@0 1209 LOG(("SpdySession31::HandleSynReply uncompress failed\n"));
michael@0 1210 return rv;
michael@0 1211 }
michael@0 1212
michael@0 1213 if (self->mInputFrameDataStream->GetFullyOpen()) {
michael@0 1214 // "If an endpoint receives multiple SYN_REPLY frames for the same active
michael@0 1215 // stream ID, it MUST issue a stream error (Section 2.4.2) with the error
michael@0 1216 // code STREAM_IN_USE."
michael@0 1217 //
michael@0 1218 // "STREAM_ALREADY_CLOSED. The endpoint received a data or SYN_REPLY
michael@0 1219 // frame for a stream which is half closed."
michael@0 1220 //
michael@0 1221 // If the stream is open then just RST_STREAM with STREAM_IN_USE
michael@0 1222 // If the stream is half closed then RST_STREAM with STREAM_ALREADY_CLOSED
michael@0 1223 // abort the session
michael@0 1224 //
michael@0 1225 LOG3(("SpdySession31::HandleSynReply %p dup SYN_REPLY for 0x%X"
michael@0 1226 " recvdfin=%d", self, self->mInputFrameDataStream->StreamID(),
michael@0 1227 self->mInputFrameDataStream->RecvdFin()));
michael@0 1228
michael@0 1229 self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ALREADY_OPENED,
michael@0 1230 self->mInputFrameDataStream->RecvdFin() ?
michael@0 1231 RST_STREAM_ALREADY_CLOSED : RST_STREAM_IN_USE);
michael@0 1232 self->ResetDownstreamState();
michael@0 1233 return NS_OK;
michael@0 1234 }
michael@0 1235 self->mInputFrameDataStream->SetFullyOpen();
michael@0 1236
michael@0 1237 self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
michael@0 1238 self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
michael@0 1239 self->mLastDataReadEpoch = self->mLastReadEpoch;
michael@0 1240
michael@0 1241 if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) {
michael@0 1242 LOG3(("SynReply %p had undefined flag set 0x%X\n", self, streamID));
michael@0 1243 self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
michael@0 1244 RST_PROTOCOL_ERROR);
michael@0 1245 self->ResetDownstreamState();
michael@0 1246 return NS_OK;
michael@0 1247 }
michael@0 1248
michael@0 1249 if (!self->mInputFrameDataLast) {
michael@0 1250 // don't process the headers yet as there could be more coming from HEADERS
michael@0 1251 // frames
michael@0 1252 self->ResetDownstreamState();
michael@0 1253 return NS_OK;
michael@0 1254 }
michael@0 1255
michael@0 1256 rv = self->ResponseHeadersComplete();
michael@0 1257 if (rv == NS_ERROR_ILLEGAL_VALUE) {
michael@0 1258 LOG3(("SpdySession31::HandleSynReply %p PROTOCOL_ERROR detected 0x%X\n",
michael@0 1259 self, streamID));
michael@0 1260 self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
michael@0 1261 self->ResetDownstreamState();
michael@0 1262 rv = NS_OK;
michael@0 1263 }
michael@0 1264 return rv;
michael@0 1265 }
michael@0 1266
michael@0 1267 // ResponseHeadersComplete() returns NS_ERROR_ILLEGAL_VALUE when the stream
michael@0 1268 // should be reset with a PROTOCOL_ERROR, NS_OK when the SYN_REPLY was
michael@0 1269 // fine, and any other error is fatal to the session.
michael@0 1270 nsresult
michael@0 1271 SpdySession31::ResponseHeadersComplete()
michael@0 1272 {
michael@0 1273 LOG3(("SpdySession31::ResponseHeadersComplete %p for 0x%X fin=%d",
michael@0 1274 this, mInputFrameDataStream->StreamID(), mInputFrameDataLast));
michael@0 1275
michael@0 1276 // The spdystream needs to see flattened http headers
michael@0 1277 // Uncompressed spdy format headers currently live in
michael@0 1278 // SpdyStream31::mDecompressBuffer - convert that to HTTP format in
michael@0 1279 // mFlatHTTPResponseHeaders via ConvertHeaders()
michael@0 1280
michael@0 1281 mFlatHTTPResponseHeadersOut = 0;
michael@0 1282 nsresult rv = mInputFrameDataStream->ConvertHeaders(mFlatHTTPResponseHeaders);
michael@0 1283 if (NS_FAILED(rv))
michael@0 1284 return rv;
michael@0 1285
michael@0 1286 ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
michael@0 1287 return NS_OK;
michael@0 1288 }
michael@0 1289
michael@0 1290 nsresult
michael@0 1291 SpdySession31::HandleRstStream(SpdySession31 *self)
michael@0 1292 {
michael@0 1293 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_RST_STREAM);
michael@0 1294
michael@0 1295 if (self->mInputFrameDataSize != 8) {
michael@0 1296 LOG3(("SpdySession31::HandleRstStream %p RST_STREAM wrong length data=%d",
michael@0 1297 self, self->mInputFrameDataSize));
michael@0 1298 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1299 }
michael@0 1300
michael@0 1301 uint8_t flags = reinterpret_cast<uint8_t *>(self->mInputFrameBuffer.get())[4];
michael@0 1302
michael@0 1303 uint32_t streamID =
michael@0 1304 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1305
michael@0 1306 self->mDownstreamRstReason =
michael@0 1307 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
michael@0 1308
michael@0 1309 LOG3(("SpdySession31::HandleRstStream %p RST_STREAM Reason Code %u ID %x "
michael@0 1310 "flags %x", self, self->mDownstreamRstReason, streamID, flags));
michael@0 1311
michael@0 1312 if (flags != 0) {
michael@0 1313 LOG3(("SpdySession31::HandleRstStream %p RST_STREAM with flags is illegal",
michael@0 1314 self));
michael@0 1315 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1316 }
michael@0 1317
michael@0 1318 if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
michael@0 1319 self->mDownstreamRstReason == RST_STREAM_IN_USE ||
michael@0 1320 self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
michael@0 1321 // basically just ignore this
michael@0 1322 LOG3(("SpdySession31::HandleRstStream %p No Reset Processing Needed.\n"));
michael@0 1323 self->ResetDownstreamState();
michael@0 1324 return NS_OK;
michael@0 1325 }
michael@0 1326
michael@0 1327 nsresult rv = self->SetInputFrameDataStream(streamID);
michael@0 1328
michael@0 1329 if (!self->mInputFrameDataStream) {
michael@0 1330 if (NS_FAILED(rv))
michael@0 1331 LOG(("SpdySession31::HandleRstStream %p lookup streamID for RST Frame "
michael@0 1332 "0x%X failed reason = %d :: VerifyStream Failed\n", self, streamID,
michael@0 1333 self->mDownstreamRstReason));
michael@0 1334
michael@0 1335 LOG3(("SpdySession31::HandleRstStream %p lookup streamID for RST Frame "
michael@0 1336 "0x%X failed reason = %d", self, streamID,
michael@0 1337 self->mDownstreamRstReason));
michael@0 1338 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1339 }
michael@0 1340
michael@0 1341 self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
michael@0 1342 return NS_OK;
michael@0 1343 }
michael@0 1344
michael@0 1345 PLDHashOperator
michael@0 1346 SpdySession31::UpdateServerRwinEnumerator(nsAHttpTransaction *key,
michael@0 1347 nsAutoPtr<SpdyStream31> &stream,
michael@0 1348 void *closure)
michael@0 1349 {
michael@0 1350 int32_t delta = *(static_cast<int32_t *>(closure));
michael@0 1351 stream->UpdateRemoteWindow(delta);
michael@0 1352 return PL_DHASH_NEXT;
michael@0 1353 }
michael@0 1354
michael@0 1355 nsresult
michael@0 1356 SpdySession31::HandleSettings(SpdySession31 *self)
michael@0 1357 {
michael@0 1358 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_SETTINGS);
michael@0 1359
michael@0 1360 if (self->mInputFrameDataSize < 4) {
michael@0 1361 LOG3(("SpdySession31::HandleSettings %p SETTINGS wrong length data=%d",
michael@0 1362 self, self->mInputFrameDataSize));
michael@0 1363 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1364 }
michael@0 1365
michael@0 1366 uint32_t numEntries =
michael@0 1367 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1368
michael@0 1369 // Ensure frame is large enough for supplied number of entries
michael@0 1370 // Each entry is 8 bytes, frame data is reduced by 4 to account for
michael@0 1371 // the NumEntries value.
michael@0 1372 if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) {
michael@0 1373 LOG3(("SpdySession31::HandleSettings %p SETTINGS wrong length data=%d",
michael@0 1374 self, self->mInputFrameDataSize));
michael@0 1375 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1376 }
michael@0 1377
michael@0 1378 LOG3(("SpdySession31::HandleSettings %p SETTINGS Control Frame with %d entries",
michael@0 1379 self, numEntries));
michael@0 1380
michael@0 1381 for (uint32_t index = 0; index < numEntries; ++index) {
michael@0 1382 unsigned char *setting = reinterpret_cast<unsigned char *>
michael@0 1383 (self->mInputFrameBuffer.get()) + 12 + index * 8;
michael@0 1384
michael@0 1385 uint32_t flags = setting[0];
michael@0 1386 uint32_t id = PR_ntohl(reinterpret_cast<uint32_t *>(setting)[0]) & 0xffffff;
michael@0 1387 uint32_t value = PR_ntohl(reinterpret_cast<uint32_t *>(setting)[1]);
michael@0 1388
michael@0 1389 LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
michael@0 1390
michael@0 1391 switch (id)
michael@0 1392 {
michael@0 1393 case SETTINGS_TYPE_UPLOAD_BW:
michael@0 1394 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value);
michael@0 1395 break;
michael@0 1396
michael@0 1397 case SETTINGS_TYPE_DOWNLOAD_BW:
michael@0 1398 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value);
michael@0 1399 break;
michael@0 1400
michael@0 1401 case SETTINGS_TYPE_RTT:
michael@0 1402 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
michael@0 1403 break;
michael@0 1404
michael@0 1405 case SETTINGS_TYPE_MAX_CONCURRENT:
michael@0 1406 self->mMaxConcurrent = value;
michael@0 1407 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
michael@0 1408 break;
michael@0 1409
michael@0 1410 case SETTINGS_TYPE_CWND:
michael@0 1411 if (flags & PERSIST_VALUE)
michael@0 1412 {
michael@0 1413 nsRefPtr<nsHttpConnectionInfo> ci;
michael@0 1414 self->GetConnectionInfo(getter_AddRefs(ci));
michael@0 1415 if (ci)
michael@0 1416 gHttpHandler->ConnMgr()->ReportSpdyCWNDSetting(ci, value);
michael@0 1417 }
michael@0 1418 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
michael@0 1419 break;
michael@0 1420
michael@0 1421 case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
michael@0 1422 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
michael@0 1423 break;
michael@0 1424
michael@0 1425 case SETTINGS_TYPE_INITIAL_WINDOW:
michael@0 1426 Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
michael@0 1427 {
michael@0 1428 int32_t delta = value - self->mServerInitialStreamWindow;
michael@0 1429 self->mServerInitialStreamWindow = value;
michael@0 1430
michael@0 1431 // do not use SETTINGS to adjust the session window.
michael@0 1432
michael@0 1433 // we need to add the delta to all open streams (delta can be negative)
michael@0 1434 self->mStreamTransactionHash.Enumerate(UpdateServerRwinEnumerator,
michael@0 1435 &delta);
michael@0 1436 }
michael@0 1437 break;
michael@0 1438
michael@0 1439 default:
michael@0 1440 break;
michael@0 1441 }
michael@0 1442
michael@0 1443 }
michael@0 1444
michael@0 1445 self->ResetDownstreamState();
michael@0 1446 return NS_OK;
michael@0 1447 }
michael@0 1448
michael@0 1449 nsresult
michael@0 1450 SpdySession31::HandleNoop(SpdySession31 *self)
michael@0 1451 {
michael@0 1452 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_NOOP);
michael@0 1453
michael@0 1454 // Should not be receiving noop frames in spdy/3, so we'll just
michael@0 1455 // make a log and ignore it
michael@0 1456
michael@0 1457 LOG3(("SpdySession31::HandleNoop %p NOP.", self));
michael@0 1458
michael@0 1459 self->ResetDownstreamState();
michael@0 1460 return NS_OK;
michael@0 1461 }
michael@0 1462
michael@0 1463 nsresult
michael@0 1464 SpdySession31::HandlePing(SpdySession31 *self)
michael@0 1465 {
michael@0 1466 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_PING);
michael@0 1467
michael@0 1468 if (self->mInputFrameDataSize != 4) {
michael@0 1469 LOG3(("SpdySession31::HandlePing %p PING had wrong amount of data %d",
michael@0 1470 self, self->mInputFrameDataSize));
michael@0 1471 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1472 }
michael@0 1473
michael@0 1474 uint32_t pingID =
michael@0 1475 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1476
michael@0 1477 LOG3(("SpdySession31::HandlePing %p PING ID 0x%X.", self, pingID));
michael@0 1478
michael@0 1479 if (pingID & 0x01) {
michael@0 1480 // presumably a reply to our timeout ping
michael@0 1481 self->mPingSentEpoch = 0;
michael@0 1482 }
michael@0 1483 else {
michael@0 1484 // Servers initiate even numbered pings, go ahead and echo it back
michael@0 1485 self->GeneratePing(pingID);
michael@0 1486 }
michael@0 1487
michael@0 1488 self->ResetDownstreamState();
michael@0 1489 return NS_OK;
michael@0 1490 }
michael@0 1491
michael@0 1492 nsresult
michael@0 1493 SpdySession31::HandleGoAway(SpdySession31 *self)
michael@0 1494 {
michael@0 1495 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_GOAWAY);
michael@0 1496
michael@0 1497 if (self->mInputFrameDataSize != 8) {
michael@0 1498 LOG3(("SpdySession31::HandleGoAway %p GOAWAY had wrong amount of data %d",
michael@0 1499 self, self->mInputFrameDataSize));
michael@0 1500 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1501 }
michael@0 1502
michael@0 1503 self->mShouldGoAway = true;
michael@0 1504 self->mGoAwayID =
michael@0 1505 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1506 self->mCleanShutdown = true;
michael@0 1507
michael@0 1508 // Find streams greater than the last-good ID and mark them for deletion
michael@0 1509 // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. The
michael@0 1510 // underlying transaction can be restarted.
michael@0 1511 self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self);
michael@0 1512
michael@0 1513 // Process the streams marked for deletion and restart.
michael@0 1514 uint32_t size = self->mGoAwayStreamsToRestart.GetSize();
michael@0 1515 for (uint32_t count = 0; count < size; ++count) {
michael@0 1516 SpdyStream31 *stream =
michael@0 1517 static_cast<SpdyStream31 *>(self->mGoAwayStreamsToRestart.PopFront());
michael@0 1518
michael@0 1519 self->CloseStream(stream, NS_ERROR_NET_RESET);
michael@0 1520 if (stream->HasRegisteredID())
michael@0 1521 self->mStreamIDHash.Remove(stream->StreamID());
michael@0 1522 self->mStreamTransactionHash.Remove(stream->Transaction());
michael@0 1523 }
michael@0 1524
michael@0 1525 // Queued streams can also be deleted from this session and restarted
michael@0 1526 // in another one. (they were never sent on the network so they implicitly
michael@0 1527 // are not covered by the last-good id.
michael@0 1528 size = self->mQueuedStreams.GetSize();
michael@0 1529 for (uint32_t count = 0; count < size; ++count) {
michael@0 1530 SpdyStream31 *stream =
michael@0 1531 static_cast<SpdyStream31 *>(self->mQueuedStreams.PopFront());
michael@0 1532 self->CloseStream(stream, NS_ERROR_NET_RESET);
michael@0 1533 self->mStreamTransactionHash.Remove(stream->Transaction());
michael@0 1534 }
michael@0 1535
michael@0 1536 LOG3(("SpdySession31::HandleGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X "
michael@0 1537 "live streams=%d\n", self, self->mGoAwayID,
michael@0 1538 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]),
michael@0 1539 self->mStreamTransactionHash.Count()));
michael@0 1540
michael@0 1541 self->ResetDownstreamState();
michael@0 1542 return NS_OK;
michael@0 1543 }
michael@0 1544
michael@0 1545 nsresult
michael@0 1546 SpdySession31::HandleHeaders(SpdySession31 *self)
michael@0 1547 {
michael@0 1548 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_HEADERS);
michael@0 1549
michael@0 1550 if (self->mInputFrameDataSize < 4) {
michael@0 1551 LOG3(("SpdySession31::HandleHeaders %p HEADERS had wrong amount of data %d",
michael@0 1552 self, self->mInputFrameDataSize));
michael@0 1553 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1554 }
michael@0 1555
michael@0 1556 uint32_t streamID =
michael@0 1557 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1558 LOG3(("SpdySession31::HandleHeaders %p HEADERS for Stream 0x%X.\n",
michael@0 1559 self, streamID));
michael@0 1560 nsresult rv = self->SetInputFrameDataStream(streamID);
michael@0 1561 if (NS_FAILED(rv))
michael@0 1562 return rv;
michael@0 1563
michael@0 1564 if (!self->mInputFrameDataStream) {
michael@0 1565 LOG3(("SpdySession31::HandleHeaders %p lookup streamID 0x%X failed.\n",
michael@0 1566 self, streamID));
michael@0 1567 if (streamID >= self->mNextStreamID)
michael@0 1568 self->GenerateRstStream(RST_INVALID_STREAM, streamID);
michael@0 1569
michael@0 1570 rv = self->UncompressAndDiscard(12, self->mInputFrameDataSize - 4);
michael@0 1571 if (NS_FAILED(rv)) {
michael@0 1572 LOG(("SpdySession31::HandleHeaders uncompress failed\n"));
michael@0 1573 // this is fatal to the session
michael@0 1574 return rv;
michael@0 1575 }
michael@0 1576 self->ResetDownstreamState();
michael@0 1577 return NS_OK;
michael@0 1578 }
michael@0 1579
michael@0 1580 // Uncompress the headers into local buffers in the SpdyStream, leaving
michael@0 1581 // them in spdy format for the time being. Make certain to do this
michael@0 1582 // step before any error handling that might abort the stream but not
michael@0 1583 // the session becuase the session compression context will become
michael@0 1584 // inconsistent if all of the compressed data is not processed.
michael@0 1585 rv = self->mInputFrameDataStream->Uncompress(&self->mDownstreamZlib,
michael@0 1586 self->mInputFrameBuffer + 12,
michael@0 1587 self->mInputFrameDataSize - 4);
michael@0 1588 if (NS_FAILED(rv)) {
michael@0 1589 LOG(("SpdySession31::HandleHeaders uncompress failed\n"));
michael@0 1590 return rv;
michael@0 1591 }
michael@0 1592
michael@0 1593 self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
michael@0 1594 self->mInputFrameDataStream->
michael@0 1595 UpdateTransportReadEvents(self->mInputFrameDataSize);
michael@0 1596 self->mLastDataReadEpoch = self->mLastReadEpoch;
michael@0 1597
michael@0 1598 if (self->mInputFrameBuffer[4] & ~kFlag_Data_FIN) {
michael@0 1599 LOG3(("Headers %p had undefined flag set 0x%X\n", self, streamID));
michael@0 1600 self->CleanupStream(self->mInputFrameDataStream, NS_ERROR_ILLEGAL_VALUE,
michael@0 1601 RST_PROTOCOL_ERROR);
michael@0 1602 self->ResetDownstreamState();
michael@0 1603 return NS_OK;
michael@0 1604 }
michael@0 1605
michael@0 1606 if (!self->mInputFrameDataLast) {
michael@0 1607 // don't process the headers yet as there could be more HEADERS frames
michael@0 1608 self->ResetDownstreamState();
michael@0 1609 return NS_OK;
michael@0 1610 }
michael@0 1611
michael@0 1612 rv = self->ResponseHeadersComplete();
michael@0 1613 if (rv == NS_ERROR_ILLEGAL_VALUE) {
michael@0 1614 LOG3(("SpdySession31::HanndleHeaders %p PROTOCOL_ERROR detected 0x%X\n",
michael@0 1615 self, streamID));
michael@0 1616 self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
michael@0 1617 self->ResetDownstreamState();
michael@0 1618 rv = NS_OK;
michael@0 1619 }
michael@0 1620 return rv;
michael@0 1621 }
michael@0 1622
michael@0 1623 PLDHashOperator
michael@0 1624 SpdySession31::RestartBlockedOnRwinEnumerator(nsAHttpTransaction *key,
michael@0 1625 nsAutoPtr<SpdyStream31> &stream,
michael@0 1626 void *closure)
michael@0 1627 {
michael@0 1628 SpdySession31 *self = static_cast<SpdySession31 *>(closure);
michael@0 1629 MOZ_ASSERT(self->mRemoteSessionWindow > 0);
michael@0 1630
michael@0 1631 if (!stream->BlockedOnRwin() || stream->RemoteWindow() <= 0)
michael@0 1632 return PL_DHASH_NEXT;
michael@0 1633
michael@0 1634 self->mReadyForWrite.Push(stream);
michael@0 1635 self->SetWriteCallbacks();
michael@0 1636 return PL_DHASH_NEXT;
michael@0 1637 }
michael@0 1638
michael@0 1639 nsresult
michael@0 1640 SpdySession31::HandleWindowUpdate(SpdySession31 *self)
michael@0 1641 {
michael@0 1642 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE);
michael@0 1643
michael@0 1644 if (self->mInputFrameDataSize < 8) {
michael@0 1645 LOG3(("SpdySession31::HandleWindowUpdate %p Window Update wrong length %d\n",
michael@0 1646 self, self->mInputFrameDataSize));
michael@0 1647 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1648 }
michael@0 1649
michael@0 1650 uint32_t delta =
michael@0 1651 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[3]);
michael@0 1652 delta &= 0x7fffffff;
michael@0 1653 uint32_t streamID =
michael@0 1654 PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
michael@0 1655 streamID &= 0x7fffffff;
michael@0 1656
michael@0 1657 LOG3(("SpdySession31::HandleWindowUpdate %p len=%d for Stream 0x%X.\n",
michael@0 1658 self, delta, streamID));
michael@0 1659
michael@0 1660 // ID of 0 is a session window update
michael@0 1661 if (streamID) {
michael@0 1662 nsresult rv = self->SetInputFrameDataStream(streamID);
michael@0 1663 if (NS_FAILED(rv))
michael@0 1664 return rv;
michael@0 1665
michael@0 1666 if (!self->mInputFrameDataStream) {
michael@0 1667 LOG3(("SpdySession31::HandleWindowUpdate %p lookup streamID 0x%X failed.\n",
michael@0 1668 self, streamID));
michael@0 1669 if (streamID >= self->mNextStreamID)
michael@0 1670 self->GenerateRstStream(RST_INVALID_STREAM, streamID);
michael@0 1671 self->ResetDownstreamState();
michael@0 1672 return NS_OK;
michael@0 1673 }
michael@0 1674
michael@0 1675 self->mInputFrameDataStream->UpdateRemoteWindow(delta);
michael@0 1676 } else {
michael@0 1677 int64_t oldRemoteWindow = self->mRemoteSessionWindow;
michael@0 1678 self->mRemoteSessionWindow += delta;
michael@0 1679 if ((oldRemoteWindow <= 0) && (self->mRemoteSessionWindow > 0)) {
michael@0 1680 LOG3(("SpdySession31::HandleWindowUpdate %p restart session window\n",
michael@0 1681 self));
michael@0 1682 self->mStreamTransactionHash.Enumerate(RestartBlockedOnRwinEnumerator, self);
michael@0 1683 }
michael@0 1684 }
michael@0 1685
michael@0 1686 self->ResetDownstreamState();
michael@0 1687 return NS_OK;
michael@0 1688 }
michael@0 1689
michael@0 1690 nsresult
michael@0 1691 SpdySession31::HandleCredential(SpdySession31 *self)
michael@0 1692 {
michael@0 1693 MOZ_ASSERT(self->mFrameControlType == CONTROL_TYPE_CREDENTIAL);
michael@0 1694
michael@0 1695 // These aren't used yet. Just ignore the frame.
michael@0 1696
michael@0 1697 LOG3(("SpdySession31::HandleCredential %p NOP.", self));
michael@0 1698
michael@0 1699 self->ResetDownstreamState();
michael@0 1700 return NS_OK;
michael@0 1701 }
michael@0 1702
michael@0 1703 //-----------------------------------------------------------------------------
michael@0 1704 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
michael@0 1705 // of these methods
michael@0 1706 //-----------------------------------------------------------------------------
michael@0 1707
michael@0 1708 void
michael@0 1709 SpdySession31::OnTransportStatus(nsITransport* aTransport,
michael@0 1710 nsresult aStatus,
michael@0 1711 uint64_t aProgress)
michael@0 1712 {
michael@0 1713 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 1714
michael@0 1715 switch (aStatus) {
michael@0 1716 // These should appear only once, deliver to the first
michael@0 1717 // transaction on the session.
michael@0 1718 case NS_NET_STATUS_RESOLVING_HOST:
michael@0 1719 case NS_NET_STATUS_RESOLVED_HOST:
michael@0 1720 case NS_NET_STATUS_CONNECTING_TO:
michael@0 1721 case NS_NET_STATUS_CONNECTED_TO:
michael@0 1722 {
michael@0 1723 SpdyStream31 *target = mStreamIDHash.Get(1);
michael@0 1724 nsAHttpTransaction *transaction = target ? target->Transaction() : nullptr;
michael@0 1725 if (transaction)
michael@0 1726 transaction->OnTransportStatus(aTransport, aStatus, aProgress);
michael@0 1727 break;
michael@0 1728 }
michael@0 1729
michael@0 1730 default:
michael@0 1731 // The other transport events are ignored here because there is no good
michael@0 1732 // way to map them to the right transaction in spdy. Instead, the events
michael@0 1733 // are generated again from the spdy code and passed directly to the
michael@0 1734 // correct transaction.
michael@0 1735
michael@0 1736 // NS_NET_STATUS_SENDING_TO:
michael@0 1737 // This is generated by the socket transport when (part) of
michael@0 1738 // a transaction is written out
michael@0 1739 //
michael@0 1740 // There is no good way to map it to the right transaction in spdy,
michael@0 1741 // so it is ignored here and generated separately when the SYN_STREAM
michael@0 1742 // is sent from SpdyStream31::TransmitFrame
michael@0 1743
michael@0 1744 // NS_NET_STATUS_WAITING_FOR:
michael@0 1745 // Created by nsHttpConnection when the request has been totally sent.
michael@0 1746 // There is no good way to map it to the right transaction in spdy,
michael@0 1747 // so it is ignored here and generated separately when the same
michael@0 1748 // condition is complete in SpdyStream31 when there is no more
michael@0 1749 // request body left to be transmitted.
michael@0 1750
michael@0 1751 // NS_NET_STATUS_RECEIVING_FROM
michael@0 1752 // Generated in spdysession whenever we read a data frame or a syn_reply
michael@0 1753 // that can be attributed to a particular stream/transaction
michael@0 1754
michael@0 1755 break;
michael@0 1756 }
michael@0 1757 }
michael@0 1758
michael@0 1759 // ReadSegments() is used to write data to the network. Generally, HTTP
michael@0 1760 // request data is pulled from the approriate transaction and
michael@0 1761 // converted to SPDY data. Sometimes control data like window-update are
michael@0 1762 // generated instead.
michael@0 1763
michael@0 1764 nsresult
michael@0 1765 SpdySession31::ReadSegments(nsAHttpSegmentReader *reader,
michael@0 1766 uint32_t count,
michael@0 1767 uint32_t *countRead)
michael@0 1768 {
michael@0 1769 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 1770
michael@0 1771 MOZ_ASSERT(!mSegmentReader || !reader || (mSegmentReader == reader),
michael@0 1772 "Inconsistent Write Function Callback");
michael@0 1773
michael@0 1774 if (reader)
michael@0 1775 mSegmentReader = reader;
michael@0 1776
michael@0 1777 nsresult rv;
michael@0 1778 *countRead = 0;
michael@0 1779
michael@0 1780 LOG3(("SpdySession31::ReadSegments %p", this));
michael@0 1781
michael@0 1782 SpdyStream31 *stream = static_cast<SpdyStream31 *>(mReadyForWrite.PopFront());
michael@0 1783 if (!stream) {
michael@0 1784 LOG3(("SpdySession31 %p could not identify a stream to write; suspending.",
michael@0 1785 this));
michael@0 1786 FlushOutputQueue();
michael@0 1787 SetWriteCallbacks();
michael@0 1788 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 1789 }
michael@0 1790
michael@0 1791 LOG3(("SpdySession31 %p will write from SpdyStream31 %p 0x%X "
michael@0 1792 "block-input=%d block-output=%d\n", this, stream, stream->StreamID(),
michael@0 1793 stream->RequestBlockedOnRead(), stream->BlockedOnRwin()));
michael@0 1794
michael@0 1795 rv = stream->ReadSegments(this, count, countRead);
michael@0 1796
michael@0 1797 // Not every permutation of stream->ReadSegents produces data (and therefore
michael@0 1798 // tries to flush the output queue) - SENDING_FIN_STREAM can be an example
michael@0 1799 // of that. But we might still have old data buffered that would be good
michael@0 1800 // to flush.
michael@0 1801 FlushOutputQueue();
michael@0 1802
michael@0 1803 // Allow new server reads - that might be data or control information
michael@0 1804 // (e.g. window updates or http replies) that are responses to these writes
michael@0 1805 ResumeRecv();
michael@0 1806
michael@0 1807 if (stream->RequestBlockedOnRead()) {
michael@0 1808
michael@0 1809 // We are blocked waiting for input - either more http headers or
michael@0 1810 // any request body data. When more data from the request stream
michael@0 1811 // becomes available the httptransaction will call conn->ResumeSend().
michael@0 1812
michael@0 1813 LOG3(("SpdySession31::ReadSegments %p dealing with block on read", this));
michael@0 1814
michael@0 1815 // call readsegments again if there are other streams ready
michael@0 1816 // to run in this session
michael@0 1817 if (GetWriteQueueSize())
michael@0 1818 rv = NS_OK;
michael@0 1819 else
michael@0 1820 rv = NS_BASE_STREAM_WOULD_BLOCK;
michael@0 1821 SetWriteCallbacks();
michael@0 1822 return rv;
michael@0 1823 }
michael@0 1824
michael@0 1825 if (NS_FAILED(rv)) {
michael@0 1826 LOG3(("SpdySession31::ReadSegments %p returning FAIL code %X",
michael@0 1827 this, rv));
michael@0 1828 if (rv != NS_BASE_STREAM_WOULD_BLOCK)
michael@0 1829 CleanupStream(stream, rv, RST_CANCEL);
michael@0 1830 return rv;
michael@0 1831 }
michael@0 1832
michael@0 1833 if (*countRead > 0) {
michael@0 1834 LOG3(("SpdySession31::ReadSegments %p stream=%p countread=%d",
michael@0 1835 this, stream, *countRead));
michael@0 1836 mReadyForWrite.Push(stream);
michael@0 1837 SetWriteCallbacks();
michael@0 1838 return rv;
michael@0 1839 }
michael@0 1840
michael@0 1841 if (stream->BlockedOnRwin()) {
michael@0 1842 LOG3(("SpdySession31 %p will stream %p 0x%X suspended for flow control\n",
michael@0 1843 this, stream, stream->StreamID()));
michael@0 1844 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 1845 }
michael@0 1846
michael@0 1847 LOG3(("SpdySession31::ReadSegments %p stream=%p stream send complete",
michael@0 1848 this, stream));
michael@0 1849
michael@0 1850 // call readsegments again if there are other streams ready
michael@0 1851 // to go in this session
michael@0 1852 SetWriteCallbacks();
michael@0 1853
michael@0 1854 return rv;
michael@0 1855 }
michael@0 1856
michael@0 1857 // WriteSegments() is used to read data off the socket. Generally this is
michael@0 1858 // just the SPDY frame header and from there the appropriate SPDYStream
michael@0 1859 // is identified from the Stream-ID. The http transaction associated with
michael@0 1860 // that read then pulls in the data directly, which it will feed to
michael@0 1861 // OnWriteSegment(). That function will gateway it into http and feed
michael@0 1862 // it to the appropriate transaction.
michael@0 1863
michael@0 1864 // we call writer->OnWriteSegment via NetworkRead() to get a spdy header..
michael@0 1865 // and decide if it is data or control.. if it is control, just deal with it.
michael@0 1866 // if it is data, identify the spdy stream
michael@0 1867 // call stream->WriteSegments which can call this::OnWriteSegment to get the
michael@0 1868 // data. It always gets full frames if they are part of the stream
michael@0 1869
michael@0 1870 nsresult
michael@0 1871 SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
michael@0 1872 uint32_t count,
michael@0 1873 uint32_t *countWritten)
michael@0 1874 {
michael@0 1875 typedef nsresult (*Control_FX) (SpdySession31 *self);
michael@0 1876 static const Control_FX sControlFunctions[] =
michael@0 1877 {
michael@0 1878 nullptr,
michael@0 1879 SpdySession31::HandleSynStream,
michael@0 1880 SpdySession31::HandleSynReply,
michael@0 1881 SpdySession31::HandleRstStream,
michael@0 1882 SpdySession31::HandleSettings,
michael@0 1883 SpdySession31::HandleNoop,
michael@0 1884 SpdySession31::HandlePing,
michael@0 1885 SpdySession31::HandleGoAway,
michael@0 1886 SpdySession31::HandleHeaders,
michael@0 1887 SpdySession31::HandleWindowUpdate,
michael@0 1888 SpdySession31::HandleCredential
michael@0 1889 };
michael@0 1890
michael@0 1891 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 1892
michael@0 1893 nsresult rv;
michael@0 1894 *countWritten = 0;
michael@0 1895
michael@0 1896 if (mClosed)
michael@0 1897 return NS_ERROR_FAILURE;
michael@0 1898
michael@0 1899 SetWriteCallbacks();
michael@0 1900
michael@0 1901 // If there are http transactions attached to a push stream with filled buffers
michael@0 1902 // trigger that data pump here. This only reads from buffers (not the network)
michael@0 1903 // so mDownstreamState doesn't matter.
michael@0 1904 SpdyStream31 *pushConnectedStream =
michael@0 1905 static_cast<SpdyStream31 *>(mReadyForRead.PopFront());
michael@0 1906 if (pushConnectedStream) {
michael@0 1907 LOG3(("SpdySession31::WriteSegments %p processing pushed stream 0x%X\n",
michael@0 1908 this, pushConnectedStream->StreamID()));
michael@0 1909 mSegmentWriter = writer;
michael@0 1910 rv = pushConnectedStream->WriteSegments(this, count, countWritten);
michael@0 1911 mSegmentWriter = nullptr;
michael@0 1912
michael@0 1913 // The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
michael@0 1914 // so we need this check to determine the truth.
michael@0 1915 if (NS_SUCCEEDED(rv) && !*countWritten &&
michael@0 1916 pushConnectedStream->PushSource() &&
michael@0 1917 pushConnectedStream->PushSource()->GetPushComplete()) {
michael@0 1918 rv = NS_BASE_STREAM_CLOSED;
michael@0 1919 }
michael@0 1920
michael@0 1921 if (rv == NS_BASE_STREAM_CLOSED) {
michael@0 1922 CleanupStream(pushConnectedStream, NS_OK, RST_CANCEL);
michael@0 1923 rv = NS_OK;
michael@0 1924 }
michael@0 1925
michael@0 1926 // if we return OK to nsHttpConnection it will use mSocketInCondition
michael@0 1927 // to determine whether to schedule more reads, incorrectly
michael@0 1928 // assuming that nsHttpConnection::OnSocketWrite() was called.
michael@0 1929 if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
michael@0 1930 rv = NS_BASE_STREAM_WOULD_BLOCK;
michael@0 1931 ResumeRecv();
michael@0 1932 }
michael@0 1933
michael@0 1934 return rv;
michael@0 1935 }
michael@0 1936
michael@0 1937 // We buffer all control frames and act on them in this layer.
michael@0 1938 // We buffer the first 8 bytes of data frames (the header) but
michael@0 1939 // the actual data is passed through unprocessed.
michael@0 1940
michael@0 1941 if (mDownstreamState == BUFFERING_FRAME_HEADER) {
michael@0 1942 // The first 8 bytes of every frame is header information that
michael@0 1943 // we are going to want to strip before passing to http. That is
michael@0 1944 // true of both control and data packets.
michael@0 1945
michael@0 1946 MOZ_ASSERT(mInputFrameBufferUsed < 8,
michael@0 1947 "Frame Buffer Used Too Large for State");
michael@0 1948
michael@0 1949 rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
michael@0 1950 8 - mInputFrameBufferUsed, countWritten);
michael@0 1951
michael@0 1952 if (NS_FAILED(rv)) {
michael@0 1953 LOG3(("SpdySession31 %p buffering frame header read failure %x\n",
michael@0 1954 this, rv));
michael@0 1955 // maybe just blocked reading from network
michael@0 1956 if (rv == NS_BASE_STREAM_WOULD_BLOCK)
michael@0 1957 rv = NS_OK;
michael@0 1958 return rv;
michael@0 1959 }
michael@0 1960
michael@0 1961 LogIO(this, nullptr, "Reading Frame Header",
michael@0 1962 mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
michael@0 1963
michael@0 1964 mInputFrameBufferUsed += *countWritten;
michael@0 1965
michael@0 1966 if (mInputFrameBufferUsed < 8)
michael@0 1967 {
michael@0 1968 LOG3(("SpdySession31::WriteSegments %p "
michael@0 1969 "BUFFERING FRAME HEADER incomplete size=%d",
michael@0 1970 this, mInputFrameBufferUsed));
michael@0 1971 return rv;
michael@0 1972 }
michael@0 1973
michael@0 1974 // For both control and data frames the second 32 bit word of the header
michael@0 1975 // is 8-flags, 24-length. (network byte order)
michael@0 1976 mInputFrameDataSize =
michael@0 1977 PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[1]);
michael@0 1978 mInputFrameDataSize &= 0x00ffffff;
michael@0 1979 mInputFrameDataRead = 0;
michael@0 1980
michael@0 1981 if (mInputFrameBuffer[0] & kFlag_Control) {
michael@0 1982 EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
michael@0 1983 mInputFrameBufferSize);
michael@0 1984 ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
michael@0 1985
michael@0 1986 // The first 32 bit word of the header is
michael@0 1987 // 1 ctrl - 15 version - 16 type
michael@0 1988 uint16_t version =
michael@0 1989 PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[0]);
michael@0 1990 version &= 0x7fff;
michael@0 1991
michael@0 1992 mFrameControlType =
michael@0 1993 PR_ntohs(reinterpret_cast<uint16_t *>(mInputFrameBuffer.get())[1]);
michael@0 1994
michael@0 1995 LOG3(("SpdySession31::WriteSegments %p - Control Frame Identified "
michael@0 1996 "type %d version %d data len %d",
michael@0 1997 this, mFrameControlType, version, mInputFrameDataSize));
michael@0 1998
michael@0 1999 if (mFrameControlType >= CONTROL_TYPE_LAST ||
michael@0 2000 mFrameControlType <= CONTROL_TYPE_FIRST)
michael@0 2001 return NS_ERROR_ILLEGAL_VALUE;
michael@0 2002
michael@0 2003 if (version != kVersion)
michael@0 2004 return NS_ERROR_ILLEGAL_VALUE;
michael@0 2005 }
michael@0 2006 else {
michael@0 2007 ChangeDownstreamState(PROCESSING_DATA_FRAME);
michael@0 2008
michael@0 2009 Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
michael@0 2010 mInputFrameDataSize >> 10);
michael@0 2011 mLastDataReadEpoch = mLastReadEpoch;
michael@0 2012
michael@0 2013 uint32_t streamID =
michael@0 2014 PR_ntohl(reinterpret_cast<uint32_t *>(mInputFrameBuffer.get())[0]);
michael@0 2015 rv = SetInputFrameDataStream(streamID);
michael@0 2016 if (NS_FAILED(rv)) {
michael@0 2017 LOG(("SpdySession31::WriteSegments %p lookup streamID 0x%X failed. "
michael@0 2018 "probably due to verification.\n", this, streamID));
michael@0 2019 return rv;
michael@0 2020 }
michael@0 2021 if (!mInputFrameDataStream) {
michael@0 2022 LOG3(("SpdySession31::WriteSegments %p lookup streamID 0x%X failed. "
michael@0 2023 "Next = 0x%X", this, streamID, mNextStreamID));
michael@0 2024 if (streamID >= mNextStreamID)
michael@0 2025 GenerateRstStream(RST_INVALID_STREAM, streamID);
michael@0 2026 ChangeDownstreamState(DISCARDING_DATA_FRAME);
michael@0 2027 }
michael@0 2028 else if (mInputFrameDataStream->RecvdFin()) {
michael@0 2029 LOG3(("SpdySession31::WriteSegments %p streamID 0x%X "
michael@0 2030 "Data arrived for already server closed stream.\n",
michael@0 2031 this, streamID));
michael@0 2032 GenerateRstStream(RST_STREAM_ALREADY_CLOSED, streamID);
michael@0 2033 ChangeDownstreamState(DISCARDING_DATA_FRAME);
michael@0 2034 }
michael@0 2035 else if (!mInputFrameDataStream->RecvdData()) {
michael@0 2036 LOG3(("SpdySession31 %p First Data Frame Flushes Headers stream 0x%X\n",
michael@0 2037 this, streamID));
michael@0 2038
michael@0 2039 mInputFrameDataStream->SetRecvdData(true);
michael@0 2040 rv = ResponseHeadersComplete();
michael@0 2041 if (rv == NS_ERROR_ILLEGAL_VALUE) {
michael@0 2042 LOG3(("SpdySession31 %p PROTOCOL_ERROR detected 0x%X\n",
michael@0 2043 this, streamID));
michael@0 2044 CleanupStream(mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
michael@0 2045 ChangeDownstreamState(DISCARDING_DATA_FRAME);
michael@0 2046 }
michael@0 2047 else {
michael@0 2048 mDataPending = true;
michael@0 2049 }
michael@0 2050 }
michael@0 2051
michael@0 2052 mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN);
michael@0 2053 LOG3(("Start Processing Data Frame. "
michael@0 2054 "Session=%p Stream ID 0x%X Stream Ptr %p Fin=%d Len=%d",
michael@0 2055 this, streamID, mInputFrameDataStream, mInputFrameDataLast,
michael@0 2056 mInputFrameDataSize));
michael@0 2057 UpdateLocalRwin(mInputFrameDataStream, mInputFrameDataSize);
michael@0 2058 }
michael@0 2059 }
michael@0 2060
michael@0 2061 if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
michael@0 2062 if (mDownstreamRstReason == RST_REFUSED_STREAM)
michael@0 2063 rv = NS_ERROR_NET_RESET; //we can retry this 100% safely
michael@0 2064 else if (mDownstreamRstReason == RST_CANCEL ||
michael@0 2065 mDownstreamRstReason == RST_PROTOCOL_ERROR ||
michael@0 2066 mDownstreamRstReason == RST_INTERNAL_ERROR ||
michael@0 2067 mDownstreamRstReason == RST_UNSUPPORTED_VERSION)
michael@0 2068 rv = NS_ERROR_NET_INTERRUPT;
michael@0 2069 else if (mDownstreamRstReason == RST_FRAME_TOO_LARGE)
michael@0 2070 rv = NS_ERROR_FILE_TOO_BIG;
michael@0 2071 else
michael@0 2072 rv = NS_ERROR_ILLEGAL_VALUE;
michael@0 2073
michael@0 2074 if (mDownstreamRstReason != RST_REFUSED_STREAM &&
michael@0 2075 mDownstreamRstReason != RST_CANCEL)
michael@0 2076 mShouldGoAway = true;
michael@0 2077
michael@0 2078 // mInputFrameDataStream is reset by ChangeDownstreamState
michael@0 2079 SpdyStream31 *stream = mInputFrameDataStream;
michael@0 2080 ResetDownstreamState();
michael@0 2081 LOG3(("SpdySession31::WriteSegments cleanup stream on recv of rst "
michael@0 2082 "session=%p stream=%p 0x%X\n", this, stream,
michael@0 2083 stream ? stream->StreamID() : 0));
michael@0 2084 CleanupStream(stream, rv, RST_CANCEL);
michael@0 2085 return NS_OK;
michael@0 2086 }
michael@0 2087
michael@0 2088 if (mDownstreamState == PROCESSING_DATA_FRAME ||
michael@0 2089 mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
michael@0 2090
michael@0 2091 // The cleanup stream should only be set while stream->WriteSegments is
michael@0 2092 // on the stack and then cleaned up in this code block afterwards.
michael@0 2093 MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
michael@0 2094 mNeedsCleanup = nullptr; /* just in case */
michael@0 2095
michael@0 2096 mSegmentWriter = writer;
michael@0 2097 rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
michael@0 2098 mSegmentWriter = nullptr;
michael@0 2099
michael@0 2100 mLastDataReadEpoch = mLastReadEpoch;
michael@0 2101
michael@0 2102 if (SoftStreamError(rv)) {
michael@0 2103 // This will happen when the transaction figures out it is EOF, generally
michael@0 2104 // due to a content-length match being made. Return OK from this function
michael@0 2105 // otherwise the whole session would be torn down.
michael@0 2106 SpdyStream31 *stream = mInputFrameDataStream;
michael@0 2107
michael@0 2108 // if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
michael@0 2109 // back to PROCESSING_DATA_FRAME where we came from
michael@0 2110 mDownstreamState = PROCESSING_DATA_FRAME;
michael@0 2111
michael@0 2112 if (mInputFrameDataRead == mInputFrameDataSize)
michael@0 2113 ResetDownstreamState();
michael@0 2114 LOG3(("SpdySession31::WriteSegments session=%p stream=%p 0x%X "
michael@0 2115 "needscleanup=%p. cleanup stream based on "
michael@0 2116 "stream->writeSegments returning code %X\n",
michael@0 2117 this, stream, stream ? stream->StreamID() : 0,
michael@0 2118 mNeedsCleanup, rv));
michael@0 2119 CleanupStream(stream, NS_OK, RST_CANCEL);
michael@0 2120 MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
michael@0 2121 mNeedsCleanup = nullptr; /* just in case */
michael@0 2122 return NS_OK;
michael@0 2123 }
michael@0 2124
michael@0 2125 if (mNeedsCleanup) {
michael@0 2126 LOG3(("SpdySession31::WriteSegments session=%p stream=%p 0x%X "
michael@0 2127 "cleanup stream based on mNeedsCleanup.\n",
michael@0 2128 this, mNeedsCleanup, mNeedsCleanup ? mNeedsCleanup->StreamID() : 0));
michael@0 2129 CleanupStream(mNeedsCleanup, NS_OK, RST_CANCEL);
michael@0 2130 mNeedsCleanup = nullptr;
michael@0 2131 }
michael@0 2132
michael@0 2133 if (NS_FAILED(rv)) {
michael@0 2134 LOG3(("SpdySession31 %p data frame read failure %x\n", this, rv));
michael@0 2135 // maybe just blocked reading from network
michael@0 2136 if (rv == NS_BASE_STREAM_WOULD_BLOCK)
michael@0 2137 rv = NS_OK;
michael@0 2138 }
michael@0 2139
michael@0 2140 return rv;
michael@0 2141 }
michael@0 2142
michael@0 2143 if (mDownstreamState == DISCARDING_DATA_FRAME) {
michael@0 2144 char trash[4096];
michael@0 2145 uint32_t count = std::min(4096U, mInputFrameDataSize - mInputFrameDataRead);
michael@0 2146
michael@0 2147 if (!count) {
michael@0 2148 ResetDownstreamState();
michael@0 2149 ResumeRecv();
michael@0 2150 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 2151 }
michael@0 2152
michael@0 2153 rv = NetworkRead(writer, trash, count, countWritten);
michael@0 2154
michael@0 2155 if (NS_FAILED(rv)) {
michael@0 2156 LOG3(("SpdySession31 %p discard frame read failure %x\n", this, rv));
michael@0 2157 // maybe just blocked reading from network
michael@0 2158 if (rv == NS_BASE_STREAM_WOULD_BLOCK)
michael@0 2159 rv = NS_OK;
michael@0 2160 return rv;
michael@0 2161 }
michael@0 2162
michael@0 2163 LogIO(this, nullptr, "Discarding Frame", trash, *countWritten);
michael@0 2164
michael@0 2165 mInputFrameDataRead += *countWritten;
michael@0 2166
michael@0 2167 if (mInputFrameDataRead == mInputFrameDataSize)
michael@0 2168 ResetDownstreamState();
michael@0 2169 return rv;
michael@0 2170 }
michael@0 2171
michael@0 2172 MOZ_ASSERT(mDownstreamState == BUFFERING_CONTROL_FRAME);
michael@0 2173 if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
michael@0 2174 // this cannot happen
michael@0 2175 return NS_ERROR_UNEXPECTED;
michael@0 2176 }
michael@0 2177
michael@0 2178 MOZ_ASSERT(mInputFrameBufferUsed == 8,
michael@0 2179 "Frame Buffer Header Not Present");
michael@0 2180
michael@0 2181 rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
michael@0 2182 mInputFrameDataSize - mInputFrameDataRead, countWritten);
michael@0 2183
michael@0 2184 if (NS_FAILED(rv)) {
michael@0 2185 LOG3(("SpdySession31 %p buffering control frame read failure %x\n",
michael@0 2186 this, rv));
michael@0 2187 // maybe just blocked reading from network
michael@0 2188 if (rv == NS_BASE_STREAM_WOULD_BLOCK)
michael@0 2189 rv = NS_OK;
michael@0 2190 return rv;
michael@0 2191 }
michael@0 2192
michael@0 2193 LogIO(this, nullptr, "Reading Control Frame",
michael@0 2194 mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
michael@0 2195
michael@0 2196 mInputFrameDataRead += *countWritten;
michael@0 2197
michael@0 2198 if (mInputFrameDataRead != mInputFrameDataSize)
michael@0 2199 return NS_OK;
michael@0 2200
michael@0 2201 // This check is actually redundant, the control type was previously
michael@0 2202 // checked to make sure it was in range, but we will check it again
michael@0 2203 // at time of use to make sure a regression doesn't creep in.
michael@0 2204 if (mFrameControlType >= CONTROL_TYPE_LAST ||
michael@0 2205 mFrameControlType <= CONTROL_TYPE_FIRST)
michael@0 2206 {
michael@0 2207 MOZ_ASSERT(false, "control type out of range");
michael@0 2208 return NS_ERROR_ILLEGAL_VALUE;
michael@0 2209 }
michael@0 2210 rv = sControlFunctions[mFrameControlType](this);
michael@0 2211
michael@0 2212 MOZ_ASSERT(NS_FAILED(rv) ||
michael@0 2213 mDownstreamState != BUFFERING_CONTROL_FRAME,
michael@0 2214 "Control Handler returned OK but did not change state");
michael@0 2215
michael@0 2216 if (mShouldGoAway && !mStreamTransactionHash.Count())
michael@0 2217 Close(NS_OK);
michael@0 2218 return rv;
michael@0 2219 }
michael@0 2220
michael@0 2221 void
michael@0 2222 SpdySession31::UpdateLocalStreamWindow(SpdyStream31 *stream,
michael@0 2223 uint32_t bytes)
michael@0 2224 {
michael@0 2225 if (!stream) // this is ok - it means there was a data frame for a rst stream
michael@0 2226 return;
michael@0 2227
michael@0 2228 stream->DecrementLocalWindow(bytes);
michael@0 2229
michael@0 2230 // If this data packet was not for a valid or live stream then there
michael@0 2231 // is no reason to mess with the flow control
michael@0 2232 if (stream->RecvdFin())
michael@0 2233 return;
michael@0 2234
michael@0 2235 // Don't necessarily ack every data packet. Only do it
michael@0 2236 // after a significant amount of data.
michael@0 2237 uint64_t unacked = stream->LocalUnAcked();
michael@0 2238 int64_t localWindow = stream->LocalWindow();
michael@0 2239
michael@0 2240 LOG3(("SpdySession31::UpdateLocalStreamWindow this=%p id=0x%X newbytes=%u "
michael@0 2241 "unacked=%llu localWindow=%lld\n",
michael@0 2242 this, stream->StreamID(), bytes, unacked, localWindow));
michael@0 2243
michael@0 2244 if (!unacked)
michael@0 2245 return;
michael@0 2246
michael@0 2247 if ((unacked < kMinimumToAck) && (localWindow > kEmergencyWindowThreshold))
michael@0 2248 return;
michael@0 2249
michael@0 2250 if (!stream->HasSink()) {
michael@0 2251 LOG3(("SpdySession31::UpdateLocalStreamWindow %p 0x%X Pushed Stream Has No Sink\n",
michael@0 2252 this, stream->StreamID()));
michael@0 2253 return;
michael@0 2254 }
michael@0 2255
michael@0 2256 // Generate window updates directly out of spdysession instead of the stream
michael@0 2257 // in order to avoid queue delays in getting the 'ACK' out.
michael@0 2258 uint32_t toack = (unacked <= 0x7fffffffU) ? unacked : 0x7fffffffU;
michael@0 2259
michael@0 2260 LOG3(("SpdySession31::UpdateLocalStreamWindow Ack this=%p id=0x%X acksize=%d\n",
michael@0 2261 this, stream->StreamID(), toack));
michael@0 2262 stream->IncrementLocalWindow(toack);
michael@0 2263
michael@0 2264 // room for this packet needs to be ensured before calling this function
michael@0 2265 static const uint32_t dataLen = 8;
michael@0 2266 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 2267 mOutputQueueUsed += 8 + dataLen;
michael@0 2268 MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
michael@0 2269
michael@0 2270 memset(packet, 0, 8 + dataLen);
michael@0 2271 packet[0] = kFlag_Control;
michael@0 2272 packet[1] = kVersion;
michael@0 2273 packet[3] = CONTROL_TYPE_WINDOW_UPDATE;
michael@0 2274 packet[7] = dataLen;
michael@0 2275
michael@0 2276 uint32_t id = PR_htonl(stream->StreamID());
michael@0 2277 memcpy(packet + 8, &id, 4);
michael@0 2278 toack = PR_htonl(toack);
michael@0 2279 memcpy(packet + 12, &toack, 4);
michael@0 2280
michael@0 2281 LogIO(this, stream, "Stream Window Update", packet, 8 + dataLen);
michael@0 2282 // dont flush here, this write can commonly be coalesced with a
michael@0 2283 // session window update to immediately follow.
michael@0 2284 }
michael@0 2285
michael@0 2286 void
michael@0 2287 SpdySession31::UpdateLocalSessionWindow(uint32_t bytes)
michael@0 2288 {
michael@0 2289 if (!bytes)
michael@0 2290 return;
michael@0 2291
michael@0 2292 mLocalSessionWindow -= bytes;
michael@0 2293
michael@0 2294 LOG3(("SpdySession31::UpdateLocalSessionWindow this=%p newbytes=%u "
michael@0 2295 "localWindow=%lld\n", this, bytes, mLocalSessionWindow));
michael@0 2296
michael@0 2297 // Don't necessarily ack every data packet. Only do it
michael@0 2298 // after a significant amount of data.
michael@0 2299 if ((mLocalSessionWindow > (ASpdySession::kInitialRwin - kMinimumToAck)) &&
michael@0 2300 (mLocalSessionWindow > kEmergencyWindowThreshold))
michael@0 2301 return;
michael@0 2302
michael@0 2303 // Only send max 31 bits of window updates at a time.
michael@0 2304 uint64_t toack64 = ASpdySession::kInitialRwin - mLocalSessionWindow;
michael@0 2305 uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
michael@0 2306
michael@0 2307 LOG3(("SpdySession31::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
michael@0 2308 this, toack));
michael@0 2309 mLocalSessionWindow += toack;
michael@0 2310
michael@0 2311 // room for this packet needs to be ensured before calling this function
michael@0 2312 static const uint32_t dataLen = 8;
michael@0 2313 char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
michael@0 2314 mOutputQueueUsed += 8 + dataLen;
michael@0 2315 MOZ_ASSERT(mOutputQueueUsed <= mOutputQueueSize);
michael@0 2316
michael@0 2317 memset(packet, 0, 8 + dataLen);
michael@0 2318 packet[0] = kFlag_Control;
michael@0 2319 packet[1] = kVersion;
michael@0 2320 packet[3] = CONTROL_TYPE_WINDOW_UPDATE;
michael@0 2321 packet[7] = dataLen;
michael@0 2322
michael@0 2323 // packet 8-11 is ID and left at 0 for session ID
michael@0 2324 toack = PR_htonl(toack);
michael@0 2325 memcpy(packet + 12, &toack, 4);
michael@0 2326
michael@0 2327 LogIO(this, nullptr, "Session Window Update", packet, 8 + dataLen);
michael@0 2328 // dont flush here, this write can commonly be coalesced with others
michael@0 2329 }
michael@0 2330
michael@0 2331 void
michael@0 2332 SpdySession31::UpdateLocalRwin(SpdyStream31 *stream,
michael@0 2333 uint32_t bytes)
michael@0 2334 {
michael@0 2335 // make sure there is room for 2 window updates even though
michael@0 2336 // we may not generate any.
michael@0 2337 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + (16 *2),
michael@0 2338 mOutputQueueUsed, mOutputQueueSize);
michael@0 2339
michael@0 2340 UpdateLocalStreamWindow(stream, bytes);
michael@0 2341 UpdateLocalSessionWindow(bytes);
michael@0 2342 FlushOutputQueue();
michael@0 2343 }
michael@0 2344
michael@0 2345 void
michael@0 2346 SpdySession31::Close(nsresult aReason)
michael@0 2347 {
michael@0 2348 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2349
michael@0 2350 if (mClosed)
michael@0 2351 return;
michael@0 2352
michael@0 2353 LOG3(("SpdySession31::Close %p %X", this, aReason));
michael@0 2354
michael@0 2355 mClosed = true;
michael@0 2356
michael@0 2357 mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
michael@0 2358 mStreamIDHash.Clear();
michael@0 2359 mStreamTransactionHash.Clear();
michael@0 2360
michael@0 2361 uint32_t goAwayReason;
michael@0 2362 if (NS_SUCCEEDED(aReason)) {
michael@0 2363 goAwayReason = OK;
michael@0 2364 } else if (aReason == NS_ERROR_ILLEGAL_VALUE) {
michael@0 2365 goAwayReason = PROTOCOL_ERROR;
michael@0 2366 } else {
michael@0 2367 goAwayReason = INTERNAL_ERROR;
michael@0 2368 }
michael@0 2369 GenerateGoAway(goAwayReason);
michael@0 2370 mConnection = nullptr;
michael@0 2371 mSegmentReader = nullptr;
michael@0 2372 mSegmentWriter = nullptr;
michael@0 2373 }
michael@0 2374
michael@0 2375 void
michael@0 2376 SpdySession31::CloseTransaction(nsAHttpTransaction *aTransaction,
michael@0 2377 nsresult aResult)
michael@0 2378 {
michael@0 2379 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2380 LOG3(("SpdySession31::CloseTransaction %p %p %x", this, aTransaction, aResult));
michael@0 2381
michael@0 2382 // Generally this arrives as a cancel event from the connection manager.
michael@0 2383
michael@0 2384 // need to find the stream and call CleanupStream() on it.
michael@0 2385 SpdyStream31 *stream = mStreamTransactionHash.Get(aTransaction);
michael@0 2386 if (!stream) {
michael@0 2387 LOG3(("SpdySession31::CloseTransaction %p %p %x - not found.",
michael@0 2388 this, aTransaction, aResult));
michael@0 2389 return;
michael@0 2390 }
michael@0 2391 LOG3(("SpdySession31::CloseTranscation probably a cancel. "
michael@0 2392 "this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
michael@0 2393 this, aTransaction, aResult, stream->StreamID(), stream));
michael@0 2394 CleanupStream(stream, aResult, RST_CANCEL);
michael@0 2395 ResumeRecv();
michael@0 2396 }
michael@0 2397
michael@0 2398 //-----------------------------------------------------------------------------
michael@0 2399 // nsAHttpSegmentReader
michael@0 2400 //-----------------------------------------------------------------------------
michael@0 2401
michael@0 2402 nsresult
michael@0 2403 SpdySession31::OnReadSegment(const char *buf,
michael@0 2404 uint32_t count,
michael@0 2405 uint32_t *countRead)
michael@0 2406 {
michael@0 2407 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2408
michael@0 2409 nsresult rv;
michael@0 2410
michael@0 2411 // If we can release old queued data then we can try and write the new
michael@0 2412 // data directly to the network without using the output queue at all
michael@0 2413 if (mOutputQueueUsed)
michael@0 2414 FlushOutputQueue();
michael@0 2415
michael@0 2416 if (!mOutputQueueUsed && mSegmentReader) {
michael@0 2417 // try and write directly without output queue
michael@0 2418 rv = mSegmentReader->OnReadSegment(buf, count, countRead);
michael@0 2419
michael@0 2420 if (rv == NS_BASE_STREAM_WOULD_BLOCK)
michael@0 2421 *countRead = 0;
michael@0 2422 else if (NS_FAILED(rv))
michael@0 2423 return rv;
michael@0 2424
michael@0 2425 if (*countRead < count) {
michael@0 2426 uint32_t required = count - *countRead;
michael@0 2427 // assuming a commitment() happened, this ensurebuffer is a nop
michael@0 2428 // but just in case the queuesize is too small for the required data
michael@0 2429 // call ensurebuffer().
michael@0 2430 EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
michael@0 2431 memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
michael@0 2432 mOutputQueueUsed = required;
michael@0 2433 }
michael@0 2434
michael@0 2435 *countRead = count;
michael@0 2436 return NS_OK;
michael@0 2437 }
michael@0 2438
michael@0 2439 // At this point we are going to buffer the new data in the output
michael@0 2440 // queue if it fits. By coalescing multiple small submissions into one larger
michael@0 2441 // buffer we can get larger writes out to the network later on.
michael@0 2442
michael@0 2443 // This routine should not be allowed to fill up the output queue
michael@0 2444 // all on its own - at least kQueueReserved bytes are always left
michael@0 2445 // for other routines to use - but this is an all-or-nothing function,
michael@0 2446 // so if it will not all fit just return WOULD_BLOCK
michael@0 2447
michael@0 2448 if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
michael@0 2449 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 2450
michael@0 2451 memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
michael@0 2452 mOutputQueueUsed += count;
michael@0 2453 *countRead = count;
michael@0 2454
michael@0 2455 FlushOutputQueue();
michael@0 2456
michael@0 2457 return NS_OK;
michael@0 2458 }
michael@0 2459
michael@0 2460 nsresult
michael@0 2461 SpdySession31::CommitToSegmentSize(uint32_t count, bool forceCommitment)
michael@0 2462 {
michael@0 2463 if (mOutputQueueUsed)
michael@0 2464 FlushOutputQueue();
michael@0 2465
michael@0 2466 // would there be enough room to buffer this if needed?
michael@0 2467 if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
michael@0 2468 return NS_OK;
michael@0 2469
michael@0 2470 // if we are using part of our buffers already, try again later unless
michael@0 2471 // forceCommitment is set.
michael@0 2472 if (mOutputQueueUsed && !forceCommitment)
michael@0 2473 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 2474
michael@0 2475 if (mOutputQueueUsed) {
michael@0 2476 // normally we avoid the memmove of RealignOutputQueue, but we'll try
michael@0 2477 // it if forceCommitment is set before growing the buffer.
michael@0 2478 RealignOutputQueue();
michael@0 2479
michael@0 2480 // is there enough room now?
michael@0 2481 if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
michael@0 2482 return NS_OK;
michael@0 2483 }
michael@0 2484
michael@0 2485 // resize the buffers as needed
michael@0 2486 EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + count + kQueueReserved,
michael@0 2487 mOutputQueueUsed, mOutputQueueSize);
michael@0 2488
michael@0 2489 MOZ_ASSERT((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved),
michael@0 2490 "buffer not as large as expected");
michael@0 2491
michael@0 2492 return NS_OK;
michael@0 2493 }
michael@0 2494
michael@0 2495 //-----------------------------------------------------------------------------
michael@0 2496 // nsAHttpSegmentWriter
michael@0 2497 //-----------------------------------------------------------------------------
michael@0 2498
michael@0 2499 nsresult
michael@0 2500 SpdySession31::OnWriteSegment(char *buf,
michael@0 2501 uint32_t count,
michael@0 2502 uint32_t *countWritten)
michael@0 2503 {
michael@0 2504 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2505 nsresult rv;
michael@0 2506
michael@0 2507 if (!mSegmentWriter) {
michael@0 2508 // the only way this could happen would be if Close() were called on the
michael@0 2509 // stack with WriteSegments()
michael@0 2510 return NS_ERROR_FAILURE;
michael@0 2511 }
michael@0 2512
michael@0 2513 if (mDownstreamState == PROCESSING_DATA_FRAME) {
michael@0 2514
michael@0 2515 if (mInputFrameDataLast &&
michael@0 2516 mInputFrameDataRead == mInputFrameDataSize) {
michael@0 2517 *countWritten = 0;
michael@0 2518 SetNeedsCleanup();
michael@0 2519 return NS_BASE_STREAM_CLOSED;
michael@0 2520 }
michael@0 2521
michael@0 2522 count = std::min(count, mInputFrameDataSize - mInputFrameDataRead);
michael@0 2523 rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
michael@0 2524 if (NS_FAILED(rv))
michael@0 2525 return rv;
michael@0 2526
michael@0 2527 LogIO(this, mInputFrameDataStream, "Reading Data Frame",
michael@0 2528 buf, *countWritten);
michael@0 2529
michael@0 2530 mInputFrameDataRead += *countWritten;
michael@0 2531
michael@0 2532 mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
michael@0 2533 if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast)
michael@0 2534 ResetDownstreamState();
michael@0 2535
michael@0 2536 return rv;
michael@0 2537 }
michael@0 2538
michael@0 2539 if (mDownstreamState == PROCESSING_COMPLETE_HEADERS) {
michael@0 2540
michael@0 2541 if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
michael@0 2542 mInputFrameDataLast) {
michael@0 2543 *countWritten = 0;
michael@0 2544 SetNeedsCleanup();
michael@0 2545 return NS_BASE_STREAM_CLOSED;
michael@0 2546 }
michael@0 2547
michael@0 2548 count = std::min(count,
michael@0 2549 mFlatHTTPResponseHeaders.Length() -
michael@0 2550 mFlatHTTPResponseHeadersOut);
michael@0 2551 memcpy(buf,
michael@0 2552 mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
michael@0 2553 count);
michael@0 2554 mFlatHTTPResponseHeadersOut += count;
michael@0 2555 *countWritten = count;
michael@0 2556
michael@0 2557 if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut) {
michael@0 2558 if (mDataPending) {
michael@0 2559 // Now ready to process data frames - pop PROCESING_DATA_FRAME back onto
michael@0 2560 // the stack because receipt of that first data frame triggered the
michael@0 2561 // response header processing
michael@0 2562 mDataPending = false;
michael@0 2563 ChangeDownstreamState(PROCESSING_DATA_FRAME);
michael@0 2564 }
michael@0 2565 else if (!mInputFrameDataLast) {
michael@0 2566 // If more frames are expected in this stream, then reset the state so they can be
michael@0 2567 // handled. Otherwise (e.g. a 0 length response with the fin on the SYN_REPLY)
michael@0 2568 // stay in PROCESSING_COMPLETE_HEADERS state so the SetNeedsCleanup() code above can
michael@0 2569 // cleanup the stream.
michael@0 2570 ResetDownstreamState();
michael@0 2571 }
michael@0 2572 }
michael@0 2573
michael@0 2574 return NS_OK;
michael@0 2575 }
michael@0 2576
michael@0 2577 return NS_ERROR_UNEXPECTED;
michael@0 2578 }
michael@0 2579
michael@0 2580 void
michael@0 2581 SpdySession31::SetNeedsCleanup()
michael@0 2582 {
michael@0 2583 LOG3(("SpdySession31::SetNeedsCleanup %p - recorded downstream fin of "
michael@0 2584 "stream %p 0x%X", this, mInputFrameDataStream,
michael@0 2585 mInputFrameDataStream->StreamID()));
michael@0 2586
michael@0 2587 // This will result in Close() being called
michael@0 2588 MOZ_ASSERT(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
michael@0 2589 mNeedsCleanup = mInputFrameDataStream;
michael@0 2590 ResetDownstreamState();
michael@0 2591 }
michael@0 2592
michael@0 2593 void
michael@0 2594 SpdySession31::ConnectPushedStream(SpdyStream31 *stream)
michael@0 2595 {
michael@0 2596 mReadyForRead.Push(stream);
michael@0 2597 ForceRecv();
michael@0 2598 }
michael@0 2599
michael@0 2600 nsresult
michael@0 2601 SpdySession31::BufferOutput(const char *buf,
michael@0 2602 uint32_t count,
michael@0 2603 uint32_t *countRead)
michael@0 2604 {
michael@0 2605 nsAHttpSegmentReader *old = mSegmentReader;
michael@0 2606 mSegmentReader = nullptr;
michael@0 2607 nsresult rv = OnReadSegment(buf, count, countRead);
michael@0 2608 mSegmentReader = old;
michael@0 2609 return rv;
michael@0 2610 }
michael@0 2611
michael@0 2612 //-----------------------------------------------------------------------------
michael@0 2613 // Modified methods of nsAHttpConnection
michael@0 2614 //-----------------------------------------------------------------------------
michael@0 2615
michael@0 2616 void
michael@0 2617 SpdySession31::TransactionHasDataToWrite(nsAHttpTransaction *caller)
michael@0 2618 {
michael@0 2619 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2620 LOG3(("SpdySession31::TransactionHasDataToWrite %p trans=%p", this, caller));
michael@0 2621
michael@0 2622 // a trapped signal from the http transaction to the connection that
michael@0 2623 // it is no longer blocked on read.
michael@0 2624
michael@0 2625 SpdyStream31 *stream = mStreamTransactionHash.Get(caller);
michael@0 2626 if (!stream || !VerifyStream(stream)) {
michael@0 2627 LOG3(("SpdySession31::TransactionHasDataToWrite %p caller %p not found",
michael@0 2628 this, caller));
michael@0 2629 return;
michael@0 2630 }
michael@0 2631
michael@0 2632 LOG3(("SpdySession31::TransactionHasDataToWrite %p ID is 0x%X\n",
michael@0 2633 this, stream->StreamID()));
michael@0 2634
michael@0 2635 mReadyForWrite.Push(stream);
michael@0 2636 }
michael@0 2637
michael@0 2638 void
michael@0 2639 SpdySession31::TransactionHasDataToWrite(SpdyStream31 *stream)
michael@0 2640 {
michael@0 2641 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2642 LOG3(("SpdySession31::TransactionHasDataToWrite %p stream=%p ID=%x",
michael@0 2643 this, stream, stream->StreamID()));
michael@0 2644
michael@0 2645 mReadyForWrite.Push(stream);
michael@0 2646 SetWriteCallbacks();
michael@0 2647 }
michael@0 2648
michael@0 2649 bool
michael@0 2650 SpdySession31::IsPersistent()
michael@0 2651 {
michael@0 2652 return true;
michael@0 2653 }
michael@0 2654
michael@0 2655 nsresult
michael@0 2656 SpdySession31::TakeTransport(nsISocketTransport **,
michael@0 2657 nsIAsyncInputStream **,
michael@0 2658 nsIAsyncOutputStream **)
michael@0 2659 {
michael@0 2660 MOZ_ASSERT(false, "TakeTransport of SpdySession31");
michael@0 2661 return NS_ERROR_UNEXPECTED;
michael@0 2662 }
michael@0 2663
michael@0 2664 nsHttpConnection *
michael@0 2665 SpdySession31::TakeHttpConnection()
michael@0 2666 {
michael@0 2667 MOZ_ASSERT(false, "TakeHttpConnection of SpdySession31");
michael@0 2668 return nullptr;
michael@0 2669 }
michael@0 2670
michael@0 2671 uint32_t
michael@0 2672 SpdySession31::CancelPipeline(nsresult reason)
michael@0 2673 {
michael@0 2674 // we don't pipeline inside spdy, so this isn't an issue
michael@0 2675 return 0;
michael@0 2676 }
michael@0 2677
michael@0 2678 nsAHttpTransaction::Classifier
michael@0 2679 SpdySession31::Classification()
michael@0 2680 {
michael@0 2681 if (!mConnection)
michael@0 2682 return nsAHttpTransaction::CLASS_GENERAL;
michael@0 2683 return mConnection->Classification();
michael@0 2684 }
michael@0 2685
michael@0 2686 //-----------------------------------------------------------------------------
michael@0 2687 // unused methods of nsAHttpTransaction
michael@0 2688 // We can be sure of this because SpdySession31 is only constructed in
michael@0 2689 // nsHttpConnection and is never passed out of that object
michael@0 2690 //-----------------------------------------------------------------------------
michael@0 2691
michael@0 2692 void
michael@0 2693 SpdySession31::SetConnection(nsAHttpConnection *)
michael@0 2694 {
michael@0 2695 // This is unexpected
michael@0 2696 MOZ_ASSERT(false, "SpdySession31::SetConnection()");
michael@0 2697 }
michael@0 2698
michael@0 2699 void
michael@0 2700 SpdySession31::GetSecurityCallbacks(nsIInterfaceRequestor **)
michael@0 2701 {
michael@0 2702 // This is unexpected
michael@0 2703 MOZ_ASSERT(false, "SpdySession31::GetSecurityCallbacks()");
michael@0 2704 }
michael@0 2705
michael@0 2706 void
michael@0 2707 SpdySession31::SetProxyConnectFailed()
michael@0 2708 {
michael@0 2709 MOZ_ASSERT(false, "SpdySession31::SetProxyConnectFailed()");
michael@0 2710 }
michael@0 2711
michael@0 2712 bool
michael@0 2713 SpdySession31::IsDone()
michael@0 2714 {
michael@0 2715 return !mStreamTransactionHash.Count();
michael@0 2716 }
michael@0 2717
michael@0 2718 nsresult
michael@0 2719 SpdySession31::Status()
michael@0 2720 {
michael@0 2721 MOZ_ASSERT(false, "SpdySession31::Status()");
michael@0 2722 return NS_ERROR_UNEXPECTED;
michael@0 2723 }
michael@0 2724
michael@0 2725 uint32_t
michael@0 2726 SpdySession31::Caps()
michael@0 2727 {
michael@0 2728 MOZ_ASSERT(false, "SpdySession31::Caps()");
michael@0 2729 return 0;
michael@0 2730 }
michael@0 2731
michael@0 2732 void
michael@0 2733 SpdySession31::SetDNSWasRefreshed()
michael@0 2734 {
michael@0 2735 }
michael@0 2736
michael@0 2737 uint64_t
michael@0 2738 SpdySession31::Available()
michael@0 2739 {
michael@0 2740 MOZ_ASSERT(false, "SpdySession31::Available()");
michael@0 2741 return 0;
michael@0 2742 }
michael@0 2743
michael@0 2744 nsHttpRequestHead *
michael@0 2745 SpdySession31::RequestHead()
michael@0 2746 {
michael@0 2747 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2748 MOZ_ASSERT(false,
michael@0 2749 "SpdySession31::RequestHead() "
michael@0 2750 "should not be called after SPDY is setup");
michael@0 2751 return nullptr;
michael@0 2752 }
michael@0 2753
michael@0 2754 uint32_t
michael@0 2755 SpdySession31::Http1xTransactionCount()
michael@0 2756 {
michael@0 2757 return 0;
michael@0 2758 }
michael@0 2759
michael@0 2760 // used as an enumerator by TakeSubTransactions()
michael@0 2761 static PLDHashOperator
michael@0 2762 TakeStream(nsAHttpTransaction *key,
michael@0 2763 nsAutoPtr<SpdyStream31> &stream,
michael@0 2764 void *closure)
michael@0 2765 {
michael@0 2766 nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
michael@0 2767 static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
michael@0 2768
michael@0 2769 list->AppendElement(key);
michael@0 2770
michael@0 2771 // removing the stream from the hash will delete the stream
michael@0 2772 // and drop the transaction reference the hash held
michael@0 2773 return PL_DHASH_REMOVE;
michael@0 2774 }
michael@0 2775
michael@0 2776 nsresult
michael@0 2777 SpdySession31::TakeSubTransactions(
michael@0 2778 nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
michael@0 2779 {
michael@0 2780 // Generally this cannot be done with spdy as transactions are
michael@0 2781 // started right away.
michael@0 2782
michael@0 2783 LOG3(("SpdySession31::TakeSubTransactions %p\n", this));
michael@0 2784
michael@0 2785 if (mConcurrentHighWater > 0)
michael@0 2786 return NS_ERROR_ALREADY_OPENED;
michael@0 2787
michael@0 2788 LOG3((" taking %d\n", mStreamTransactionHash.Count()));
michael@0 2789
michael@0 2790 mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
michael@0 2791 return NS_OK;
michael@0 2792 }
michael@0 2793
michael@0 2794 nsresult
michael@0 2795 SpdySession31::AddTransaction(nsAHttpTransaction *)
michael@0 2796 {
michael@0 2797 // This API is meant for pipelining, SpdySession31's should be
michael@0 2798 // extended with AddStream()
michael@0 2799
michael@0 2800 MOZ_ASSERT(false,
michael@0 2801 "SpdySession31::AddTransaction() should not be called");
michael@0 2802
michael@0 2803 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2804 }
michael@0 2805
michael@0 2806 uint32_t
michael@0 2807 SpdySession31::PipelineDepth()
michael@0 2808 {
michael@0 2809 return IsDone() ? 0 : 1;
michael@0 2810 }
michael@0 2811
michael@0 2812 nsresult
michael@0 2813 SpdySession31::SetPipelinePosition(int32_t position)
michael@0 2814 {
michael@0 2815 // This API is meant for pipelining, SpdySession31's should be
michael@0 2816 // extended with AddStream()
michael@0 2817
michael@0 2818 MOZ_ASSERT(false,
michael@0 2819 "SpdySession31::SetPipelinePosition() should not be called");
michael@0 2820
michael@0 2821 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 2822 }
michael@0 2823
michael@0 2824 int32_t
michael@0 2825 SpdySession31::PipelinePosition()
michael@0 2826 {
michael@0 2827 return 0;
michael@0 2828 }
michael@0 2829
michael@0 2830 //-----------------------------------------------------------------------------
michael@0 2831 // Pass through methods of nsAHttpConnection
michael@0 2832 //-----------------------------------------------------------------------------
michael@0 2833
michael@0 2834 nsAHttpConnection *
michael@0 2835 SpdySession31::Connection()
michael@0 2836 {
michael@0 2837 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 2838 return mConnection;
michael@0 2839 }
michael@0 2840
michael@0 2841 nsresult
michael@0 2842 SpdySession31::OnHeadersAvailable(nsAHttpTransaction *transaction,
michael@0 2843 nsHttpRequestHead *requestHead,
michael@0 2844 nsHttpResponseHead *responseHead,
michael@0 2845 bool *reset)
michael@0 2846 {
michael@0 2847 return mConnection->OnHeadersAvailable(transaction,
michael@0 2848 requestHead,
michael@0 2849 responseHead,
michael@0 2850 reset);
michael@0 2851 }
michael@0 2852
michael@0 2853 bool
michael@0 2854 SpdySession31::IsReused()
michael@0 2855 {
michael@0 2856 return mConnection->IsReused();
michael@0 2857 }
michael@0 2858
michael@0 2859 nsresult
michael@0 2860 SpdySession31::PushBack(const char *buf, uint32_t len)
michael@0 2861 {
michael@0 2862 return mConnection->PushBack(buf, len);
michael@0 2863 }
michael@0 2864
michael@0 2865 } // namespace mozilla::net
michael@0 2866 } // namespace mozilla

mercurial