Thu, 15 Jan 2015 21:03:48 +0100
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, ¬Used); |
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 |