netwerk/protocol/http/SpdyStream3.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set sw=2 ts=8 et tw=80 : */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // HttpLog.h should generally be included first
michael@0 8 #include "HttpLog.h"
michael@0 9
michael@0 10 // Log on level :5, instead of default :4.
michael@0 11 #undef LOG
michael@0 12 #define LOG(args) LOG5(args)
michael@0 13 #undef LOG_ENABLED
michael@0 14 #define LOG_ENABLED() LOG5_ENABLED()
michael@0 15
michael@0 16 #include "mozilla/Endian.h"
michael@0 17 #include "mozilla/Telemetry.h"
michael@0 18 #include "nsHttp.h"
michael@0 19 #include "nsHttpHandler.h"
michael@0 20 #include "nsHttpRequestHead.h"
michael@0 21 #include "nsISocketTransport.h"
michael@0 22 #include "nsISupportsPriority.h"
michael@0 23 #include "prnetdb.h"
michael@0 24 #include "SpdyPush3.h"
michael@0 25 #include "SpdySession3.h"
michael@0 26 #include "SpdyStream3.h"
michael@0 27 #include "PSpdyPush.h"
michael@0 28 #include "SpdyZlibReporter.h"
michael@0 29
michael@0 30 #include <algorithm>
michael@0 31
michael@0 32 #ifdef DEBUG
michael@0 33 // defined by the socket transport service while active
michael@0 34 extern PRThread *gSocketThread;
michael@0 35 #endif
michael@0 36
michael@0 37 namespace mozilla {
michael@0 38 namespace net {
michael@0 39
michael@0 40 SpdyStream3::SpdyStream3(nsAHttpTransaction *httpTransaction,
michael@0 41 SpdySession3 *spdySession,
michael@0 42 int32_t priority)
michael@0 43 : mStreamID(0),
michael@0 44 mSession(spdySession),
michael@0 45 mUpstreamState(GENERATING_SYN_STREAM),
michael@0 46 mSynFrameComplete(0),
michael@0 47 mSentFinOnData(0),
michael@0 48 mTransaction(httpTransaction),
michael@0 49 mSocketTransport(spdySession->SocketTransport()),
michael@0 50 mSegmentReader(nullptr),
michael@0 51 mSegmentWriter(nullptr),
michael@0 52 mChunkSize(spdySession->SendingChunkSize()),
michael@0 53 mRequestBlockedOnRead(0),
michael@0 54 mRecvdFin(0),
michael@0 55 mFullyOpen(0),
michael@0 56 mSentWaitingFor(0),
michael@0 57 mReceivedData(0),
michael@0 58 mSetTCPSocketBuffer(0),
michael@0 59 mTxInlineFrameSize(SpdySession3::kDefaultBufferSize),
michael@0 60 mTxInlineFrameUsed(0),
michael@0 61 mTxStreamFrameSize(0),
michael@0 62 mZlib(spdySession->UpstreamZlib()),
michael@0 63 mDecompressBufferSize(SpdySession3::kDefaultBufferSize),
michael@0 64 mDecompressBufferUsed(0),
michael@0 65 mDecompressedBytes(0),
michael@0 66 mRequestBodyLenRemaining(0),
michael@0 67 mPriority(priority),
michael@0 68 mLocalUnacked(0),
michael@0 69 mBlockedOnRwin(false),
michael@0 70 mTotalSent(0),
michael@0 71 mTotalRead(0),
michael@0 72 mPushSource(nullptr)
michael@0 73 {
michael@0 74 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 75
michael@0 76 LOG3(("SpdyStream3::SpdyStream3 %p", this));
michael@0 77
michael@0 78 mRemoteWindow = spdySession->GetServerInitialWindow();
michael@0 79 mLocalWindow = spdySession->PushAllowance();
michael@0 80
michael@0 81 mTxInlineFrame = new uint8_t[mTxInlineFrameSize];
michael@0 82 mDecompressBuffer = new char[mDecompressBufferSize];
michael@0 83 }
michael@0 84
michael@0 85 SpdyStream3::~SpdyStream3()
michael@0 86 {
michael@0 87 mStreamID = SpdySession3::kDeadStreamID;
michael@0 88 }
michael@0 89
michael@0 90 // ReadSegments() is used to write data down the socket. Generally, HTTP
michael@0 91 // request data is pulled from the approriate transaction and
michael@0 92 // converted to SPDY data. Sometimes control data like a window-update is
michael@0 93 // generated instead.
michael@0 94
michael@0 95 nsresult
michael@0 96 SpdyStream3::ReadSegments(nsAHttpSegmentReader *reader,
michael@0 97 uint32_t count,
michael@0 98 uint32_t *countRead)
michael@0 99 {
michael@0 100 LOG3(("SpdyStream3 %p ReadSegments reader=%p count=%d state=%x",
michael@0 101 this, reader, count, mUpstreamState));
michael@0 102
michael@0 103 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 104
michael@0 105 nsresult rv = NS_ERROR_UNEXPECTED;
michael@0 106 mRequestBlockedOnRead = 0;
michael@0 107
michael@0 108 switch (mUpstreamState) {
michael@0 109 case GENERATING_SYN_STREAM:
michael@0 110 case GENERATING_REQUEST_BODY:
michael@0 111 case SENDING_REQUEST_BODY:
michael@0 112 // Call into the HTTP Transaction to generate the HTTP request
michael@0 113 // stream. That stream will show up in OnReadSegment().
michael@0 114 mSegmentReader = reader;
michael@0 115 rv = mTransaction->ReadSegments(this, count, countRead);
michael@0 116 mSegmentReader = nullptr;
michael@0 117
michael@0 118 // Check to see if the transaction's request could be written out now.
michael@0 119 // If not, mark the stream for callback when writing can proceed.
michael@0 120 if (NS_SUCCEEDED(rv) &&
michael@0 121 mUpstreamState == GENERATING_SYN_STREAM &&
michael@0 122 !mSynFrameComplete)
michael@0 123 mSession->TransactionHasDataToWrite(this);
michael@0 124
michael@0 125 // mTxinlineFrameUsed represents any queued un-sent frame. It might
michael@0 126 // be 0 if there is no such frame, which is not a gurantee that we
michael@0 127 // don't have more request body to send - just that any data that was
michael@0 128 // sent comprised a complete SPDY frame. Likewise, a non 0 value is
michael@0 129 // a queued, but complete, spdy frame length.
michael@0 130
michael@0 131 // Mark that we are blocked on read if the http transaction needs to
michael@0 132 // provide more of the request message body and there is nothing queued
michael@0 133 // for writing
michael@0 134 if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
michael@0 135 mRequestBlockedOnRead = 1;
michael@0 136
michael@0 137 // If the sending flow control window is open (!mBlockedOnRwin) then
michael@0 138 // continue sending the request
michael@0 139 if (!mBlockedOnRwin &&
michael@0 140 !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
michael@0 141 LOG3(("SpdyStream3::ReadSegments %p 0x%X: Sending request data complete, "
michael@0 142 "mUpstreamState=%x",this, mStreamID, mUpstreamState));
michael@0 143 if (mSentFinOnData) {
michael@0 144 ChangeState(UPSTREAM_COMPLETE);
michael@0 145 }
michael@0 146 else {
michael@0 147 GenerateDataFrameHeader(0, true);
michael@0 148 ChangeState(SENDING_FIN_STREAM);
michael@0 149 mSession->TransactionHasDataToWrite(this);
michael@0 150 rv = NS_BASE_STREAM_WOULD_BLOCK;
michael@0 151 }
michael@0 152 }
michael@0 153 break;
michael@0 154
michael@0 155 case SENDING_FIN_STREAM:
michael@0 156 // We were trying to send the FIN-STREAM but were blocked from
michael@0 157 // sending it out - try again.
michael@0 158 if (!mSentFinOnData) {
michael@0 159 mSegmentReader = reader;
michael@0 160 rv = TransmitFrame(nullptr, nullptr, false);
michael@0 161 mSegmentReader = nullptr;
michael@0 162 MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
michael@0 163 "Transmit Frame should be all or nothing");
michael@0 164 if (NS_SUCCEEDED(rv))
michael@0 165 ChangeState(UPSTREAM_COMPLETE);
michael@0 166 }
michael@0 167 else {
michael@0 168 rv = NS_OK;
michael@0 169 mTxInlineFrameUsed = 0; // cancel fin data packet
michael@0 170 ChangeState(UPSTREAM_COMPLETE);
michael@0 171 }
michael@0 172
michael@0 173 *countRead = 0;
michael@0 174
michael@0 175 // don't change OK to WOULD BLOCK. we are really done sending if OK
michael@0 176 break;
michael@0 177
michael@0 178 case UPSTREAM_COMPLETE:
michael@0 179 *countRead = 0;
michael@0 180 rv = NS_OK;
michael@0 181 break;
michael@0 182
michael@0 183 default:
michael@0 184 MOZ_ASSERT(false, "SpdyStream3::ReadSegments unknown state");
michael@0 185 break;
michael@0 186 }
michael@0 187
michael@0 188 return rv;
michael@0 189 }
michael@0 190
michael@0 191 // WriteSegments() is used to read data off the socket. Generally this is
michael@0 192 // just the SPDY frame header and from there the appropriate SPDYStream
michael@0 193 // is identified from the Stream-ID. The http transaction associated with
michael@0 194 // that read then pulls in the data directly.
michael@0 195
michael@0 196 nsresult
michael@0 197 SpdyStream3::WriteSegments(nsAHttpSegmentWriter *writer,
michael@0 198 uint32_t count,
michael@0 199 uint32_t *countWritten)
michael@0 200 {
michael@0 201 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 202 MOZ_ASSERT(!mSegmentWriter, "segment writer in progress");
michael@0 203
michael@0 204 LOG3(("SpdyStream3::WriteSegments %p count=%d state=%x",
michael@0 205 this, count, mUpstreamState));
michael@0 206
michael@0 207 mSegmentWriter = writer;
michael@0 208 nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
michael@0 209 mSegmentWriter = nullptr;
michael@0 210
michael@0 211 return rv;
michael@0 212 }
michael@0 213
michael@0 214 PLDHashOperator
michael@0 215 SpdyStream3::hdrHashEnumerate(const nsACString &key,
michael@0 216 nsAutoPtr<nsCString> &value,
michael@0 217 void *closure)
michael@0 218 {
michael@0 219 SpdyStream3 *self = static_cast<SpdyStream3 *>(closure);
michael@0 220
michael@0 221 self->CompressToFrame(key);
michael@0 222 self->CompressToFrame(value.get());
michael@0 223 return PL_DHASH_NEXT;
michael@0 224 }
michael@0 225
michael@0 226 void
michael@0 227 SpdyStream3::CreatePushHashKey(const nsCString &scheme,
michael@0 228 const nsCString &hostHeader,
michael@0 229 uint64_t serial,
michael@0 230 const nsCSubstring &pathInfo,
michael@0 231 nsCString &outOrigin,
michael@0 232 nsCString &outKey)
michael@0 233 {
michael@0 234 outOrigin = scheme;
michael@0 235 outOrigin.Append(NS_LITERAL_CSTRING("://"));
michael@0 236 outOrigin.Append(hostHeader);
michael@0 237
michael@0 238 outKey = outOrigin;
michael@0 239 outKey.Append(NS_LITERAL_CSTRING("/[spdy3."));
michael@0 240 outKey.AppendInt(serial);
michael@0 241 outKey.Append(NS_LITERAL_CSTRING("]"));
michael@0 242 outKey.Append(pathInfo);
michael@0 243 }
michael@0 244
michael@0 245
michael@0 246 nsresult
michael@0 247 SpdyStream3::ParseHttpRequestHeaders(const char *buf,
michael@0 248 uint32_t avail,
michael@0 249 uint32_t *countUsed)
michael@0 250 {
michael@0 251 // Returns NS_OK even if the headers are incomplete
michael@0 252 // set mSynFrameComplete flag if they are complete
michael@0 253
michael@0 254 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 255 MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM);
michael@0 256
michael@0 257 LOG3(("SpdyStream3::ParseHttpRequestHeaders %p avail=%d state=%x",
michael@0 258 this, avail, mUpstreamState));
michael@0 259
michael@0 260 mFlatHttpRequestHeaders.Append(buf, avail);
michael@0 261
michael@0 262 // We can use the simple double crlf because firefox is the
michael@0 263 // only client we are parsing
michael@0 264 int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
michael@0 265
michael@0 266 if (endHeader == kNotFound) {
michael@0 267 // We don't have all the headers yet
michael@0 268 LOG3(("SpdyStream3::ParseHttpRequestHeaders %p "
michael@0 269 "Need more header bytes. Len = %d",
michael@0 270 this, mFlatHttpRequestHeaders.Length()));
michael@0 271 *countUsed = avail;
michael@0 272 return NS_OK;
michael@0 273 }
michael@0 274
michael@0 275 // We have recvd all the headers, trim the local
michael@0 276 // buffer of the final empty line, and set countUsed to reflect
michael@0 277 // the whole header has been consumed.
michael@0 278 uint32_t oldLen = mFlatHttpRequestHeaders.Length();
michael@0 279 mFlatHttpRequestHeaders.SetLength(endHeader + 2);
michael@0 280 *countUsed = avail - (oldLen - endHeader) + 4;
michael@0 281 mSynFrameComplete = 1;
michael@0 282
michael@0 283 nsCString hostHeader;
michael@0 284 nsCString hashkey;
michael@0 285 mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
michael@0 286
michael@0 287 CreatePushHashKey(NS_LITERAL_CSTRING("https"),
michael@0 288 hostHeader, mSession->Serial(),
michael@0 289 mTransaction->RequestHead()->RequestURI(),
michael@0 290 mOrigin, hashkey);
michael@0 291
michael@0 292 // check the push cache for GET
michael@0 293 if (mTransaction->RequestHead()->IsGet()) {
michael@0 294 // from :scheme, :host, :path
michael@0 295 nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
michael@0 296 SpdyPushCache *cache = nullptr;
michael@0 297 if (loadGroupCI)
michael@0 298 loadGroupCI->GetSpdyPushCache(&cache);
michael@0 299
michael@0 300 SpdyPushedStream3 *pushedStream = nullptr;
michael@0 301 // we remove the pushedstream from the push cache so that
michael@0 302 // it will not be used for another GET. This does not destroy the
michael@0 303 // stream itself - that is done when the transactionhash is done with it.
michael@0 304 if (cache)
michael@0 305 pushedStream = cache->RemovePushedStreamSpdy3(hashkey);
michael@0 306
michael@0 307 if (pushedStream) {
michael@0 308 LOG3(("Pushed Stream Match located id=0x%X key=%s\n",
michael@0 309 pushedStream->StreamID(), hashkey.get()));
michael@0 310 pushedStream->SetConsumerStream(this);
michael@0 311 mPushSource = pushedStream;
michael@0 312 mSentFinOnData = 1;
michael@0 313
michael@0 314 // This stream has been activated (and thus counts against the concurrency
michael@0 315 // limit intentionally), but will not be registered via
michael@0 316 // RegisterStreamID (below) because of the push match. Therefore the
michael@0 317 // concurrency sempahore needs to be balanced.
michael@0 318 mSession->DecrementConcurrent(this);
michael@0 319
michael@0 320 // There is probably pushed data buffered so trigger a read manually
michael@0 321 // as we can't rely on future network events to do it
michael@0 322 mSession->ConnectPushedStream(this);
michael@0 323 return NS_OK;
michael@0 324 }
michael@0 325 }
michael@0 326
michael@0 327 // It is now OK to assign a streamID that we are assured will
michael@0 328 // be monotonically increasing amongst syn-streams on this
michael@0 329 // session
michael@0 330 mStreamID = mSession->RegisterStreamID(this);
michael@0 331 MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd");
michael@0 332
michael@0 333 if (mStreamID >= 0x80000000) {
michael@0 334 // streamID must fit in 31 bits. This is theoretically possible
michael@0 335 // because stream ID assignment is asynchronous to stream creation
michael@0 336 // because of the protocol requirement that the ID in syn-stream
michael@0 337 // be monotonically increasing. In reality this is really not possible
michael@0 338 // because new streams stop being added to a session with 0x10000000 / 2
michael@0 339 // IDs still available and no race condition is going to bridge that gap,
michael@0 340 // so we can be comfortable on just erroring out for correctness in that
michael@0 341 // case.
michael@0 342 LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
michael@0 343 return NS_ERROR_UNEXPECTED;
michael@0 344 }
michael@0 345
michael@0 346 // Now we need to convert the flat http headers into a set
michael@0 347 // of SPDY headers.. writing to mTxInlineFrame{sz}
michael@0 348
michael@0 349 mTxInlineFrame[0] = SpdySession3::kFlag_Control;
michael@0 350 mTxInlineFrame[1] = SpdySession3::kVersion;
michael@0 351 mTxInlineFrame[2] = 0;
michael@0 352 mTxInlineFrame[3] = SpdySession3::CONTROL_TYPE_SYN_STREAM;
michael@0 353 // 4 to 7 are length and flags, we'll fill that in later
michael@0 354
michael@0 355 NetworkEndian::writeUint32(mTxInlineFrame + 8, mStreamID);
michael@0 356
michael@0 357 // this is the associated-to field, which is not used sending
michael@0 358 // from the client in the http binding
michael@0 359 memset(mTxInlineFrame + 12, 0, 4);
michael@0 360
michael@0 361 // Priority flags are the E0 mask of byte 16.
michael@0 362 // 0 is highest priority, 7 is lowest.
michael@0 363 // The other 5 bits of byte 16 are unused.
michael@0 364
michael@0 365 if (mPriority >= nsISupportsPriority::PRIORITY_LOWEST)
michael@0 366 mTxInlineFrame[16] = 7 << 5;
michael@0 367 else if (mPriority <= nsISupportsPriority::PRIORITY_HIGHEST)
michael@0 368 mTxInlineFrame[16] = 0 << 5;
michael@0 369 else {
michael@0 370 // The priority mapping relies on the unfiltered ranged to be
michael@0 371 // between -20 .. +20
michael@0 372 PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_LOWEST == 20);
michael@0 373 PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_HIGHEST == -20);
michael@0 374
michael@0 375 // Add one to the priority so that values such as -10 and -11
michael@0 376 // get different spdy priorities - this appears to be an important
michael@0 377 // breaking line in the priorities content assigns to
michael@0 378 // transactions.
michael@0 379 uint8_t calculatedPriority = 3 + ((mPriority + 1) / 5);
michael@0 380 MOZ_ASSERT (!(calculatedPriority & 0xf8),
michael@0 381 "Calculated Priority Out Of Range");
michael@0 382 mTxInlineFrame[16] = calculatedPriority << 5;
michael@0 383 }
michael@0 384
michael@0 385 // The client cert "slot". Right now we don't send client certs
michael@0 386 mTxInlineFrame[17] = 0;
michael@0 387
michael@0 388 nsCString versionHeader;
michael@0 389 if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
michael@0 390 versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
michael@0 391 else
michael@0 392 versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
michael@0 393
michael@0 394 // use mRequestHead() to get a sense of how big to make the hash,
michael@0 395 // even though we are parsing the actual text stream because
michael@0 396 // it is legit to append headers.
michael@0 397 nsClassHashtable<nsCStringHashKey, nsCString>
michael@0 398 hdrHash(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
michael@0 399
michael@0 400 const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
michael@0 401
michael@0 402 // need to hash all the headers together to remove duplicates, special
michael@0 403 // headers, etc..
michael@0 404
michael@0 405 int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
michael@0 406 while (true) {
michael@0 407 int32_t startIndex = crlfIndex + 2;
michael@0 408
michael@0 409 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
michael@0 410 if (crlfIndex == -1)
michael@0 411 break;
michael@0 412
michael@0 413 int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
michael@0 414 crlfIndex - startIndex);
michael@0 415 if (colonIndex == -1)
michael@0 416 break;
michael@0 417
michael@0 418 nsDependentCSubstring name = Substring(beginBuffer + startIndex,
michael@0 419 beginBuffer + colonIndex);
michael@0 420 // all header names are lower case in spdy
michael@0 421 ToLowerCase(name);
michael@0 422
michael@0 423 // exclusions.. mostly from 3.2.1
michael@0 424 if (name.Equals("connection") ||
michael@0 425 name.Equals("keep-alive") ||
michael@0 426 name.Equals("host") ||
michael@0 427 name.Equals("accept-encoding") ||
michael@0 428 name.Equals("te") ||
michael@0 429 name.Equals("transfer-encoding"))
michael@0 430 continue;
michael@0 431
michael@0 432 nsCString *val = hdrHash.Get(name);
michael@0 433 if (!val) {
michael@0 434 val = new nsCString();
michael@0 435 hdrHash.Put(name, val);
michael@0 436 }
michael@0 437
michael@0 438 int32_t valueIndex = colonIndex + 1;
michael@0 439 while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
michael@0 440 ++valueIndex;
michael@0 441
michael@0 442 nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
michael@0 443 beginBuffer + crlfIndex);
michael@0 444 if (!val->IsEmpty())
michael@0 445 val->Append(static_cast<char>(0));
michael@0 446 val->Append(v);
michael@0 447
michael@0 448 if (name.Equals("content-length")) {
michael@0 449 int64_t len;
michael@0 450 if (nsHttp::ParseInt64(val->get(), nullptr, &len))
michael@0 451 mRequestBodyLenRemaining = len;
michael@0 452 }
michael@0 453 }
michael@0 454
michael@0 455 mTxInlineFrameUsed = 18;
michael@0 456
michael@0 457 // Do not naively log the request headers here beacuse they might
michael@0 458 // contain auth. The http transaction already logs the sanitized request
michael@0 459 // headers at this same level so it is not necessary to do so here.
michael@0 460
michael@0 461 const char *methodHeader = mTransaction->RequestHead()->Method().get();
michael@0 462
michael@0 463 // The header block length
michael@0 464 uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */
michael@0 465 CompressToFrame(count);
michael@0 466
michael@0 467 // :method, :path, :version comprise a HTTP/1 request line, so send those first
michael@0 468 // to make life easy for any gateways
michael@0 469 CompressToFrame(NS_LITERAL_CSTRING(":method"));
michael@0 470 CompressToFrame(methodHeader, strlen(methodHeader));
michael@0 471 CompressToFrame(NS_LITERAL_CSTRING(":path"));
michael@0 472 CompressToFrame(mTransaction->RequestHead()->RequestURI());
michael@0 473 CompressToFrame(NS_LITERAL_CSTRING(":version"));
michael@0 474 CompressToFrame(versionHeader);
michael@0 475
michael@0 476 CompressToFrame(NS_LITERAL_CSTRING(":host"));
michael@0 477 CompressToFrame(hostHeader);
michael@0 478 CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
michael@0 479 CompressToFrame(NS_LITERAL_CSTRING("https"));
michael@0 480
michael@0 481 hdrHash.Enumerate(hdrHashEnumerate, this);
michael@0 482 CompressFlushFrame();
michael@0 483
michael@0 484 // 4 to 7 are length and flags, which we can now fill in
michael@0 485 NetworkEndian::writeUint32(mTxInlineFrame + 1 * sizeof(uint32_t),
michael@0 486 mTxInlineFrameUsed - 8);
michael@0 487
michael@0 488 MOZ_ASSERT(!mTxInlineFrame[4], "Size greater than 24 bits");
michael@0 489
michael@0 490 // Determine whether to put the fin bit on the syn stream frame or whether
michael@0 491 // to wait for a data packet to put it on.
michael@0 492
michael@0 493 if (mTransaction->RequestHead()->IsGet() ||
michael@0 494 mTransaction->RequestHead()->IsConnect() ||
michael@0 495 mTransaction->RequestHead()->IsHead()) {
michael@0 496 // for GET, CONNECT, and HEAD place the fin bit right on the
michael@0 497 // syn stream packet
michael@0 498
michael@0 499 mSentFinOnData = 1;
michael@0 500 mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN;
michael@0 501 }
michael@0 502 else if (mTransaction->RequestHead()->IsPost() ||
michael@0 503 mTransaction->RequestHead()->IsPut() ||
michael@0 504 mTransaction->RequestHead()->IsOptions()) {
michael@0 505 // place fin in a data frame even for 0 length messages, I've seen
michael@0 506 // the google gateway be unhappy with fin-on-syn for 0 length POST
michael@0 507 }
michael@0 508 else if (!mRequestBodyLenRemaining) {
michael@0 509 // for other HTTP extension methods, rely on the content-length
michael@0 510 // to determine whether or not to put fin on syn
michael@0 511 mSentFinOnData = 1;
michael@0 512 mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN;
michael@0 513 }
michael@0 514
michael@0 515 Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
michael@0 516
michael@0 517 // The size of the input headers is approximate
michael@0 518 uint32_t ratio =
michael@0 519 (mTxInlineFrameUsed - 18) * 100 /
michael@0 520 (11 + mTransaction->RequestHead()->RequestURI().Length() +
michael@0 521 mFlatHttpRequestHeaders.Length());
michael@0 522
michael@0 523 Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 void
michael@0 528 SpdyStream3::AdjustInitialWindow()
michael@0 529 {
michael@0 530 MOZ_ASSERT(mSession->PushAllowance() <= ASpdySession::kInitialRwin);
michael@0 531
michael@0 532 // The session initial_window is sized for serverpushed streams. When we
michael@0 533 // generate a client pulled stream we want to adjust the initial window
michael@0 534 // to a huge value in a pipeline with that SYN_STREAM.
michael@0 535
michael@0 536 // >0 even numbered IDs are pushed streams.
michael@0 537 // odd numbered IDs are pulled streams.
michael@0 538 // 0 is the sink for a pushed stream.
michael@0 539 SpdyStream3 *stream = this;
michael@0 540 if (!mStreamID) {
michael@0 541 MOZ_ASSERT(mPushSource);
michael@0 542 if (!mPushSource)
michael@0 543 return;
michael@0 544 stream = mPushSource;
michael@0 545 MOZ_ASSERT(stream->mStreamID);
michael@0 546 MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream
michael@0 547
michael@0 548 // If the pushed stream has sent a FIN, there is no reason to update
michael@0 549 // the window
michael@0 550 if (stream->RecvdFin())
michael@0 551 return;
michael@0 552 }
michael@0 553
michael@0 554 // For server pushes we also want to include in the ack any data that has been
michael@0 555 // buffered but unacknowledged.
michael@0 556
michael@0 557 // mLocalUnacked is technically 64 bits, but because it can never grow larger than
michael@0 558 // our window size (which is closer to 29bits max) we know it fits comfortably in 32.
michael@0 559 // However we don't really enforce that, and track it as a 64 so that broken senders
michael@0 560 // can still interoperate. That means we have to be careful with this calculation.
michael@0 561 uint64_t toack64 = (ASpdySession::kInitialRwin - mSession->PushAllowance()) +
michael@0 562 stream->mLocalUnacked;
michael@0 563 stream->mLocalUnacked = 0;
michael@0 564 if (toack64 > 0x7fffffff) {
michael@0 565 stream->mLocalUnacked = toack64 - 0x7fffffff;
michael@0 566 toack64 = 0x7fffffff;
michael@0 567 }
michael@0 568 uint32_t toack = static_cast<uint32_t>(toack64);
michael@0 569 if (!toack)
michael@0 570 return;
michael@0 571 toack = PR_htonl(toack);
michael@0 572
michael@0 573 SpdySession3::EnsureBuffer(mTxInlineFrame,
michael@0 574 mTxInlineFrameUsed + 16,
michael@0 575 mTxInlineFrameUsed,
michael@0 576 mTxInlineFrameSize);
michael@0 577
michael@0 578 unsigned char *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
michael@0 579 mTxInlineFrameUsed += 16;
michael@0 580
michael@0 581 memset(packet, 0, 8);
michael@0 582 packet[0] = SpdySession3::kFlag_Control;
michael@0 583 packet[1] = SpdySession3::kVersion;
michael@0 584 packet[3] = SpdySession3::CONTROL_TYPE_WINDOW_UPDATE;
michael@0 585 packet[7] = 8; // 8 data bytes after 8 byte header
michael@0 586
michael@0 587 uint32_t id = PR_htonl(stream->mStreamID);
michael@0 588 memcpy(packet + 8, &id, 4);
michael@0 589 memcpy(packet + 12, &toack, 4);
michael@0 590
michael@0 591 stream->mLocalWindow += PR_ntohl(toack);
michael@0 592 LOG3(("AdjustInitialwindow %p 0x%X %u\n",
michael@0 593 this, stream->mStreamID, PR_ntohl(toack)));
michael@0 594 }
michael@0 595
michael@0 596 void
michael@0 597 SpdyStream3::UpdateTransportReadEvents(uint32_t count)
michael@0 598 {
michael@0 599 mTotalRead += count;
michael@0 600
michael@0 601 mTransaction->OnTransportStatus(mSocketTransport,
michael@0 602 NS_NET_STATUS_RECEIVING_FROM,
michael@0 603 mTotalRead);
michael@0 604 }
michael@0 605
michael@0 606 void
michael@0 607 SpdyStream3::UpdateTransportSendEvents(uint32_t count)
michael@0 608 {
michael@0 609 mTotalSent += count;
michael@0 610
michael@0 611 // normally on non-windows platform we use TCP autotuning for
michael@0 612 // the socket buffers, and this works well (managing enough
michael@0 613 // buffers for BDP while conserving memory) for HTTP even when
michael@0 614 // it creates really deep queues. However this 'buffer bloat' is
michael@0 615 // a problem for spdy because it ruins the low latency properties
michael@0 616 // necessary for PING and cancel to work meaningfully.
michael@0 617 //
michael@0 618 // If this stream represents a large upload, disable autotuning for
michael@0 619 // the session and cap the send buffers by default at 128KB.
michael@0 620 // (10Mbit/sec @ 100ms)
michael@0 621 //
michael@0 622 uint32_t bufferSize = gHttpHandler->SpdySendBufferSize();
michael@0 623 if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) {
michael@0 624 mSetTCPSocketBuffer = 1;
michael@0 625 mSocketTransport->SetSendBufferSize(bufferSize);
michael@0 626 }
michael@0 627
michael@0 628 if (mUpstreamState != SENDING_FIN_STREAM)
michael@0 629 mTransaction->OnTransportStatus(mSocketTransport,
michael@0 630 NS_NET_STATUS_SENDING_TO,
michael@0 631 mTotalSent);
michael@0 632
michael@0 633 if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
michael@0 634 mSentWaitingFor = 1;
michael@0 635 mTransaction->OnTransportStatus(mSocketTransport,
michael@0 636 NS_NET_STATUS_WAITING_FOR,
michael@0 637 0);
michael@0 638 }
michael@0 639 }
michael@0 640
michael@0 641 nsresult
michael@0 642 SpdyStream3::TransmitFrame(const char *buf,
michael@0 643 uint32_t *countUsed,
michael@0 644 bool forceCommitment)
michael@0 645 {
michael@0 646 // If TransmitFrame returns SUCCESS than all the data is sent (or at least
michael@0 647 // buffered at the session level), if it returns WOULD_BLOCK then none of
michael@0 648 // the data is sent.
michael@0 649
michael@0 650 // You can call this function with no data and no out parameter in order to
michael@0 651 // flush internal buffers that were previously blocked on writing. You can
michael@0 652 // of course feed new data to it as well.
michael@0 653
michael@0 654 LOG3(("SpdyStream3::TransmitFrame %p inline=%d stream=%d",
michael@0 655 this, mTxInlineFrameUsed, mTxStreamFrameSize));
michael@0 656 if (countUsed)
michael@0 657 *countUsed = 0;
michael@0 658
michael@0 659 if (!mTxInlineFrameUsed) {
michael@0 660 MOZ_ASSERT(!buf);
michael@0 661 return NS_OK;
michael@0 662 }
michael@0 663
michael@0 664 MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit");
michael@0 665 MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader");
michael@0 666 MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed),
michael@0 667 "TransmitFrame arguments inconsistent");
michael@0 668
michael@0 669 uint32_t transmittedCount;
michael@0 670 nsresult rv;
michael@0 671
michael@0 672 // In the (relatively common) event that we have a small amount of data
michael@0 673 // split between the inlineframe and the streamframe, then move the stream
michael@0 674 // data into the inlineframe via copy in order to coalesce into one write.
michael@0 675 // Given the interaction with ssl this is worth the small copy cost.
michael@0 676 if (mTxStreamFrameSize && mTxInlineFrameUsed &&
michael@0 677 mTxStreamFrameSize < SpdySession3::kDefaultBufferSize &&
michael@0 678 mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
michael@0 679 LOG3(("Coalesce Transmit"));
michael@0 680 memcpy (mTxInlineFrame + mTxInlineFrameUsed,
michael@0 681 buf, mTxStreamFrameSize);
michael@0 682 if (countUsed)
michael@0 683 *countUsed += mTxStreamFrameSize;
michael@0 684 mTxInlineFrameUsed += mTxStreamFrameSize;
michael@0 685 mTxStreamFrameSize = 0;
michael@0 686 }
michael@0 687
michael@0 688 rv =
michael@0 689 mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed,
michael@0 690 forceCommitment);
michael@0 691
michael@0 692 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
michael@0 693 MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK");
michael@0 694 mSession->TransactionHasDataToWrite(this);
michael@0 695 }
michael@0 696 if (NS_FAILED(rv)) // this will include WOULD_BLOCK
michael@0 697 return rv;
michael@0 698
michael@0 699 // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
michael@0 700 // bytes through to the SpdySession3 and then the HttpConnection which calls
michael@0 701 // the socket write function. It will accept all of the inline and stream
michael@0 702 // data because of the above 'commitment' even if it has to buffer
michael@0 703
michael@0 704 rv = mSegmentReader->OnReadSegment(reinterpret_cast<char*>(mTxInlineFrame.get()),
michael@0 705 mTxInlineFrameUsed,
michael@0 706 &transmittedCount);
michael@0 707 LOG3(("SpdyStream3::TransmitFrame for inline session=%p "
michael@0 708 "stream=%p result %x len=%d",
michael@0 709 mSession, this, rv, transmittedCount));
michael@0 710
michael@0 711 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
michael@0 712 "inconsistent inline commitment result");
michael@0 713
michael@0 714 if (NS_FAILED(rv))
michael@0 715 return rv;
michael@0 716
michael@0 717 MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed,
michael@0 718 "inconsistent inline commitment count");
michael@0 719
michael@0 720 SpdySession3::LogIO(mSession, this, "Writing from Inline Buffer",
michael@0 721 reinterpret_cast<char*>(mTxInlineFrame.get()),
michael@0 722 transmittedCount);
michael@0 723
michael@0 724 if (mTxStreamFrameSize) {
michael@0 725 if (!buf) {
michael@0 726 // this cannot happen
michael@0 727 MOZ_ASSERT(false, "Stream transmit with null buf argument to "
michael@0 728 "TransmitFrame()");
michael@0 729 LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
michael@0 730 return NS_ERROR_UNEXPECTED;
michael@0 731 }
michael@0 732
michael@0 733 rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize,
michael@0 734 &transmittedCount);
michael@0 735
michael@0 736 LOG3(("SpdyStream3::TransmitFrame for regular session=%p "
michael@0 737 "stream=%p result %x len=%d",
michael@0 738 mSession, this, rv, transmittedCount));
michael@0 739
michael@0 740 MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
michael@0 741 "inconsistent stream commitment result");
michael@0 742
michael@0 743 if (NS_FAILED(rv))
michael@0 744 return rv;
michael@0 745
michael@0 746 MOZ_ASSERT(transmittedCount == mTxStreamFrameSize,
michael@0 747 "inconsistent stream commitment count");
michael@0 748
michael@0 749 SpdySession3::LogIO(mSession, this, "Writing from Transaction Buffer",
michael@0 750 buf, transmittedCount);
michael@0 751
michael@0 752 *countUsed += mTxStreamFrameSize;
michael@0 753 }
michael@0 754
michael@0 755 // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
michael@0 756 UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
michael@0 757
michael@0 758 mTxInlineFrameUsed = 0;
michael@0 759 mTxStreamFrameSize = 0;
michael@0 760
michael@0 761 return NS_OK;
michael@0 762 }
michael@0 763
michael@0 764 void
michael@0 765 SpdyStream3::ChangeState(enum stateType newState)
michael@0 766 {
michael@0 767 LOG3(("SpdyStream3::ChangeState() %p from %X to %X",
michael@0 768 this, mUpstreamState, newState));
michael@0 769 mUpstreamState = newState;
michael@0 770 return;
michael@0 771 }
michael@0 772
michael@0 773 void
michael@0 774 SpdyStream3::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
michael@0 775 {
michael@0 776 LOG3(("SpdyStream3::GenerateDataFrameHeader %p len=%d last=%d",
michael@0 777 this, dataLength, lastFrame));
michael@0 778
michael@0 779 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 780 MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty");
michael@0 781 MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty");
michael@0 782 MOZ_ASSERT(!(dataLength & 0xff000000), "datalength > 24 bits");
michael@0 783
michael@0 784 NetworkEndian::writeUint32(mTxInlineFrame, mStreamID);
michael@0 785 NetworkEndian::writeUint32(mTxInlineFrame + 1 * sizeof(uint32_t), dataLength);
michael@0 786
michael@0 787 MOZ_ASSERT(!(mTxInlineFrame[0] & 0x80), "control bit set unexpectedly");
michael@0 788 MOZ_ASSERT(!mTxInlineFrame[4], "flag bits set unexpectedly");
michael@0 789
michael@0 790 mTxInlineFrameUsed = 8;
michael@0 791 mTxStreamFrameSize = dataLength;
michael@0 792
michael@0 793 if (lastFrame) {
michael@0 794 mTxInlineFrame[4] |= SpdySession3::kFlag_Data_FIN;
michael@0 795 if (dataLength)
michael@0 796 mSentFinOnData = 1;
michael@0 797 }
michael@0 798 }
michael@0 799
michael@0 800 void
michael@0 801 SpdyStream3::CompressToFrame(const nsACString &str)
michael@0 802 {
michael@0 803 CompressToFrame(str.BeginReading(), str.Length());
michael@0 804 }
michael@0 805
michael@0 806 void
michael@0 807 SpdyStream3::CompressToFrame(const nsACString *str)
michael@0 808 {
michael@0 809 CompressToFrame(str->BeginReading(), str->Length());
michael@0 810 }
michael@0 811
michael@0 812 // Dictionary taken from
michael@0 813 // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3
michael@0 814
michael@0 815 const unsigned char SpdyStream3::kDictionary[] = {
michael@0 816 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i
michael@0 817 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h
michael@0 818 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p
michael@0 819 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p
michael@0 820 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e
michael@0 821 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - -
michael@0 822 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - -
michael@0 823 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t -
michael@0 824 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
michael@0 825 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e
michael@0 826 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c
michael@0 827 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o
michael@0 828 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - -
michael@0 829 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l
michael@0 830 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e -
michael@0 831 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p
michael@0 832 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s
michael@0 833 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e -
michael@0 834 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w
michael@0 835 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h
michael@0 836 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o
michael@0 837 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c
michael@0 838 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r
michael@0 839 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o
michael@0 840 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n
michael@0 841 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
michael@0 842 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e
michael@0 843 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
michael@0 844 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o
michael@0 845 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - -
michael@0 846 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t -
michael@0 847 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e
michael@0 848 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t
michael@0 849 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g
michael@0 850 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o
michael@0 851 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o
michael@0 852 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - -
michael@0 853 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
michael@0 854 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - -
michael@0 855 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t
michael@0 856 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - -
michael@0 857 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n
michael@0 858 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - -
michael@0 859 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - -
michael@0 860 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - -
michael@0 861 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t
michael@0 862 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i
michael@0 863 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f
michael@0 864 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h
michael@0 865 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i
michael@0 866 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h -
michael@0 867 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o
michael@0 868 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s
michael@0 869 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - -
michael@0 870 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e -
michael@0 871 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - -
michael@0 872 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g
michael@0 873 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f -
michael@0 874 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i
michael@0 875 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e
michael@0 876 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t
michael@0 877 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e
michael@0 878 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c
michael@0 879 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - -
michael@0 880 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r
michael@0 881 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - -
michael@0 882 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a -
michael@0 883 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y
michael@0 884 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t
michael@0 885 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - -
michael@0 886 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a
michael@0 887 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a
michael@0 888 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - -
michael@0 889 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - -
michael@0 890 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r
michael@0 891 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r
michael@0 892 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r -
michael@0 893 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e
michael@0 894 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e -
michael@0 895 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l
michael@0 896 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r
michael@0 897 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e
michael@0 898 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g -
michael@0 899 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a
michael@0 900 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s
michael@0 901 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t
michael@0 902 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y
michael@0 903 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a -
michael@0 904 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i
michael@0 905 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w
michael@0 906 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n
michael@0 907 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - -
michael@0 908 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d
michael@0 909 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t -
michael@0 910 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u
michael@0 911 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0
michael@0 912 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v
michael@0 913 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - -
michael@0 914 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1
michael@0 915 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r
michael@0 916 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b
michael@0 917 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s
michael@0 918 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i
michael@0 919 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e
michael@0 920 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e -
michael@0 921 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i
michael@0 922 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2
michael@0 923 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5
michael@0 924 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0
michael@0 925 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3
michael@0 926 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7
michael@0 927 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0
michael@0 928 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4
michael@0 929 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1
michael@0 930 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1
michael@0 931 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4
michael@0 932 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4
michael@0 933 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N
michael@0 934 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o
michael@0 935 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e
michael@0 936 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a
michael@0 937 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 -
michael@0 938 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e
michael@0 939 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o
michael@0 940 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m
michael@0 941 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4
michael@0 942 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R
michael@0 943 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0
michael@0 944 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h
michael@0 945 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0
michael@0 946 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d
michael@0 947 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N
michael@0 948 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d
michael@0 949 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e
michael@0 950 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r
michael@0 951 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o
michael@0 952 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t
michael@0 953 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e
michael@0 954 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 -
michael@0 955 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e -
michael@0 956 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a
michael@0 957 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F
michael@0 958 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A
michael@0 959 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J
michael@0 960 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A
michael@0 961 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t -
michael@0 962 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v -
michael@0 963 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0
michael@0 964 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n
michael@0 965 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W
michael@0 966 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u -
michael@0 967 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a
michael@0 968 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - -
michael@0 969 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k
michael@0 970 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t -
michael@0 971 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a
michael@0 972 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i
michael@0 973 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g
michael@0 974 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g
michael@0 975 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i
michael@0 976 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
michael@0 977 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i
michael@0 978 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x
michael@0 979 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l
michael@0 980 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l
michael@0 981 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t
michael@0 982 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r
michael@0 983 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l
michael@0 984 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t
michael@0 985 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e
michael@0 986 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e
michael@0 987 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d
michael@0 988 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e
michael@0 989 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c
michael@0 990 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i
michael@0 991 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 -
michael@0 992 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - -
michael@0 993 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 -
michael@0 994 };
michael@0 995
michael@0 996 // This can be called N times.. 1 for syn_reply and 0->N for headers
michael@0 997 nsresult
michael@0 998 SpdyStream3::Uncompress(z_stream *context,
michael@0 999 char *blockStart,
michael@0 1000 uint32_t blockLen)
michael@0 1001 {
michael@0 1002 mDecompressedBytes += blockLen;
michael@0 1003
michael@0 1004 context->avail_in = blockLen;
michael@0 1005 context->next_in = reinterpret_cast<unsigned char *>(blockStart);
michael@0 1006 bool triedDictionary = false;
michael@0 1007
michael@0 1008 do {
michael@0 1009 context->next_out =
michael@0 1010 reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
michael@0 1011 mDecompressBufferUsed;
michael@0 1012 context->avail_out = mDecompressBufferSize - mDecompressBufferUsed;
michael@0 1013 int zlib_rv = inflate(context, Z_NO_FLUSH);
michael@0 1014
michael@0 1015 if (zlib_rv == Z_NEED_DICT) {
michael@0 1016 if (triedDictionary) {
michael@0 1017 LOG3(("SpdySession3::Uncompress %p Dictionary Error\n", this));
michael@0 1018 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1019 }
michael@0 1020
michael@0 1021 triedDictionary = true;
michael@0 1022 inflateSetDictionary(context, kDictionary, sizeof(kDictionary));
michael@0 1023 }
michael@0 1024
michael@0 1025 if (zlib_rv == Z_DATA_ERROR)
michael@0 1026 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1027
michael@0 1028 if (zlib_rv == Z_MEM_ERROR)
michael@0 1029 return NS_ERROR_FAILURE;
michael@0 1030
michael@0 1031 // zlib's inflate() decreases context->avail_out by the amount it places
michael@0 1032 // in the output buffer
michael@0 1033
michael@0 1034 mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
michael@0 1035 context->avail_out;
michael@0 1036
michael@0 1037 // When there is no more output room, but input still available then
michael@0 1038 // increase the output space
michael@0 1039 if (zlib_rv == Z_OK &&
michael@0 1040 !context->avail_out && context->avail_in) {
michael@0 1041 LOG3(("SpdyStream3::Uncompress %p Large Headers - so far %d",
michael@0 1042 this, mDecompressBufferSize));
michael@0 1043 SpdySession3::EnsureBuffer(mDecompressBuffer,
michael@0 1044 mDecompressBufferSize + 4096,
michael@0 1045 mDecompressBufferUsed,
michael@0 1046 mDecompressBufferSize);
michael@0 1047 }
michael@0 1048 }
michael@0 1049 while (context->avail_in);
michael@0 1050 return NS_OK;
michael@0 1051 }
michael@0 1052
michael@0 1053 // mDecompressBuffer contains 0 to N uncompressed Name/Value Header blocks
michael@0 1054 nsresult
michael@0 1055 SpdyStream3::FindHeader(nsCString name,
michael@0 1056 nsDependentCSubstring &value)
michael@0 1057 {
michael@0 1058 const unsigned char *nvpair = reinterpret_cast<unsigned char *>
michael@0 1059 (mDecompressBuffer.get()) + 4;
michael@0 1060 const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
michael@0 1061 (mDecompressBuffer.get()) + mDecompressBufferUsed;
michael@0 1062 if (lastHeaderByte < nvpair)
michael@0 1063 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1064
michael@0 1065 do {
michael@0 1066 uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t));
michael@0 1067
michael@0 1068 for (uint32_t index = 0; index < numPairs; ++index) {
michael@0 1069 if (lastHeaderByte < nvpair + 4)
michael@0 1070 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1071 uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
michael@0 1072 (nvpair[2] << 8) + nvpair[3];
michael@0 1073 if (lastHeaderByte < nvpair + 4 + nameLen)
michael@0 1074 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1075 nsDependentCSubstring nameString =
michael@0 1076 Substring(reinterpret_cast<const char *>(nvpair) + 4,
michael@0 1077 reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
michael@0 1078 if (lastHeaderByte < nvpair + 8 + nameLen)
michael@0 1079 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1080 uint32_t valueLen = (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
michael@0 1081 (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen];
michael@0 1082 if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
michael@0 1083 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1084 if (nameString.Equals(name)) {
michael@0 1085 value.Assign(((char *)nvpair) + 8 + nameLen, valueLen);
michael@0 1086 return NS_OK;
michael@0 1087 }
michael@0 1088
michael@0 1089 // that pair didn't match - try the next one in this block
michael@0 1090 nvpair += 8 + nameLen + valueLen;
michael@0 1091 }
michael@0 1092
michael@0 1093 // move to the next name/value header block (if there is one) - the
michael@0 1094 // first pair is offset 4 bytes into it
michael@0 1095 nvpair += 4;
michael@0 1096 } while (lastHeaderByte >= nvpair);
michael@0 1097
michael@0 1098 return NS_ERROR_NOT_AVAILABLE;
michael@0 1099 }
michael@0 1100
michael@0 1101 // ConvertHeaders is used to convert the response headers
michael@0 1102 // in a syn_reply or in 0..N headers frames that follow it into
michael@0 1103 // HTTP/1 format
michael@0 1104 nsresult
michael@0 1105 SpdyStream3::ConvertHeaders(nsACString &aHeadersOut)
michael@0 1106 {
michael@0 1107 // :status and :version are required.
michael@0 1108 nsDependentCSubstring status, version;
michael@0 1109 nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
michael@0 1110 status);
michael@0 1111 if (NS_FAILED(rv))
michael@0 1112 return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
michael@0 1113
michael@0 1114 rv = FindHeader(NS_LITERAL_CSTRING(":version"),
michael@0 1115 version);
michael@0 1116 if (NS_FAILED(rv))
michael@0 1117 return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
michael@0 1118
michael@0 1119 if (mDecompressedBytes && mDecompressBufferUsed) {
michael@0 1120 Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, mDecompressedBytes);
michael@0 1121 uint32_t ratio =
michael@0 1122 mDecompressedBytes * 100 / mDecompressBufferUsed;
michael@0 1123 Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
michael@0 1124 }
michael@0 1125
michael@0 1126 aHeadersOut.Truncate();
michael@0 1127 aHeadersOut.SetCapacity(mDecompressBufferUsed + 64);
michael@0 1128
michael@0 1129 // Connection, Keep-Alive and chunked transfer encodings are to be
michael@0 1130 // removed.
michael@0 1131
michael@0 1132 // Content-Length is 'advisory'.. we will not strip it because it can
michael@0 1133 // create UI feedback.
michael@0 1134
michael@0 1135 aHeadersOut.Append(version);
michael@0 1136 aHeadersOut.Append(NS_LITERAL_CSTRING(" "));
michael@0 1137 aHeadersOut.Append(status);
michael@0 1138 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
michael@0 1139
michael@0 1140 const unsigned char *nvpair = reinterpret_cast<unsigned char *>
michael@0 1141 (mDecompressBuffer.get()) + 4;
michael@0 1142 const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
michael@0 1143 (mDecompressBuffer.get()) + mDecompressBufferUsed;
michael@0 1144 if (lastHeaderByte < nvpair)
michael@0 1145 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1146
michael@0 1147 do {
michael@0 1148 uint32_t numPairs = NetworkEndian::readUint32(nvpair - 1 * sizeof(uint32_t));
michael@0 1149
michael@0 1150 for (uint32_t index = 0; index < numPairs; ++index) {
michael@0 1151 if (lastHeaderByte < nvpair + 4)
michael@0 1152 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1153
michael@0 1154 uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
michael@0 1155 (nvpair[2] << 8) + nvpair[3];
michael@0 1156 if (lastHeaderByte < nvpair + 4 + nameLen)
michael@0 1157 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1158
michael@0 1159 nsDependentCSubstring nameString =
michael@0 1160 Substring(reinterpret_cast<const char *>(nvpair) + 4,
michael@0 1161 reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
michael@0 1162
michael@0 1163 if (lastHeaderByte < nvpair + 8 + nameLen)
michael@0 1164 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1165
michael@0 1166 // Look for illegal characters in the nameString.
michael@0 1167 // This includes upper case characters and nulls (as they will
michael@0 1168 // break the fixup-nulls-in-value-string algorithm)
michael@0 1169 // Look for upper case characters in the name. They are illegal.
michael@0 1170 for (char *cPtr = nameString.BeginWriting();
michael@0 1171 cPtr && cPtr < nameString.EndWriting();
michael@0 1172 ++cPtr) {
michael@0 1173 if (*cPtr <= 'Z' && *cPtr >= 'A') {
michael@0 1174 nsCString toLog(nameString);
michael@0 1175
michael@0 1176 LOG3(("SpdyStream3::ConvertHeaders session=%p stream=%p "
michael@0 1177 "upper case response header found. [%s]\n",
michael@0 1178 mSession, this, toLog.get()));
michael@0 1179
michael@0 1180 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1181 }
michael@0 1182
michael@0 1183 // check for null characters
michael@0 1184 if (*cPtr == '\0')
michael@0 1185 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1186 }
michael@0 1187
michael@0 1188 // HTTP Chunked responses are not legal over spdy. We do not need
michael@0 1189 // to look for chunked specifically because it is the only HTTP
michael@0 1190 // allowed default encoding and we did not negotiate further encodings
michael@0 1191 // via TE
michael@0 1192 if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
michael@0 1193 LOG3(("SpdyStream3::ConvertHeaders session=%p stream=%p "
michael@0 1194 "transfer-encoding found. Chunked is invalid and no TE sent.",
michael@0 1195 mSession, this));
michael@0 1196
michael@0 1197 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1198 }
michael@0 1199
michael@0 1200 uint32_t valueLen =
michael@0 1201 (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
michael@0 1202 (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen];
michael@0 1203
michael@0 1204 if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
michael@0 1205 return NS_ERROR_ILLEGAL_VALUE;
michael@0 1206
michael@0 1207 // spdy transport level headers shouldn't be gatewayed into http/1
michael@0 1208 if (!nameString.IsEmpty() && nameString[0] != ':' &&
michael@0 1209 !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
michael@0 1210 !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
michael@0 1211 nsDependentCSubstring valueString =
michael@0 1212 Substring(reinterpret_cast<const char *>(nvpair) + 8 + nameLen,
michael@0 1213 reinterpret_cast<const char *>(nvpair) + 8 + nameLen +
michael@0 1214 valueLen);
michael@0 1215
michael@0 1216 aHeadersOut.Append(nameString);
michael@0 1217 aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
michael@0 1218
michael@0 1219 // expand nullptr bytes in the value string
michael@0 1220 for (char *cPtr = valueString.BeginWriting();
michael@0 1221 cPtr && cPtr < valueString.EndWriting();
michael@0 1222 ++cPtr) {
michael@0 1223 if (*cPtr != 0) {
michael@0 1224 aHeadersOut.Append(*cPtr);
michael@0 1225 continue;
michael@0 1226 }
michael@0 1227
michael@0 1228 // NULLs are really "\r\nhdr: "
michael@0 1229 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
michael@0 1230 aHeadersOut.Append(nameString);
michael@0 1231 aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
michael@0 1232 }
michael@0 1233
michael@0 1234 aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
michael@0 1235 }
michael@0 1236 // move to the next name/value pair in this block
michael@0 1237 nvpair += 8 + nameLen + valueLen;
michael@0 1238 }
michael@0 1239
michael@0 1240 // move to the next name/value header block (if there is one) - the
michael@0 1241 // first pair is offset 4 bytes into it
michael@0 1242 nvpair += 4;
michael@0 1243 } while (lastHeaderByte >= nvpair);
michael@0 1244
michael@0 1245 aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: 3\r\n\r\n"));
michael@0 1246 LOG (("decoded response headers are:\n%s",
michael@0 1247 aHeadersOut.BeginReading()));
michael@0 1248
michael@0 1249 // The spdy formatted buffer isnt needed anymore - free it up
michael@0 1250 mDecompressBuffer = nullptr;
michael@0 1251 mDecompressBufferSize = 0;
michael@0 1252 mDecompressBufferUsed = 0;
michael@0 1253
michael@0 1254 return NS_OK;
michael@0 1255 }
michael@0 1256
michael@0 1257 void
michael@0 1258 SpdyStream3::ExecuteCompress(uint32_t flushMode)
michael@0 1259 {
michael@0 1260 // Expect mZlib->avail_in and mZlib->next_in to be set.
michael@0 1261 // Append the compressed version of next_in to mTxInlineFrame
michael@0 1262
michael@0 1263 do
michael@0 1264 {
michael@0 1265 uint32_t avail = mTxInlineFrameSize - mTxInlineFrameUsed;
michael@0 1266 if (avail < 1) {
michael@0 1267 SpdySession3::EnsureBuffer(mTxInlineFrame,
michael@0 1268 mTxInlineFrameSize + 2000,
michael@0 1269 mTxInlineFrameUsed,
michael@0 1270 mTxInlineFrameSize);
michael@0 1271 avail = mTxInlineFrameSize - mTxInlineFrameUsed;
michael@0 1272 }
michael@0 1273
michael@0 1274 mZlib->next_out = mTxInlineFrame + mTxInlineFrameUsed;
michael@0 1275 mZlib->avail_out = avail;
michael@0 1276 deflate(mZlib, flushMode);
michael@0 1277 mTxInlineFrameUsed += avail - mZlib->avail_out;
michael@0 1278 } while (mZlib->avail_in > 0 || !mZlib->avail_out);
michael@0 1279 }
michael@0 1280
michael@0 1281 void
michael@0 1282 SpdyStream3::CompressToFrame(uint32_t data)
michael@0 1283 {
michael@0 1284 // convert the data to 4 byte network byte order and write that
michael@0 1285 // to the compressed stream
michael@0 1286 unsigned char databuf[sizeof(data)];
michael@0 1287 NetworkEndian::writeUint32(databuf, data);
michael@0 1288
michael@0 1289 mZlib->next_in = databuf;
michael@0 1290 mZlib->avail_in = sizeof(databuf);
michael@0 1291 ExecuteCompress(Z_NO_FLUSH);
michael@0 1292 }
michael@0 1293
michael@0 1294
michael@0 1295 void
michael@0 1296 SpdyStream3::CompressToFrame(const char *data, uint32_t len)
michael@0 1297 {
michael@0 1298 // Format calls for a network ordered 32 bit length
michael@0 1299 // followed by the utf8 string
michael@0 1300
michael@0 1301 unsigned char lenbuf[sizeof(len)];
michael@0 1302 NetworkEndian::writeUint32(lenbuf, len);
michael@0 1303
michael@0 1304 // write out the length
michael@0 1305 mZlib->next_in = lenbuf;
michael@0 1306 mZlib->avail_in = sizeof(lenbuf);
michael@0 1307 ExecuteCompress(Z_NO_FLUSH);
michael@0 1308
michael@0 1309 // write out the data
michael@0 1310 mZlib->next_in = (unsigned char *)data;
michael@0 1311 mZlib->avail_in = len;
michael@0 1312 ExecuteCompress(Z_NO_FLUSH);
michael@0 1313 }
michael@0 1314
michael@0 1315 void
michael@0 1316 SpdyStream3::CompressFlushFrame()
michael@0 1317 {
michael@0 1318 mZlib->next_in = (unsigned char *) "";
michael@0 1319 mZlib->avail_in = 0;
michael@0 1320 ExecuteCompress(Z_SYNC_FLUSH);
michael@0 1321 }
michael@0 1322
michael@0 1323 void
michael@0 1324 SpdyStream3::Close(nsresult reason)
michael@0 1325 {
michael@0 1326 mTransaction->Close(reason);
michael@0 1327 }
michael@0 1328
michael@0 1329 void
michael@0 1330 SpdyStream3::UpdateRemoteWindow(int32_t delta)
michael@0 1331 {
michael@0 1332 int64_t oldRemoteWindow = mRemoteWindow;
michael@0 1333 mRemoteWindow += delta;
michael@0 1334 if (oldRemoteWindow <= 0 && mRemoteWindow > 0) {
michael@0 1335 // the window has been opened :)
michael@0 1336 mSession->TransactionHasDataToWrite(this);
michael@0 1337 }
michael@0 1338 }
michael@0 1339
michael@0 1340 //-----------------------------------------------------------------------------
michael@0 1341 // nsAHttpSegmentReader
michael@0 1342 //-----------------------------------------------------------------------------
michael@0 1343
michael@0 1344 nsresult
michael@0 1345 SpdyStream3::OnReadSegment(const char *buf,
michael@0 1346 uint32_t count,
michael@0 1347 uint32_t *countRead)
michael@0 1348 {
michael@0 1349 LOG3(("SpdyStream3::OnReadSegment %p count=%d state=%x",
michael@0 1350 this, count, mUpstreamState));
michael@0 1351
michael@0 1352 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 1353 MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader");
michael@0 1354
michael@0 1355 nsresult rv = NS_ERROR_UNEXPECTED;
michael@0 1356 uint32_t dataLength;
michael@0 1357
michael@0 1358 switch (mUpstreamState) {
michael@0 1359 case GENERATING_SYN_STREAM:
michael@0 1360 // The buffer is the HTTP request stream, including at least part of the
michael@0 1361 // HTTP request header. This state's job is to build a SYN_STREAM frame
michael@0 1362 // from the header information. count is the number of http bytes available
michael@0 1363 // (which may include more than the header), and in countRead we return
michael@0 1364 // the number of those bytes that we consume (i.e. the portion that are
michael@0 1365 // header bytes)
michael@0 1366
michael@0 1367 rv = ParseHttpRequestHeaders(buf, count, countRead);
michael@0 1368 if (NS_FAILED(rv))
michael@0 1369 return rv;
michael@0 1370 LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
michael@0 1371 this, *countRead, count, mSynFrameComplete));
michael@0 1372 if (mSynFrameComplete) {
michael@0 1373 AdjustInitialWindow();
michael@0 1374 rv = TransmitFrame(nullptr, nullptr, true);
michael@0 1375 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
michael@0 1376 // this can't happen
michael@0 1377 MOZ_ASSERT(false, "Transmit Frame SYN_FRAME must at least buffer data");
michael@0 1378 rv = NS_ERROR_UNEXPECTED;
michael@0 1379 }
michael@0 1380
michael@0 1381 ChangeState(GENERATING_REQUEST_BODY);
michael@0 1382 break;
michael@0 1383 }
michael@0 1384 MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data");
michael@0 1385 break;
michael@0 1386
michael@0 1387 case GENERATING_REQUEST_BODY:
michael@0 1388 if (mRemoteWindow <= 0) {
michael@0 1389 *countRead = 0;
michael@0 1390 LOG3(("SpdyStream3 this=%p, id 0x%X request body suspended because "
michael@0 1391 "remote window is %d.\n", this, mStreamID, mRemoteWindow));
michael@0 1392 mBlockedOnRwin = true;
michael@0 1393 return NS_BASE_STREAM_WOULD_BLOCK;
michael@0 1394 }
michael@0 1395 mBlockedOnRwin = false;
michael@0 1396
michael@0 1397 dataLength = std::min(count, mChunkSize);
michael@0 1398
michael@0 1399 if (dataLength > mRemoteWindow)
michael@0 1400 dataLength = static_cast<uint32_t>(mRemoteWindow);
michael@0 1401
michael@0 1402 LOG3(("SpdyStream3 this=%p id 0x%X remote window is %d. Chunk is %d\n",
michael@0 1403 this, mStreamID, mRemoteWindow, dataLength));
michael@0 1404 mRemoteWindow -= dataLength;
michael@0 1405
michael@0 1406 LOG3(("SpdyStream3 %p id %x request len remaining %d, "
michael@0 1407 "count avail %d, chunk used %d",
michael@0 1408 this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
michael@0 1409 if (dataLength > mRequestBodyLenRemaining)
michael@0 1410 return NS_ERROR_UNEXPECTED;
michael@0 1411 mRequestBodyLenRemaining -= dataLength;
michael@0 1412 GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
michael@0 1413 ChangeState(SENDING_REQUEST_BODY);
michael@0 1414 // NO BREAK
michael@0 1415
michael@0 1416 case SENDING_REQUEST_BODY:
michael@0 1417 MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
michael@0 1418 rv = TransmitFrame(buf, countRead, false);
michael@0 1419 MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
michael@0 1420 "Transmit Frame should be all or nothing");
michael@0 1421
michael@0 1422 LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
michael@0 1423 "Header is %d Body is %d.",
michael@0 1424 rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
michael@0 1425
michael@0 1426 // normalize a partial write with a WOULD_BLOCK into just a partial write
michael@0 1427 // as some code will take WOULD_BLOCK to mean an error with nothing
michael@0 1428 // written (e.g. nsHttpTransaction::ReadRequestSegment()
michael@0 1429 if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
michael@0 1430 rv = NS_OK;
michael@0 1431
michael@0 1432 // If that frame was all sent, look for another one
michael@0 1433 if (!mTxInlineFrameUsed)
michael@0 1434 ChangeState(GENERATING_REQUEST_BODY);
michael@0 1435 break;
michael@0 1436
michael@0 1437 case SENDING_FIN_STREAM:
michael@0 1438 MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment");
michael@0 1439 break;
michael@0 1440
michael@0 1441 default:
michael@0 1442 MOZ_ASSERT(false, "SpdyStream3::OnReadSegment non-write state");
michael@0 1443 break;
michael@0 1444 }
michael@0 1445
michael@0 1446 return rv;
michael@0 1447 }
michael@0 1448
michael@0 1449 //-----------------------------------------------------------------------------
michael@0 1450 // nsAHttpSegmentWriter
michael@0 1451 //-----------------------------------------------------------------------------
michael@0 1452
michael@0 1453 nsresult
michael@0 1454 SpdyStream3::OnWriteSegment(char *buf,
michael@0 1455 uint32_t count,
michael@0 1456 uint32_t *countWritten)
michael@0 1457 {
michael@0 1458 LOG3(("SpdyStream3::OnWriteSegment %p count=%d state=%x 0x%X\n",
michael@0 1459 this, count, mUpstreamState, mStreamID));
michael@0 1460
michael@0 1461 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
michael@0 1462 MOZ_ASSERT(mSegmentWriter);
michael@0 1463
michael@0 1464 if (!mPushSource)
michael@0 1465 return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
michael@0 1466
michael@0 1467 nsresult rv;
michael@0 1468 rv = mPushSource->GetBufferedData(buf, count, countWritten);
michael@0 1469 if (NS_FAILED(rv))
michael@0 1470 return rv;
michael@0 1471
michael@0 1472 mSession->ConnectPushedStream(this);
michael@0 1473 return NS_OK;
michael@0 1474 }
michael@0 1475
michael@0 1476 } // namespace mozilla::net
michael@0 1477 } // namespace mozilla
michael@0 1478

mercurial