netwerk/protocol/http/SpdyStream31.cpp

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

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

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

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

mercurial