netwerk/protocol/http/SpdyStream31.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/http/SpdyStream31.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1502 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set sw=2 ts=8 et tw=80 : */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +// HttpLog.h should generally be included first
    1.11 +#include "HttpLog.h"
    1.12 +
    1.13 +// Log on level :5, instead of default :4.
    1.14 +#undef LOG
    1.15 +#define LOG(args) LOG5(args)
    1.16 +#undef LOG_ENABLED
    1.17 +#define LOG_ENABLED() LOG5_ENABLED()
    1.18 +
    1.19 +#include "mozilla/Telemetry.h"
    1.20 +#include "nsAlgorithm.h"
    1.21 +#include "nsHttp.h"
    1.22 +#include "nsHttpHandler.h"
    1.23 +#include "nsHttpRequestHead.h"
    1.24 +#include "nsISocketTransport.h"
    1.25 +#include "nsISupportsPriority.h"
    1.26 +#include "prnetdb.h"
    1.27 +#include "SpdyPush31.h"
    1.28 +#include "SpdySession31.h"
    1.29 +#include "SpdyStream31.h"
    1.30 +
    1.31 +#include <algorithm>
    1.32 +
    1.33 +#ifdef DEBUG
    1.34 +// defined by the socket transport service while active
    1.35 +extern PRThread *gSocketThread;
    1.36 +#endif
    1.37 +
    1.38 +namespace mozilla {
    1.39 +namespace net {
    1.40 +
    1.41 +SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction,
    1.42 +                           SpdySession31 *spdySession,
    1.43 +                           int32_t priority)
    1.44 +  : mStreamID(0),
    1.45 +  mSession(spdySession),
    1.46 +  mUpstreamState(GENERATING_SYN_STREAM),
    1.47 +  mSynFrameComplete(0),
    1.48 +  mSentFinOnData(0),
    1.49 +  mTransaction(httpTransaction),
    1.50 +  mSocketTransport(spdySession->SocketTransport()),
    1.51 +  mSegmentReader(nullptr),
    1.52 +  mSegmentWriter(nullptr),
    1.53 +  mChunkSize(spdySession->SendingChunkSize()),
    1.54 +  mRequestBlockedOnRead(0),
    1.55 +  mRecvdFin(0),
    1.56 +  mFullyOpen(0),
    1.57 +  mSentWaitingFor(0),
    1.58 +  mReceivedData(0),
    1.59 +  mSetTCPSocketBuffer(0),
    1.60 +  mTxInlineFrameSize(SpdySession31::kDefaultBufferSize),
    1.61 +  mTxInlineFrameUsed(0),
    1.62 +  mTxStreamFrameSize(0),
    1.63 +  mZlib(spdySession->UpstreamZlib()),
    1.64 +  mDecompressBufferSize(SpdySession31::kDefaultBufferSize),
    1.65 +  mDecompressBufferUsed(0),
    1.66 +  mDecompressedBytes(0),
    1.67 +  mRequestBodyLenRemaining(0),
    1.68 +  mPriority(priority),
    1.69 +  mLocalUnacked(0),
    1.70 +  mBlockedOnRwin(false),
    1.71 +  mTotalSent(0),
    1.72 +  mTotalRead(0),
    1.73 +  mPushSource(nullptr)
    1.74 +{
    1.75 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
    1.76 +
    1.77 +  LOG3(("SpdyStream31::SpdyStream31 %p", this));
    1.78 +
    1.79 +  mRemoteWindow = spdySession->GetServerInitialStreamWindow();
    1.80 +  mLocalWindow = spdySession->PushAllowance();
    1.81 +
    1.82 +  mTxInlineFrame = new uint8_t[mTxInlineFrameSize];
    1.83 +  mDecompressBuffer = new char[mDecompressBufferSize];
    1.84 +}
    1.85 +
    1.86 +SpdyStream31::~SpdyStream31()
    1.87 +{
    1.88 +  mStreamID = SpdySession31::kDeadStreamID;
    1.89 +}
    1.90 +
    1.91 +// ReadSegments() is used to write data down the socket. Generally, HTTP
    1.92 +// request data is pulled from the approriate transaction and
    1.93 +// converted to SPDY data. Sometimes control data like a window-update is
    1.94 +// generated instead.
    1.95 +
    1.96 +nsresult
    1.97 +SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader,
    1.98 +                           uint32_t count,
    1.99 +                           uint32_t *countRead)
   1.100 +{
   1.101 +  LOG3(("SpdyStream31 %p ReadSegments reader=%p count=%d state=%x",
   1.102 +        this, reader, count, mUpstreamState));
   1.103 +
   1.104 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.105 +
   1.106 +  nsresult rv = NS_ERROR_UNEXPECTED;
   1.107 +  mRequestBlockedOnRead = 0;
   1.108 +
   1.109 +  // avoid runt chunks if possible by anticipating
   1.110 +  // full data frames
   1.111 +  if (count > (mChunkSize + 8)) {
   1.112 +    uint32_t numchunks = count / (mChunkSize + 8);
   1.113 +    count = numchunks * (mChunkSize + 8);
   1.114 +  }
   1.115 +
   1.116 +  switch (mUpstreamState) {
   1.117 +  case GENERATING_SYN_STREAM:
   1.118 +  case GENERATING_REQUEST_BODY:
   1.119 +  case SENDING_REQUEST_BODY:
   1.120 +    // Call into the HTTP Transaction to generate the HTTP request
   1.121 +    // stream. That stream will show up in OnReadSegment().
   1.122 +    mSegmentReader = reader;
   1.123 +    rv = mTransaction->ReadSegments(this, count, countRead);
   1.124 +    mSegmentReader = nullptr;
   1.125 +
   1.126 +    // Check to see if the transaction's request could be written out now.
   1.127 +    // If not, mark the stream for callback when writing can proceed.
   1.128 +    if (NS_SUCCEEDED(rv) &&
   1.129 +        mUpstreamState == GENERATING_SYN_STREAM &&
   1.130 +        !mSynFrameComplete)
   1.131 +      mSession->TransactionHasDataToWrite(this);
   1.132 +
   1.133 +    // mTxinlineFrameUsed represents any queued un-sent frame. It might
   1.134 +    // be 0 if there is no such frame, which is not a gurantee that we
   1.135 +    // don't have more request body to send - just that any data that was
   1.136 +    // sent comprised a complete SPDY frame. Likewise, a non 0 value is
   1.137 +    // a queued, but complete, spdy frame length.
   1.138 +
   1.139 +    // Mark that we are blocked on read if the http transaction needs to
   1.140 +    // provide more of the request message body and there is nothing queued
   1.141 +    // for writing
   1.142 +    if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
   1.143 +      mRequestBlockedOnRead = 1;
   1.144 +
   1.145 +    // If the sending flow control window is open (!mBlockedOnRwin) then
   1.146 +    // continue sending the request
   1.147 +    if (!mBlockedOnRwin &&
   1.148 +        !mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
   1.149 +      LOG3(("SpdyStream31::ReadSegments %p 0x%X: Sending request data complete, "
   1.150 +            "mUpstreamState=%x",this, mStreamID, mUpstreamState));
   1.151 +      if (mSentFinOnData) {
   1.152 +        ChangeState(UPSTREAM_COMPLETE);
   1.153 +      }
   1.154 +      else {
   1.155 +        GenerateDataFrameHeader(0, true);
   1.156 +        ChangeState(SENDING_FIN_STREAM);
   1.157 +        mSession->TransactionHasDataToWrite(this);
   1.158 +        rv = NS_BASE_STREAM_WOULD_BLOCK;
   1.159 +      }
   1.160 +    }
   1.161 +    break;
   1.162 +
   1.163 +  case SENDING_FIN_STREAM:
   1.164 +    // We were trying to send the FIN-STREAM but were blocked from
   1.165 +    // sending it out - try again.
   1.166 +    if (!mSentFinOnData) {
   1.167 +      mSegmentReader = reader;
   1.168 +      rv = TransmitFrame(nullptr, nullptr, false);
   1.169 +      mSegmentReader = nullptr;
   1.170 +      MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
   1.171 +                 "Transmit Frame should be all or nothing");
   1.172 +      if (NS_SUCCEEDED(rv))
   1.173 +        ChangeState(UPSTREAM_COMPLETE);
   1.174 +    }
   1.175 +    else {
   1.176 +      rv = NS_OK;
   1.177 +      mTxInlineFrameUsed = 0;         // cancel fin data packet
   1.178 +      ChangeState(UPSTREAM_COMPLETE);
   1.179 +    }
   1.180 +
   1.181 +    *countRead = 0;
   1.182 +
   1.183 +    // don't change OK to WOULD BLOCK. we are really done sending if OK
   1.184 +    break;
   1.185 +
   1.186 +  case UPSTREAM_COMPLETE:
   1.187 +    *countRead = 0;
   1.188 +    rv = NS_OK;
   1.189 +    break;
   1.190 +
   1.191 +  default:
   1.192 +    MOZ_ASSERT(false, "SpdyStream31::ReadSegments unknown state");
   1.193 +    break;
   1.194 +  }
   1.195 +
   1.196 +  return rv;
   1.197 +}
   1.198 +
   1.199 +// WriteSegments() is used to read data off the socket. Generally this is
   1.200 +// just the SPDY frame header and from there the appropriate SPDYStream
   1.201 +// is identified from the Stream-ID. The http transaction associated with
   1.202 +// that read then pulls in the data directly.
   1.203 +
   1.204 +nsresult
   1.205 +SpdyStream31::WriteSegments(nsAHttpSegmentWriter *writer,
   1.206 +                            uint32_t count,
   1.207 +                            uint32_t *countWritten)
   1.208 +{
   1.209 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.210 +  MOZ_ASSERT(!mSegmentWriter, "segment writer in progress");
   1.211 +
   1.212 +  LOG3(("SpdyStream31::WriteSegments %p count=%d state=%x",
   1.213 +        this, count, mUpstreamState));
   1.214 +
   1.215 +  mSegmentWriter = writer;
   1.216 +  nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
   1.217 +  mSegmentWriter = nullptr;
   1.218 +
   1.219 +  return rv;
   1.220 +}
   1.221 +
   1.222 +PLDHashOperator
   1.223 +SpdyStream31::hdrHashEnumerate(const nsACString &key,
   1.224 +                               nsAutoPtr<nsCString> &value,
   1.225 +                               void *closure)
   1.226 +{
   1.227 +  SpdyStream31 *self = static_cast<SpdyStream31 *>(closure);
   1.228 +
   1.229 +  self->CompressToFrame(key);
   1.230 +  self->CompressToFrame(value.get());
   1.231 +  return PL_DHASH_NEXT;
   1.232 +}
   1.233 +
   1.234 +void
   1.235 +SpdyStream31::CreatePushHashKey(const nsCString &scheme,
   1.236 +                                const nsCString &hostHeader,
   1.237 +                                uint64_t serial,
   1.238 +                                const nsCSubstring &pathInfo,
   1.239 +                                nsCString &outOrigin,
   1.240 +                                nsCString &outKey)
   1.241 +{
   1.242 +  outOrigin = scheme;
   1.243 +  outOrigin.Append(NS_LITERAL_CSTRING("://"));
   1.244 +  outOrigin.Append(hostHeader);
   1.245 +
   1.246 +  outKey = outOrigin;
   1.247 +  outKey.Append(NS_LITERAL_CSTRING("/[spdy3_1."));
   1.248 +  outKey.AppendInt(serial);
   1.249 +  outKey.Append(NS_LITERAL_CSTRING("]"));
   1.250 +  outKey.Append(pathInfo);
   1.251 +}
   1.252 +
   1.253 +
   1.254 +nsresult
   1.255 +SpdyStream31::ParseHttpRequestHeaders(const char *buf,
   1.256 +                                      uint32_t avail,
   1.257 +                                      uint32_t *countUsed)
   1.258 +{
   1.259 +  // Returns NS_OK even if the headers are incomplete
   1.260 +  // set mSynFrameComplete flag if they are complete
   1.261 +
   1.262 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.263 +  MOZ_ASSERT(mUpstreamState == GENERATING_SYN_STREAM);
   1.264 +
   1.265 +  LOG3(("SpdyStream31::ParseHttpRequestHeaders %p avail=%d state=%x",
   1.266 +        this, avail, mUpstreamState));
   1.267 +
   1.268 +  mFlatHttpRequestHeaders.Append(buf, avail);
   1.269 +
   1.270 +  // We can use the simple double crlf because firefox is the
   1.271 +  // only client we are parsing
   1.272 +  int32_t endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
   1.273 +
   1.274 +  if (endHeader == kNotFound) {
   1.275 +    // We don't have all the headers yet
   1.276 +    LOG3(("SpdyStream31::ParseHttpRequestHeaders %p "
   1.277 +          "Need more header bytes. Len = %d",
   1.278 +          this, mFlatHttpRequestHeaders.Length()));
   1.279 +    *countUsed = avail;
   1.280 +    return NS_OK;
   1.281 +  }
   1.282 +
   1.283 +  // We have recvd all the headers, trim the local
   1.284 +  // buffer of the final empty line, and set countUsed to reflect
   1.285 +  // the whole header has been consumed.
   1.286 +  uint32_t oldLen = mFlatHttpRequestHeaders.Length();
   1.287 +  mFlatHttpRequestHeaders.SetLength(endHeader + 2);
   1.288 +  *countUsed = avail - (oldLen - endHeader) + 4;
   1.289 +  mSynFrameComplete = 1;
   1.290 +
   1.291 +  nsCString hostHeader;
   1.292 +  nsCString hashkey;
   1.293 +  mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
   1.294 +
   1.295 +  CreatePushHashKey(NS_LITERAL_CSTRING("https"),
   1.296 +                    hostHeader, mSession->Serial(),
   1.297 +                    mTransaction->RequestHead()->RequestURI(),
   1.298 +                    mOrigin, hashkey);
   1.299 +
   1.300 +  // check the push cache for GET
   1.301 +  if (mTransaction->RequestHead()->IsGet()) {
   1.302 +    // from :scheme, :host, :path
   1.303 +    nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
   1.304 +    SpdyPushCache *cache = nullptr;
   1.305 +    if (loadGroupCI)
   1.306 +      loadGroupCI->GetSpdyPushCache(&cache);
   1.307 +
   1.308 +    SpdyPushedStream31 *pushedStream = nullptr;
   1.309 +    // we remove the pushedstream from the push cache so that
   1.310 +    // it will not be used for another GET. This does not destroy the
   1.311 +    // stream itself - that is done when the transactionhash is done with it.
   1.312 +    if (cache)
   1.313 +      pushedStream = cache->RemovePushedStreamSpdy31(hashkey);
   1.314 +
   1.315 +    if (pushedStream) {
   1.316 +      LOG3(("Pushed Stream Match located id=0x%X key=%s\n",
   1.317 +            pushedStream->StreamID(), hashkey.get()));
   1.318 +      pushedStream->SetConsumerStream(this);
   1.319 +      mPushSource = pushedStream;
   1.320 +      mSentFinOnData = 1;
   1.321 +
   1.322 +      // This stream has been activated (and thus counts against the concurrency
   1.323 +      // limit intentionally), but will not be registered via
   1.324 +      // RegisterStreamID (below) because of the push match. Therefore the
   1.325 +      // concurrency sempahore needs to be balanced.
   1.326 +      mSession->DecrementConcurrent(this);
   1.327 +
   1.328 +      // There is probably pushed data buffered so trigger a read manually
   1.329 +      // as we can't rely on future network events to do it
   1.330 +      mSession->ConnectPushedStream(this);
   1.331 +      return NS_OK;
   1.332 +    }
   1.333 +  }
   1.334 +
   1.335 +  // It is now OK to assign a streamID that we are assured will
   1.336 +  // be monotonically increasing amongst syn-streams on this
   1.337 +  // session
   1.338 +  mStreamID = mSession->RegisterStreamID(this);
   1.339 +  MOZ_ASSERT(mStreamID & 1, "Spdy Stream Channel ID must be odd");
   1.340 +
   1.341 +  if (mStreamID >= 0x80000000) {
   1.342 +    // streamID must fit in 31 bits. This is theoretically possible
   1.343 +    // because stream ID assignment is asynchronous to stream creation
   1.344 +    // because of the protocol requirement that the ID in syn-stream
   1.345 +    // be monotonically increasing. In reality this is really not possible
   1.346 +    // because new streams stop being added to a session with 0x10000000 / 2
   1.347 +    // IDs still available and no race condition is going to bridge that gap,
   1.348 +    // so we can be comfortable on just erroring out for correctness in that
   1.349 +    // case.
   1.350 +    LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
   1.351 +    return NS_ERROR_UNEXPECTED;
   1.352 +  }
   1.353 +
   1.354 +  // Now we need to convert the flat http headers into a set
   1.355 +  // of SPDY headers..  writing to mTxInlineFrame{sz}
   1.356 +
   1.357 +  mTxInlineFrame[0] = SpdySession31::kFlag_Control;
   1.358 +  mTxInlineFrame[1] = SpdySession31::kVersion;
   1.359 +  mTxInlineFrame[2] = 0;
   1.360 +  mTxInlineFrame[3] = SpdySession31::CONTROL_TYPE_SYN_STREAM;
   1.361 +  // 4 to 7 are length and flags, we'll fill that in later
   1.362 +
   1.363 +  uint32_t networkOrderID = PR_htonl(mStreamID);
   1.364 +  memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
   1.365 +
   1.366 +  // this is the associated-to field, which is not used sending
   1.367 +  // from the client in the http binding
   1.368 +  memset (mTxInlineFrame + 12, 0, 4);
   1.369 +
   1.370 +  // Priority flags are the E0 mask of byte 16.
   1.371 +  // 0 is highest priority, 7 is lowest.
   1.372 +  // The other 5 bits of byte 16 are unused.
   1.373 +
   1.374 +  if (mPriority >= nsISupportsPriority::PRIORITY_LOWEST)
   1.375 +    mTxInlineFrame[16] = 7 << 5;
   1.376 +  else if (mPriority <= nsISupportsPriority::PRIORITY_HIGHEST)
   1.377 +    mTxInlineFrame[16] = 0 << 5;
   1.378 +  else {
   1.379 +    // The priority mapping relies on the unfiltered ranged to be
   1.380 +    // between -20 .. +20
   1.381 +    PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_LOWEST == 20);
   1.382 +    PR_STATIC_ASSERT(nsISupportsPriority::PRIORITY_HIGHEST == -20);
   1.383 +
   1.384 +    // Add one to the priority so that values such as -10 and -11
   1.385 +    // get different spdy priorities - this appears to be an important
   1.386 +    // breaking line in the priorities content assigns to
   1.387 +    // transactions.
   1.388 +    uint8_t calculatedPriority = 3 + ((mPriority + 1) / 5);
   1.389 +    MOZ_ASSERT (!(calculatedPriority & 0xf8),
   1.390 +                "Calculated Priority Out Of Range");
   1.391 +    mTxInlineFrame[16] = calculatedPriority << 5;
   1.392 +  }
   1.393 +
   1.394 +  // The client cert "slot". Right now we don't send client certs
   1.395 +  mTxInlineFrame[17] = 0;
   1.396 +
   1.397 +  nsCString versionHeader;
   1.398 +  if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
   1.399 +    versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
   1.400 +  else
   1.401 +    versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
   1.402 +
   1.403 +  // use mRequestHead() to get a sense of how big to make the hash,
   1.404 +  // even though we are parsing the actual text stream because
   1.405 +  // it is legit to append headers.
   1.406 +  nsClassHashtable<nsCStringHashKey, nsCString>
   1.407 +    hdrHash(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
   1.408 +
   1.409 +  const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
   1.410 +
   1.411 +  // need to hash all the headers together to remove duplicates, special
   1.412 +  // headers, etc..
   1.413 +
   1.414 +  int32_t crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
   1.415 +  while (true) {
   1.416 +    int32_t startIndex = crlfIndex + 2;
   1.417 +
   1.418 +    crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
   1.419 +    if (crlfIndex == -1)
   1.420 +      break;
   1.421 +
   1.422 +    int32_t colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
   1.423 +                                                      crlfIndex - startIndex);
   1.424 +    if (colonIndex == -1)
   1.425 +      break;
   1.426 +
   1.427 +    nsDependentCSubstring name = Substring(beginBuffer + startIndex,
   1.428 +                                           beginBuffer + colonIndex);
   1.429 +    // all header names are lower case in spdy
   1.430 +    ToLowerCase(name);
   1.431 +
   1.432 +    // exclusions.. mostly from 3.2.1
   1.433 +    if (name.Equals("connection") ||
   1.434 +        name.Equals("keep-alive") ||
   1.435 +        name.Equals("host") ||
   1.436 +        name.Equals("accept-encoding") ||
   1.437 +        name.Equals("te") ||
   1.438 +        name.Equals("transfer-encoding"))
   1.439 +      continue;
   1.440 +
   1.441 +    nsCString *val = hdrHash.Get(name);
   1.442 +    if (!val) {
   1.443 +      val = new nsCString();
   1.444 +      hdrHash.Put(name, val);
   1.445 +    }
   1.446 +
   1.447 +    int32_t valueIndex = colonIndex + 1;
   1.448 +    while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
   1.449 +      ++valueIndex;
   1.450 +
   1.451 +    nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
   1.452 +                                        beginBuffer + crlfIndex);
   1.453 +    if (!val->IsEmpty())
   1.454 +      val->Append(static_cast<char>(0));
   1.455 +    val->Append(v);
   1.456 +
   1.457 +    if (name.Equals("content-length")) {
   1.458 +      int64_t len;
   1.459 +      if (nsHttp::ParseInt64(val->get(), nullptr, &len))
   1.460 +        mRequestBodyLenRemaining = len;
   1.461 +    }
   1.462 +  }
   1.463 +
   1.464 +  mTxInlineFrameUsed = 18;
   1.465 +
   1.466 +  // Do not naively log the request headers here beacuse they might
   1.467 +  // contain auth. The http transaction already logs the sanitized request
   1.468 +  // headers at this same level so it is not necessary to do so here.
   1.469 +
   1.470 +  const char *methodHeader = mTransaction->RequestHead()->Method().get();
   1.471 +
   1.472 +  // The header block length
   1.473 +  uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */
   1.474 +  CompressToFrame(count);
   1.475 +
   1.476 +  // :method, :path, :version comprise a HTTP/1 request line, so send those first
   1.477 +  // to make life easy for any gateways
   1.478 +  CompressToFrame(NS_LITERAL_CSTRING(":method"));
   1.479 +  CompressToFrame(methodHeader, strlen(methodHeader));
   1.480 +  CompressToFrame(NS_LITERAL_CSTRING(":path"));
   1.481 +  CompressToFrame(mTransaction->RequestHead()->RequestURI());
   1.482 +  CompressToFrame(NS_LITERAL_CSTRING(":version"));
   1.483 +  CompressToFrame(versionHeader);
   1.484 +
   1.485 +  CompressToFrame(NS_LITERAL_CSTRING(":host"));
   1.486 +  CompressToFrame(hostHeader);
   1.487 +  CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
   1.488 +  CompressToFrame(NS_LITERAL_CSTRING("https"));
   1.489 +
   1.490 +  hdrHash.Enumerate(hdrHashEnumerate, this);
   1.491 +  CompressFlushFrame();
   1.492 +
   1.493 +  // 4 to 7 are length and flags, which we can now fill in
   1.494 +  (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[1] =
   1.495 +    PR_htonl(mTxInlineFrameUsed - 8);
   1.496 +
   1.497 +  MOZ_ASSERT(!mTxInlineFrame[4], "Size greater than 24 bits");
   1.498 +
   1.499 +  // Determine whether to put the fin bit on the syn stream frame or whether
   1.500 +  // to wait for a data packet to put it on.
   1.501 +
   1.502 +  if (mTransaction->RequestHead()->IsGet() ||
   1.503 +      mTransaction->RequestHead()->IsConnect() ||
   1.504 +      mTransaction->RequestHead()->IsHead()) {
   1.505 +    // for GET, CONNECT, and HEAD place the fin bit right on the
   1.506 +    // syn stream packet
   1.507 +
   1.508 +    mSentFinOnData = 1;
   1.509 +    mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
   1.510 +  }
   1.511 +  else if (mTransaction->RequestHead()->IsPost() ||
   1.512 +           mTransaction->RequestHead()->IsPut() ||
   1.513 +           mTransaction->RequestHead()->IsOptions()) {
   1.514 +    // place fin in a data frame even for 0 length messages, I've seen
   1.515 +    // the google gateway be unhappy with fin-on-syn for 0 length POST
   1.516 +  }
   1.517 +  else if (!mRequestBodyLenRemaining) {
   1.518 +    // for other HTTP extension methods, rely on the content-length
   1.519 +    // to determine whether or not to put fin on syn
   1.520 +    mSentFinOnData = 1;
   1.521 +    mTxInlineFrame[4] = SpdySession31::kFlag_Data_FIN;
   1.522 +  }
   1.523 +
   1.524 +  Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
   1.525 +
   1.526 +  // The size of the input headers is approximate
   1.527 +  uint32_t ratio =
   1.528 +    (mTxInlineFrameUsed - 18) * 100 /
   1.529 +    (11 + mTransaction->RequestHead()->RequestURI().Length() +
   1.530 +     mFlatHttpRequestHeaders.Length());
   1.531 +
   1.532 +  Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
   1.533 +  return NS_OK;
   1.534 +}
   1.535 +
   1.536 +void
   1.537 +SpdyStream31::AdjustInitialWindow()
   1.538 +{
   1.539 +  MOZ_ASSERT(mSession->PushAllowance() <= ASpdySession::kInitialRwin);
   1.540 +
   1.541 +  // The session initial_window is sized for serverpushed streams. When we
   1.542 +  // generate a client pulled stream we want to adjust the initial window
   1.543 +  // to a huge value in a pipeline with that SYN_STREAM.
   1.544 +
   1.545 +  // >0 even numbered IDs are pushed streams.
   1.546 +  // odd numbered IDs are pulled streams.
   1.547 +  // 0 is the sink for a pushed stream.
   1.548 +  SpdyStream31 *stream = this;
   1.549 +  if (!mStreamID) {
   1.550 +    MOZ_ASSERT(mPushSource);
   1.551 +    if (!mPushSource)
   1.552 +      return;
   1.553 +    stream = mPushSource;
   1.554 +    MOZ_ASSERT(stream->mStreamID);
   1.555 +    MOZ_ASSERT(!(stream->mStreamID & 1)); // is a push stream
   1.556 +
   1.557 +    // If the pushed stream has sent a FIN, there is no reason to update
   1.558 +    // the window
   1.559 +    if (stream->RecvdFin())
   1.560 +      return;
   1.561 +  }
   1.562 +
   1.563 +  // For server pushes we also want to include in the ack any data that has been
   1.564 +  // buffered but unacknowledged.
   1.565 +
   1.566 +  // mLocalUnacked is technically 64 bits, but because it can never grow larger than
   1.567 +  // our window size (which is closer to 29bits max) we know it fits comfortably in 32.
   1.568 +  // However we don't really enforce that, and track it as a 64 so that broken senders
   1.569 +  // can still interoperate. That means we have to be careful with this calculation.
   1.570 +  uint64_t toack64 = (ASpdySession::kInitialRwin - mSession->PushAllowance()) +
   1.571 +    stream->mLocalUnacked;
   1.572 +  stream->mLocalUnacked = 0;
   1.573 +  if (toack64 > 0x7fffffff) {
   1.574 +    stream->mLocalUnacked = toack64 - 0x7fffffff;
   1.575 +    toack64 = 0x7fffffff;
   1.576 +  }
   1.577 +  uint32_t toack = static_cast<uint32_t>(toack64);
   1.578 +  if (!toack)
   1.579 +    return;
   1.580 +  toack = PR_htonl(toack);
   1.581 +
   1.582 +  SpdySession31::EnsureBuffer(mTxInlineFrame,
   1.583 +                              mTxInlineFrameUsed + 16,
   1.584 +                              mTxInlineFrameUsed,
   1.585 +                              mTxInlineFrameSize);
   1.586 +
   1.587 +  unsigned char *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
   1.588 +  mTxInlineFrameUsed += 16;
   1.589 +
   1.590 +  memset(packet, 0, 8);
   1.591 +  packet[0] = SpdySession31::kFlag_Control;
   1.592 +  packet[1] = SpdySession31::kVersion;
   1.593 +  packet[3] = SpdySession31::CONTROL_TYPE_WINDOW_UPDATE;
   1.594 +  packet[7] = 8; // 8 data bytes after 8 byte header
   1.595 +
   1.596 +  uint32_t id = PR_htonl(stream->mStreamID);
   1.597 +  memcpy(packet + 8, &id, 4);
   1.598 +  memcpy(packet + 12, &toack, 4);
   1.599 +
   1.600 +  stream->mLocalWindow += PR_ntohl(toack);
   1.601 +  LOG3(("AdjustInitialwindow %p 0x%X %u\n",
   1.602 +        this, stream->mStreamID, PR_ntohl(toack)));
   1.603 +}
   1.604 +
   1.605 +void
   1.606 +SpdyStream31::UpdateTransportReadEvents(uint32_t count)
   1.607 +{
   1.608 +  mTotalRead += count;
   1.609 +
   1.610 +  mTransaction->OnTransportStatus(mSocketTransport,
   1.611 +                                  NS_NET_STATUS_RECEIVING_FROM,
   1.612 +                                  mTotalRead);
   1.613 +}
   1.614 +
   1.615 +void
   1.616 +SpdyStream31::UpdateTransportSendEvents(uint32_t count)
   1.617 +{
   1.618 +  mTotalSent += count;
   1.619 +
   1.620 +  // normally on non-windows platform we use TCP autotuning for
   1.621 +  // the socket buffers, and this works well (managing enough
   1.622 +  // buffers for BDP while conserving memory) for HTTP even when
   1.623 +  // it creates really deep queues. However this 'buffer bloat' is
   1.624 +  // a problem for spdy because it ruins the low latency properties
   1.625 +  // necessary for PING and cancel to work meaningfully.
   1.626 +  //
   1.627 +  // If this stream represents a large upload, disable autotuning for
   1.628 +  // the session and cap the send buffers by default at 128KB.
   1.629 +  // (10Mbit/sec @ 100ms)
   1.630 +  //
   1.631 +  uint32_t bufferSize = gHttpHandler->SpdySendBufferSize();
   1.632 +  if ((mTotalSent > bufferSize) && !mSetTCPSocketBuffer) {
   1.633 +    mSetTCPSocketBuffer = 1;
   1.634 +    mSocketTransport->SetSendBufferSize(bufferSize);
   1.635 +  }
   1.636 +
   1.637 +  if (mUpstreamState != SENDING_FIN_STREAM)
   1.638 +    mTransaction->OnTransportStatus(mSocketTransport,
   1.639 +                                    NS_NET_STATUS_SENDING_TO,
   1.640 +                                    mTotalSent);
   1.641 +
   1.642 +  if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
   1.643 +    mSentWaitingFor = 1;
   1.644 +    mTransaction->OnTransportStatus(mSocketTransport,
   1.645 +                                    NS_NET_STATUS_WAITING_FOR,
   1.646 +                                    0);
   1.647 +  }
   1.648 +}
   1.649 +
   1.650 +nsresult
   1.651 +SpdyStream31::TransmitFrame(const char *buf,
   1.652 +                            uint32_t *countUsed,
   1.653 +                            bool forceCommitment)
   1.654 +{
   1.655 +  // If TransmitFrame returns SUCCESS than all the data is sent (or at least
   1.656 +  // buffered at the session level), if it returns WOULD_BLOCK then none of
   1.657 +  // the data is sent.
   1.658 +
   1.659 +  // You can call this function with no data and no out parameter in order to
   1.660 +  // flush internal buffers that were previously blocked on writing. You can
   1.661 +  // of course feed new data to it as well.
   1.662 +
   1.663 +  LOG3(("SpdyStream31::TransmitFrame %p inline=%d stream=%d",
   1.664 +        this, mTxInlineFrameUsed, mTxStreamFrameSize));
   1.665 +  if (countUsed)
   1.666 +    *countUsed = 0;
   1.667 +
   1.668 +  if (!mTxInlineFrameUsed) {
   1.669 +    MOZ_ASSERT(!buf);
   1.670 +    return NS_OK;
   1.671 +  }
   1.672 +
   1.673 +  MOZ_ASSERT(mTxInlineFrameUsed, "empty stream frame in transmit");
   1.674 +  MOZ_ASSERT(mSegmentReader, "TransmitFrame with null mSegmentReader");
   1.675 +  MOZ_ASSERT((buf && countUsed) || (!buf && !countUsed),
   1.676 +             "TransmitFrame arguments inconsistent");
   1.677 +
   1.678 +  uint32_t transmittedCount;
   1.679 +  nsresult rv;
   1.680 +
   1.681 +  // In the (relatively common) event that we have a small amount of data
   1.682 +  // split between the inlineframe and the streamframe, then move the stream
   1.683 +  // data into the inlineframe via copy in order to coalesce into one write.
   1.684 +  // Given the interaction with ssl this is worth the small copy cost.
   1.685 +  if (mTxStreamFrameSize && mTxInlineFrameUsed &&
   1.686 +      mTxStreamFrameSize < SpdySession31::kDefaultBufferSize &&
   1.687 +      mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
   1.688 +    LOG3(("Coalesce Transmit"));
   1.689 +    memcpy (mTxInlineFrame + mTxInlineFrameUsed,
   1.690 +            buf, mTxStreamFrameSize);
   1.691 +    if (countUsed)
   1.692 +      *countUsed += mTxStreamFrameSize;
   1.693 +    mTxInlineFrameUsed += mTxStreamFrameSize;
   1.694 +    mTxStreamFrameSize = 0;
   1.695 +  }
   1.696 +
   1.697 +  rv =
   1.698 +    mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize + mTxInlineFrameUsed,
   1.699 +                                        forceCommitment);
   1.700 +
   1.701 +  if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1.702 +    MOZ_ASSERT(!forceCommitment, "forceCommitment with WOULD_BLOCK");
   1.703 +    mSession->TransactionHasDataToWrite(this);
   1.704 +  }
   1.705 +  if (NS_FAILED(rv))     // this will include WOULD_BLOCK
   1.706 +    return rv;
   1.707 +
   1.708 +  // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
   1.709 +  // bytes through to the SpdySession31 and then the HttpConnection which calls
   1.710 +  // the socket write function. It will accept all of the inline and stream
   1.711 +  // data because of the above 'commitment' even if it has to buffer
   1.712 +
   1.713 +  rv = mSession->BufferOutput(reinterpret_cast<char*>(mTxInlineFrame.get()),
   1.714 +                              mTxInlineFrameUsed,
   1.715 +                              &transmittedCount);
   1.716 +  LOG3(("SpdyStream31::TransmitFrame for inline BufferOutput session=%p "
   1.717 +        "stream=%p result %x len=%d",
   1.718 +        mSession, this, rv, transmittedCount));
   1.719 +
   1.720 +  MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
   1.721 +             "inconsistent inline commitment result");
   1.722 +
   1.723 +  if (NS_FAILED(rv))
   1.724 +    return rv;
   1.725 +
   1.726 +  MOZ_ASSERT(transmittedCount == mTxInlineFrameUsed,
   1.727 +             "inconsistent inline commitment count");
   1.728 +
   1.729 +  SpdySession31::LogIO(mSession, this, "Writing from Inline Buffer",
   1.730 +                       reinterpret_cast<char*>(mTxInlineFrame.get()),
   1.731 +                       transmittedCount);
   1.732 +
   1.733 +  if (mTxStreamFrameSize) {
   1.734 +    if (!buf) {
   1.735 +      // this cannot happen
   1.736 +      MOZ_ASSERT(false, "Stream transmit with null buf argument to "
   1.737 +                 "TransmitFrame()");
   1.738 +      LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
   1.739 +      return NS_ERROR_UNEXPECTED;
   1.740 +    }
   1.741 +
   1.742 +    // If there is already data buffered, just add to that to form
   1.743 +    // a single TLS Application Data Record - otherwise skip the memcpy
   1.744 +    if (mSession->AmountOfOutputBuffered()) {
   1.745 +      rv = mSession->BufferOutput(buf, mTxStreamFrameSize,
   1.746 +                                  &transmittedCount);
   1.747 +    } else {
   1.748 +      rv = mSession->OnReadSegment(buf, mTxStreamFrameSize,
   1.749 +                                   &transmittedCount);
   1.750 +    }
   1.751 +
   1.752 +    LOG3(("SpdyStream31::TransmitFrame for regular session=%p "
   1.753 +          "stream=%p result %x len=%d",
   1.754 +          mSession, this, rv, transmittedCount));
   1.755 +
   1.756 +    MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK,
   1.757 +               "inconsistent stream commitment result");
   1.758 +
   1.759 +    if (NS_FAILED(rv))
   1.760 +      return rv;
   1.761 +
   1.762 +    MOZ_ASSERT(transmittedCount == mTxStreamFrameSize,
   1.763 +               "inconsistent stream commitment count");
   1.764 +
   1.765 +    SpdySession31::LogIO(mSession, this, "Writing from Transaction Buffer",
   1.766 +                         buf, transmittedCount);
   1.767 +
   1.768 +    *countUsed += mTxStreamFrameSize;
   1.769 +  }
   1.770 +
   1.771 +  mSession->FlushOutputQueue();
   1.772 +
   1.773 +  // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
   1.774 +  UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
   1.775 +
   1.776 +  mTxInlineFrameUsed = 0;
   1.777 +  mTxStreamFrameSize = 0;
   1.778 +
   1.779 +  return NS_OK;
   1.780 +}
   1.781 +
   1.782 +void
   1.783 +SpdyStream31::ChangeState(enum stateType newState)
   1.784 +{
   1.785 +  LOG3(("SpdyStream31::ChangeState() %p from %X to %X",
   1.786 +        this, mUpstreamState, newState));
   1.787 +  mUpstreamState = newState;
   1.788 +  return;
   1.789 +}
   1.790 +
   1.791 +void
   1.792 +SpdyStream31::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
   1.793 +{
   1.794 +  LOG3(("SpdyStream31::GenerateDataFrameHeader %p len=%d last=%d",
   1.795 +        this, dataLength, lastFrame));
   1.796 +
   1.797 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.798 +  MOZ_ASSERT(!mTxInlineFrameUsed, "inline frame not empty");
   1.799 +  MOZ_ASSERT(!mTxStreamFrameSize, "stream frame not empty");
   1.800 +  MOZ_ASSERT(!(dataLength & 0xff000000), "datalength > 24 bits");
   1.801 +
   1.802 +  (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
   1.803 +  (reinterpret_cast<uint32_t *>(mTxInlineFrame.get()))[1] =
   1.804 +    PR_htonl(dataLength);
   1.805 +
   1.806 +  MOZ_ASSERT(!(mTxInlineFrame[0] & 0x80), "control bit set unexpectedly");
   1.807 +  MOZ_ASSERT(!mTxInlineFrame[4], "flag bits set unexpectedly");
   1.808 +
   1.809 +  mTxInlineFrameUsed = 8;
   1.810 +  mTxStreamFrameSize = dataLength;
   1.811 +
   1.812 +  if (lastFrame) {
   1.813 +    mTxInlineFrame[4] |= SpdySession31::kFlag_Data_FIN;
   1.814 +    if (dataLength)
   1.815 +      mSentFinOnData = 1;
   1.816 +  }
   1.817 +}
   1.818 +
   1.819 +void
   1.820 +SpdyStream31::CompressToFrame(const nsACString &str)
   1.821 +{
   1.822 +  CompressToFrame(str.BeginReading(), str.Length());
   1.823 +}
   1.824 +
   1.825 +void
   1.826 +SpdyStream31::CompressToFrame(const nsACString *str)
   1.827 +{
   1.828 +  CompressToFrame(str->BeginReading(), str->Length());
   1.829 +}
   1.830 +
   1.831 +// Dictionary taken from
   1.832 +// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3
   1.833 +
   1.834 +const unsigned char SpdyStream31::kDictionary[] = {
   1.835 +  0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,   // - - - - o p t i
   1.836 +  0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,   // o n s - - - - h
   1.837 +  0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,   // e a d - - - - p
   1.838 +  0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,   // o s t - - - - p
   1.839 +  0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,   // u t - - - - d e
   1.840 +  0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,   // l e t e - - - -
   1.841 +  0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,   // t r a c e - - -
   1.842 +  0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,   // - a c c e p t -
   1.843 +  0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,   // - - - a c c e p
   1.844 +  0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   // t - c h a r s e
   1.845 +  0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,   // t - - - - a c c
   1.846 +  0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   // e p t - e n c o
   1.847 +  0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,   // d i n g - - - -
   1.848 +  0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,   // a c c e p t - l
   1.849 +  0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,   // a n g u a g e -
   1.850 +  0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,   // - - - a c c e p
   1.851 +  0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,   // t - r a n g e s
   1.852 +  0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,   // - - - - a g e -
   1.853 +  0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,   // - - - a l l o w
   1.854 +  0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,   // - - - - a u t h
   1.855 +  0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,   // o r i z a t i o
   1.856 +  0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,   // n - - - - c a c
   1.857 +  0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,   // h e - c o n t r
   1.858 +  0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,   // o l - - - - c o
   1.859 +  0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,   // n n e c t i o n
   1.860 +  0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
   1.861 +  0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,   // e n t - b a s e
   1.862 +  0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
   1.863 +  0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,   // e n t - e n c o
   1.864 +  0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,   // d i n g - - - -
   1.865 +  0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,   // c o n t e n t -
   1.866 +  0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,   // l a n g u a g e
   1.867 +  0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,   // - - - - c o n t
   1.868 +  0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,   // e n t - l e n g
   1.869 +  0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,   // t h - - - - c o
   1.870 +  0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,   // n t e n t - l o
   1.871 +  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,   // c a t i o n - -
   1.872 +  0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   // - - c o n t e n
   1.873 +  0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,   // t - m d 5 - - -
   1.874 +  0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,   // - c o n t e n t
   1.875 +  0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,   // - r a n g e - -
   1.876 +  0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,   // - - c o n t e n
   1.877 +  0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,   // t - t y p e - -
   1.878 +  0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,   // - - d a t e - -
   1.879 +  0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,   // - - e t a g - -
   1.880 +  0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,   // - - e x p e c t
   1.881 +  0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,   // - - - - e x p i
   1.882 +  0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,   // r e s - - - - f
   1.883 +  0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,   // r o m - - - - h
   1.884 +  0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,   // o s t - - - - i
   1.885 +  0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,   // f - m a t c h -
   1.886 +  0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,   // - - - i f - m o
   1.887 +  0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,   // d i f i e d - s
   1.888 +  0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,   // i n c e - - - -
   1.889 +  0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,   // i f - n o n e -
   1.890 +  0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,   // m a t c h - - -
   1.891 +  0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,   // - i f - r a n g
   1.892 +  0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,   // e - - - - i f -
   1.893 +  0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,   // u n m o d i f i
   1.894 +  0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,   // e d - s i n c e
   1.895 +  0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,   // - - - - l a s t
   1.896 +  0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,   // - m o d i f i e
   1.897 +  0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,   // d - - - - l o c
   1.898 +  0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,   // a t i o n - - -
   1.899 +  0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,   // - m a x - f o r
   1.900 +  0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,   // w a r d s - - -
   1.901 +  0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,   // - p r a g m a -
   1.902 +  0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,   // - - - p r o x y
   1.903 +  0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,   // - a u t h e n t
   1.904 +  0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,   // i c a t e - - -
   1.905 +  0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,   // - p r o x y - a
   1.906 +  0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,   // u t h o r i z a
   1.907 +  0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,   // t i o n - - - -
   1.908 +  0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,   // r a n g e - - -
   1.909 +  0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,   // - r e f e r e r
   1.910 +  0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,   // - - - - r e t r
   1.911 +  0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,   // y - a f t e r -
   1.912 +  0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,   // - - - s e r v e
   1.913 +  0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,   // r - - - - t e -
   1.914 +  0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,   // - - - t r a i l
   1.915 +  0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,   // e r - - - - t r
   1.916 +  0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,   // a n s f e r - e
   1.917 +  0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,   // n c o d i n g -
   1.918 +  0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,   // - - - u p g r a
   1.919 +  0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,   // d e - - - - u s
   1.920 +  0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,   // e r - a g e n t
   1.921 +  0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,   // - - - - v a r y
   1.922 +  0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,   // - - - - v i a -
   1.923 +  0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,   // - - - w a r n i
   1.924 +  0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,   // n g - - - - w w
   1.925 +  0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,   // w - a u t h e n
   1.926 +  0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,   // t i c a t e - -
   1.927 +  0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,   // - - m e t h o d
   1.928 +  0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,   // - - - - g e t -
   1.929 +  0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,   // - - - s t a t u
   1.930 +  0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,   // s - - - - 2 0 0
   1.931 +  0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,   // - O K - - - - v
   1.932 +  0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,   // e r s i o n - -
   1.933 +  0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,   // - - H T T P - 1
   1.934 +  0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,   // - 1 - - - - u r
   1.935 +  0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,   // l - - - - p u b
   1.936 +  0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,   // l i c - - - - s
   1.937 +  0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,   // e t - c o o k i
   1.938 +  0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,   // e - - - - k e e
   1.939 +  0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,   // p - a l i v e -
   1.940 +  0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,   // - - - o r i g i
   1.941 +  0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,   // n 1 0 0 1 0 1 2
   1.942 +  0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,   // 0 1 2 0 2 2 0 5
   1.943 +  0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,   // 2 0 6 3 0 0 3 0
   1.944 +  0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,   // 2 3 0 3 3 0 4 3
   1.945 +  0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,   // 0 5 3 0 6 3 0 7
   1.946 +  0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,   // 4 0 2 4 0 5 4 0
   1.947 +  0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,   // 6 4 0 7 4 0 8 4
   1.948 +  0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,   // 0 9 4 1 0 4 1 1
   1.949 +  0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,   // 4 1 2 4 1 3 4 1
   1.950 +  0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,   // 4 4 1 5 4 1 6 4
   1.951 +  0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,   // 1 7 5 0 2 5 0 4
   1.952 +  0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,   // 5 0 5 2 0 3 - N
   1.953 +  0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,   // o n - A u t h o
   1.954 +  0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,   // r i t a t i v e
   1.955 +  0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,   // - I n f o r m a
   1.956 +  0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,   // t i o n 2 0 4 -
   1.957 +  0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,   // N o - C o n t e
   1.958 +  0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,   // n t 3 0 1 - M o
   1.959 +  0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,   // v e d - P e r m
   1.960 +  0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,   // a n e n t l y 4
   1.961 +  0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,   // 0 0 - B a d - R
   1.962 +  0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,   // e q u e s t 4 0
   1.963 +  0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,   // 1 - U n a u t h
   1.964 +  0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,   // o r i z e d 4 0
   1.965 +  0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,   // 3 - F o r b i d
   1.966 +  0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,   // d e n 4 0 4 - N
   1.967 +  0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,   // o t - F o u n d
   1.968 +  0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,   // 5 0 0 - I n t e
   1.969 +  0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,   // r n a l - S e r
   1.970 +  0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,   // v e r - E r r o
   1.971 +  0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,   // r 5 0 1 - N o t
   1.972 +  0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,   // - I m p l e m e
   1.973 +  0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,   // n t e d 5 0 3 -
   1.974 +  0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,   // S e r v i c e -
   1.975 +  0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,   // U n a v a i l a
   1.976 +  0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,   // b l e J a n - F
   1.977 +  0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,   // e b - M a r - A
   1.978 +  0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,   // p r - M a y - J
   1.979 +  0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,   // u n - J u l - A
   1.980 +  0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,   // u g - S e p t -
   1.981 +  0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,   // O c t - N o v -
   1.982 +  0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,   // D e c - 0 0 - 0
   1.983 +  0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,   // 0 - 0 0 - M o n
   1.984 +  0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,   // - - T u e - - W
   1.985 +  0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,   // e d - - T h u -
   1.986 +  0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,   // - F r i - - S a
   1.987 +  0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,   // t - - S u n - -
   1.988 +  0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,   // G M T c h u n k
   1.989 +  0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,   // e d - t e x t -
   1.990 +  0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,   // h t m l - i m a
   1.991 +  0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,   // g e - p n g - i
   1.992 +  0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,   // m a g e - j p g
   1.993 +  0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,   // - i m a g e - g
   1.994 +  0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   // i f - a p p l i
   1.995 +  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   // c a t i o n - x
   1.996 +  0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,   // m l - a p p l i
   1.997 +  0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,   // c a t i o n - x
   1.998 +  0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,   // h t m l - x m l
   1.999 +  0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,   // - t e x t - p l
  1.1000 +  0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,   // a i n - t e x t
  1.1001 +  0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,   // - j a v a s c r
  1.1002 +  0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,   // i p t - p u b l
  1.1003 +  0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,   // i c p r i v a t
  1.1004 +  0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,   // e m a x - a g e
  1.1005 +  0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,   // - g z i p - d e
  1.1006 +  0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,   // f l a t e - s d
  1.1007 +  0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,   // c h c h a r s e
  1.1008 +  0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,   // t - u t f - 8 c
  1.1009 +  0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,   // h a r s e t - i
  1.1010 +  0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,   // s o - 8 8 5 9 -
  1.1011 +  0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,   // 1 - u t f - - -
  1.1012 +  0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e          // - e n q - 0 -
  1.1013 +};
  1.1014 +
  1.1015 +// This can be called N times.. 1 for syn_reply and 0->N for headers
  1.1016 +nsresult
  1.1017 +SpdyStream31::Uncompress(z_stream *context,
  1.1018 +                         char *blockStart,
  1.1019 +                         uint32_t blockLen)
  1.1020 +{
  1.1021 +  mDecompressedBytes += blockLen;
  1.1022 +
  1.1023 +  context->avail_in = blockLen;
  1.1024 +  context->next_in = reinterpret_cast<unsigned char *>(blockStart);
  1.1025 +  bool triedDictionary = false;
  1.1026 +
  1.1027 +  do {
  1.1028 +    context->next_out =
  1.1029 +      reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
  1.1030 +      mDecompressBufferUsed;
  1.1031 +    context->avail_out = mDecompressBufferSize - mDecompressBufferUsed;
  1.1032 +    int zlib_rv = inflate(context, Z_NO_FLUSH);
  1.1033 +
  1.1034 +    if (zlib_rv == Z_NEED_DICT) {
  1.1035 +      if (triedDictionary) {
  1.1036 +        LOG3(("SpdySession31::Uncompress %p Dictionary Error\n", this));
  1.1037 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1038 +      }
  1.1039 +
  1.1040 +      triedDictionary = true;
  1.1041 +      inflateSetDictionary(context, kDictionary, sizeof(kDictionary));
  1.1042 +    }
  1.1043 +
  1.1044 +    if (zlib_rv == Z_DATA_ERROR)
  1.1045 +      return NS_ERROR_ILLEGAL_VALUE;
  1.1046 +
  1.1047 +    if (zlib_rv == Z_MEM_ERROR)
  1.1048 +      return NS_ERROR_FAILURE;
  1.1049 +
  1.1050 +    // zlib's inflate() decreases context->avail_out by the amount it places
  1.1051 +    // in the output buffer
  1.1052 +
  1.1053 +    mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
  1.1054 +      context->avail_out;
  1.1055 +
  1.1056 +    // When there is no more output room, but input still available then
  1.1057 +    // increase the output space
  1.1058 +    if (zlib_rv == Z_OK &&
  1.1059 +        !context->avail_out && context->avail_in) {
  1.1060 +      LOG3(("SpdyStream31::Uncompress %p Large Headers - so far %d",
  1.1061 +            this, mDecompressBufferSize));
  1.1062 +      SpdySession31::EnsureBuffer(mDecompressBuffer,
  1.1063 +                                  mDecompressBufferSize + 4096,
  1.1064 +                                  mDecompressBufferUsed,
  1.1065 +                                  mDecompressBufferSize);
  1.1066 +    }
  1.1067 +  }
  1.1068 +  while (context->avail_in);
  1.1069 +  return NS_OK;
  1.1070 +}
  1.1071 +
  1.1072 +// mDecompressBuffer contains 0 to N uncompressed Name/Value Header blocks
  1.1073 +nsresult
  1.1074 +SpdyStream31::FindHeader(nsCString name,
  1.1075 +                         nsDependentCSubstring &value)
  1.1076 +{
  1.1077 +  const unsigned char *nvpair = reinterpret_cast<unsigned char *>
  1.1078 +    (mDecompressBuffer.get()) + 4;
  1.1079 +  const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
  1.1080 +    (mDecompressBuffer.get()) + mDecompressBufferUsed;
  1.1081 +  if (lastHeaderByte < nvpair)
  1.1082 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1083 +
  1.1084 +  do {
  1.1085 +    uint32_t numPairs = PR_ntohl(reinterpret_cast<const uint32_t *>(nvpair)[-1]);
  1.1086 +
  1.1087 +    for (uint32_t index = 0; index < numPairs; ++index) {
  1.1088 +      if (lastHeaderByte < nvpair + 4)
  1.1089 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1090 +      uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
  1.1091 +        (nvpair[2] << 8) + nvpair[3];
  1.1092 +      if (lastHeaderByte < nvpair + 4 + nameLen)
  1.1093 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1094 +      nsDependentCSubstring nameString =
  1.1095 +        Substring(reinterpret_cast<const char *>(nvpair) + 4,
  1.1096 +                  reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
  1.1097 +      if (lastHeaderByte < nvpair + 8 + nameLen)
  1.1098 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1099 +      uint32_t valueLen = (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
  1.1100 +        (nvpair[6 + nameLen] << 8) + nvpair[7 + nameLen];
  1.1101 +      if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
  1.1102 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1103 +      if (nameString.Equals(name)) {
  1.1104 +        value.Assign(((char *)nvpair) + 8 + nameLen, valueLen);
  1.1105 +        return NS_OK;
  1.1106 +      }
  1.1107 +
  1.1108 +      // that pair didn't match - try the next one in this block
  1.1109 +      nvpair += 8 + nameLen + valueLen;
  1.1110 +    }
  1.1111 +
  1.1112 +    // move to the next name/value header block (if there is one) - the
  1.1113 +    // first pair is offset 4 bytes into it
  1.1114 +    nvpair += 4;
  1.1115 +  } while (lastHeaderByte >= nvpair);
  1.1116 +
  1.1117 +  return NS_ERROR_NOT_AVAILABLE;
  1.1118 +}
  1.1119 +
  1.1120 +// ConvertHeaders is used to convert the response headers
  1.1121 +// in a syn_reply or in 0..N headers frames that follow it into
  1.1122 +// HTTP/1 format
  1.1123 +nsresult
  1.1124 +SpdyStream31::ConvertHeaders(nsACString &aHeadersOut)
  1.1125 +{
  1.1126 +  // :status and :version are required.
  1.1127 +  nsDependentCSubstring status, version;
  1.1128 +  nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
  1.1129 +                           status);
  1.1130 +  if (NS_FAILED(rv))
  1.1131 +    return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
  1.1132 +
  1.1133 +  rv = FindHeader(NS_LITERAL_CSTRING(":version"),
  1.1134 +                  version);
  1.1135 +  if (NS_FAILED(rv))
  1.1136 +    return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
  1.1137 +
  1.1138 +  if (mDecompressedBytes && mDecompressBufferUsed) {
  1.1139 +    Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, mDecompressedBytes);
  1.1140 +    uint32_t ratio =
  1.1141 +      mDecompressedBytes * 100 / mDecompressBufferUsed;
  1.1142 +    Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
  1.1143 +  }
  1.1144 +
  1.1145 +  aHeadersOut.Truncate();
  1.1146 +  aHeadersOut.SetCapacity(mDecompressBufferUsed + 64);
  1.1147 +
  1.1148 +  // Connection, Keep-Alive and chunked transfer encodings are to be
  1.1149 +  // removed.
  1.1150 +
  1.1151 +  // Content-Length is 'advisory'.. we will not strip it because it can
  1.1152 +  // create UI feedback.
  1.1153 +
  1.1154 +  aHeadersOut.Append(version);
  1.1155 +  aHeadersOut.Append(NS_LITERAL_CSTRING(" "));
  1.1156 +  aHeadersOut.Append(status);
  1.1157 +  aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
  1.1158 +
  1.1159 +  const unsigned char *nvpair = reinterpret_cast<unsigned char *>
  1.1160 +    (mDecompressBuffer.get()) + 4;
  1.1161 +  const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
  1.1162 +    (mDecompressBuffer.get()) + mDecompressBufferUsed;
  1.1163 +  if (lastHeaderByte < nvpair)
  1.1164 +    return NS_ERROR_ILLEGAL_VALUE;
  1.1165 +
  1.1166 +  do {
  1.1167 +    uint32_t numPairs = PR_ntohl(reinterpret_cast<const uint32_t *>(nvpair)[-1]);
  1.1168 +
  1.1169 +    for (uint32_t index = 0; index < numPairs; ++index) {
  1.1170 +      if (lastHeaderByte < nvpair + 4)
  1.1171 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1172 +
  1.1173 +      uint32_t nameLen = (nvpair[0] << 24) + (nvpair[1] << 16) +
  1.1174 +        (nvpair[2] << 8) + nvpair[3];
  1.1175 +      if (lastHeaderByte < nvpair + 4 + nameLen)
  1.1176 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1177 +
  1.1178 +      nsDependentCSubstring nameString =
  1.1179 +        Substring(reinterpret_cast<const char *>(nvpair) + 4,
  1.1180 +                  reinterpret_cast<const char *>(nvpair) + 4 + nameLen);
  1.1181 +
  1.1182 +      if (lastHeaderByte < nvpair + 8 + nameLen)
  1.1183 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1184 +
  1.1185 +      // Look for illegal characters in the nameString.
  1.1186 +      // This includes upper case characters and nulls (as they will
  1.1187 +      // break the fixup-nulls-in-value-string algorithm)
  1.1188 +      // Look for upper case characters in the name. They are illegal.
  1.1189 +      for (char *cPtr = nameString.BeginWriting();
  1.1190 +           cPtr && cPtr < nameString.EndWriting();
  1.1191 +           ++cPtr) {
  1.1192 +        if (*cPtr <= 'Z' && *cPtr >= 'A') {
  1.1193 +          nsCString toLog(nameString);
  1.1194 +
  1.1195 +          LOG3(("SpdyStream31::ConvertHeaders session=%p stream=%p "
  1.1196 +                "upper case response header found. [%s]\n",
  1.1197 +                mSession, this, toLog.get()));
  1.1198 +
  1.1199 +          return NS_ERROR_ILLEGAL_VALUE;
  1.1200 +        }
  1.1201 +
  1.1202 +        // check for null characters
  1.1203 +        if (*cPtr == '\0')
  1.1204 +          return NS_ERROR_ILLEGAL_VALUE;
  1.1205 +      }
  1.1206 +
  1.1207 +      // HTTP Chunked responses are not legal over spdy. We do not need
  1.1208 +      // to look for chunked specifically because it is the only HTTP
  1.1209 +      // allowed default encoding and we did not negotiate further encodings
  1.1210 +      // via TE
  1.1211 +      if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
  1.1212 +        LOG3(("SpdyStream31::ConvertHeaders session=%p stream=%p "
  1.1213 +              "transfer-encoding found. Chunked is invalid and no TE sent.",
  1.1214 +              mSession, this));
  1.1215 +
  1.1216 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1217 +      }
  1.1218 +
  1.1219 +      uint32_t valueLen =
  1.1220 +        (nvpair[4 + nameLen] << 24) + (nvpair[5 + nameLen] << 16) +
  1.1221 +        (nvpair[6 + nameLen] << 8)  +   nvpair[7 + nameLen];
  1.1222 +
  1.1223 +      if (lastHeaderByte < nvpair + 8 + nameLen + valueLen)
  1.1224 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1225 +
  1.1226 +      // spdy transport level headers shouldn't be gatewayed into http/1
  1.1227 +      if (!nameString.IsEmpty() && nameString[0] != ':' &&
  1.1228 +          !nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
  1.1229 +          !nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
  1.1230 +        nsDependentCSubstring valueString =
  1.1231 +          Substring(reinterpret_cast<const char *>(nvpair) + 8 + nameLen,
  1.1232 +                    reinterpret_cast<const char *>(nvpair) + 8 + nameLen +
  1.1233 +                    valueLen);
  1.1234 +
  1.1235 +        aHeadersOut.Append(nameString);
  1.1236 +        aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
  1.1237 +
  1.1238 +        // expand NULL bytes in the value string
  1.1239 +        for (char *cPtr = valueString.BeginWriting();
  1.1240 +             cPtr && cPtr < valueString.EndWriting();
  1.1241 +             ++cPtr) {
  1.1242 +          if (*cPtr != 0) {
  1.1243 +            aHeadersOut.Append(*cPtr);
  1.1244 +            continue;
  1.1245 +          }
  1.1246 +
  1.1247 +          // NULLs are really "\r\nhdr: "
  1.1248 +          aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
  1.1249 +          aHeadersOut.Append(nameString);
  1.1250 +          aHeadersOut.Append(NS_LITERAL_CSTRING(": "));
  1.1251 +        }
  1.1252 +
  1.1253 +        aHeadersOut.Append(NS_LITERAL_CSTRING("\r\n"));
  1.1254 +      }
  1.1255 +      // move to the next name/value pair in this block
  1.1256 +      nvpair += 8 + nameLen + valueLen;
  1.1257 +    }
  1.1258 +
  1.1259 +    // move to the next name/value header block (if there is one) - the
  1.1260 +    // first pair is offset 4 bytes into it
  1.1261 +    nvpair += 4;
  1.1262 +  } while (lastHeaderByte >= nvpair);
  1.1263 +
  1.1264 +  aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: 3.1\r\n\r\n"));
  1.1265 +  LOG (("decoded response headers are:\n%s",
  1.1266 +        aHeadersOut.BeginReading()));
  1.1267 +
  1.1268 +  // The spdy formatted buffer isnt needed anymore - free it up
  1.1269 +  mDecompressBuffer = nullptr;
  1.1270 +  mDecompressBufferSize = 0;
  1.1271 +  mDecompressBufferUsed = 0;
  1.1272 +
  1.1273 +  return NS_OK;
  1.1274 +}
  1.1275 +
  1.1276 +void
  1.1277 +SpdyStream31::ExecuteCompress(uint32_t flushMode)
  1.1278 +{
  1.1279 +  // Expect mZlib->avail_in and mZlib->next_in to be set.
  1.1280 +  // Append the compressed version of next_in to mTxInlineFrame
  1.1281 +
  1.1282 +  do
  1.1283 +  {
  1.1284 +    uint32_t avail = mTxInlineFrameSize - mTxInlineFrameUsed;
  1.1285 +    if (avail < 1) {
  1.1286 +      SpdySession31::EnsureBuffer(mTxInlineFrame,
  1.1287 +                                  mTxInlineFrameSize + 2000,
  1.1288 +                                  mTxInlineFrameUsed,
  1.1289 +                                  mTxInlineFrameSize);
  1.1290 +      avail = mTxInlineFrameSize - mTxInlineFrameUsed;
  1.1291 +    }
  1.1292 +
  1.1293 +    mZlib->next_out = mTxInlineFrame + mTxInlineFrameUsed;
  1.1294 +    mZlib->avail_out = avail;
  1.1295 +    deflate(mZlib, flushMode);
  1.1296 +    mTxInlineFrameUsed += avail - mZlib->avail_out;
  1.1297 +  } while (mZlib->avail_in > 0 || !mZlib->avail_out);
  1.1298 +}
  1.1299 +
  1.1300 +void
  1.1301 +SpdyStream31::CompressToFrame(uint32_t data)
  1.1302 +{
  1.1303 +  // convert the data to 4 byte network byte order and write that
  1.1304 +  // to the compressed stream
  1.1305 +  data = PR_htonl(data);
  1.1306 +
  1.1307 +  mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
  1.1308 +  mZlib->avail_in = 4;
  1.1309 +  ExecuteCompress(Z_NO_FLUSH);
  1.1310 +}
  1.1311 +
  1.1312 +
  1.1313 +void
  1.1314 +SpdyStream31::CompressToFrame(const char *data, uint32_t len)
  1.1315 +{
  1.1316 +  // Format calls for a network ordered 32 bit length
  1.1317 +  // followed by the utf8 string
  1.1318 +
  1.1319 +  uint32_t networkLen = PR_htonl(len);
  1.1320 +
  1.1321 +  // write out the length
  1.1322 +  mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
  1.1323 +  mZlib->avail_in = 4;
  1.1324 +  ExecuteCompress(Z_NO_FLUSH);
  1.1325 +
  1.1326 +  // write out the data
  1.1327 +  mZlib->next_in = (unsigned char *)data;
  1.1328 +  mZlib->avail_in = len;
  1.1329 +  ExecuteCompress(Z_NO_FLUSH);
  1.1330 +}
  1.1331 +
  1.1332 +void
  1.1333 +SpdyStream31::CompressFlushFrame()
  1.1334 +{
  1.1335 +  mZlib->next_in = (unsigned char *) "";
  1.1336 +  mZlib->avail_in = 0;
  1.1337 +  ExecuteCompress(Z_SYNC_FLUSH);
  1.1338 +}
  1.1339 +
  1.1340 +void
  1.1341 +SpdyStream31::Close(nsresult reason)
  1.1342 +{
  1.1343 +  mTransaction->Close(reason);
  1.1344 +}
  1.1345 +
  1.1346 +void
  1.1347 +SpdyStream31::UpdateRemoteWindow(int32_t delta)
  1.1348 +{
  1.1349 +  mRemoteWindow += delta;
  1.1350 +
  1.1351 +  // If the stream had a <=0 window, that has now opened
  1.1352 +  // schedule it for writing again
  1.1353 +  if (mBlockedOnRwin && mSession->RemoteSessionWindow() > 0 &&
  1.1354 +      mRemoteWindow > 0) {
  1.1355 +    // the window has been opened :)
  1.1356 +    mSession->TransactionHasDataToWrite(this);
  1.1357 +  }
  1.1358 +}
  1.1359 +
  1.1360 +//-----------------------------------------------------------------------------
  1.1361 +// nsAHttpSegmentReader
  1.1362 +//-----------------------------------------------------------------------------
  1.1363 +
  1.1364 +nsresult
  1.1365 +SpdyStream31::OnReadSegment(const char *buf,
  1.1366 +                            uint32_t count,
  1.1367 +                            uint32_t *countRead)
  1.1368 +{
  1.1369 +  LOG3(("SpdyStream31::OnReadSegment %p count=%d state=%x",
  1.1370 +        this, count, mUpstreamState));
  1.1371 +
  1.1372 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1373 +  MOZ_ASSERT(mSegmentReader, "OnReadSegment with null mSegmentReader");
  1.1374 +
  1.1375 +  nsresult rv = NS_ERROR_UNEXPECTED;
  1.1376 +  uint32_t dataLength;
  1.1377 +
  1.1378 +  switch (mUpstreamState) {
  1.1379 +  case GENERATING_SYN_STREAM:
  1.1380 +    // The buffer is the HTTP request stream, including at least part of the
  1.1381 +    // HTTP request header. This state's job is to build a SYN_STREAM frame
  1.1382 +    // from the header information. count is the number of http bytes available
  1.1383 +    // (which may include more than the header), and in countRead we return
  1.1384 +    // the number of those bytes that we consume (i.e. the portion that are
  1.1385 +    // header bytes)
  1.1386 +
  1.1387 +    rv = ParseHttpRequestHeaders(buf, count, countRead);
  1.1388 +    if (NS_FAILED(rv))
  1.1389 +      return rv;
  1.1390 +    LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
  1.1391 +          this, *countRead, count, mSynFrameComplete));
  1.1392 +    if (mSynFrameComplete) {
  1.1393 +      AdjustInitialWindow();
  1.1394 +      rv = TransmitFrame(nullptr, nullptr, true);
  1.1395 +      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
  1.1396 +        // this can't happen
  1.1397 +        MOZ_ASSERT(false, "Transmit Frame SYN_FRAME must at least buffer data");
  1.1398 +        rv = NS_ERROR_UNEXPECTED;
  1.1399 +      }
  1.1400 +
  1.1401 +      ChangeState(GENERATING_REQUEST_BODY);
  1.1402 +      break;
  1.1403 +    }
  1.1404 +    MOZ_ASSERT(*countRead == count, "Header parsing not complete but unused data");
  1.1405 +    break;
  1.1406 +
  1.1407 +  case GENERATING_REQUEST_BODY:
  1.1408 +    if ((mRemoteWindow <= 0) || (mSession->RemoteSessionWindow() <= 0)) {
  1.1409 +      *countRead = 0;
  1.1410 +      LOG3(("SpdyStream31 this=%p, id 0x%X request body suspended because "
  1.1411 +            "remote window is stream=%ld session=%ld.\n", this, mStreamID,
  1.1412 +            mRemoteWindow, mSession->RemoteSessionWindow()));
  1.1413 +      mBlockedOnRwin = true;
  1.1414 +      return NS_BASE_STREAM_WOULD_BLOCK;
  1.1415 +    }
  1.1416 +    mBlockedOnRwin = false;
  1.1417 +
  1.1418 +    dataLength = std::min(count, mChunkSize);
  1.1419 +
  1.1420 +    if (dataLength > mRemoteWindow)
  1.1421 +      dataLength = static_cast<uint32_t>(mRemoteWindow);
  1.1422 +
  1.1423 +    if (dataLength > mSession->RemoteSessionWindow())
  1.1424 +      dataLength = static_cast<uint32_t>(mSession->RemoteSessionWindow());
  1.1425 +
  1.1426 +    LOG3(("SpdyStream31 this=%p id 0x%X remote window is stream %ld and "
  1.1427 +          "session %ld. Chunk is %d\n",
  1.1428 +          this, mStreamID, mRemoteWindow, mSession->RemoteSessionWindow(),
  1.1429 +          dataLength));
  1.1430 +    mRemoteWindow -= dataLength;
  1.1431 +    mSession->DecrementRemoteSessionWindow(dataLength);
  1.1432 +
  1.1433 +    LOG3(("SpdyStream31 %p id %x request len remaining %d, "
  1.1434 +          "count avail %d, chunk used %d",
  1.1435 +          this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
  1.1436 +    if (dataLength > mRequestBodyLenRemaining)
  1.1437 +      return NS_ERROR_UNEXPECTED;
  1.1438 +    mRequestBodyLenRemaining -= dataLength;
  1.1439 +    GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
  1.1440 +    ChangeState(SENDING_REQUEST_BODY);
  1.1441 +    // NO BREAK
  1.1442 +
  1.1443 +  case SENDING_REQUEST_BODY:
  1.1444 +    MOZ_ASSERT(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
  1.1445 +    rv = TransmitFrame(buf, countRead, false);
  1.1446 +    MOZ_ASSERT(NS_FAILED(rv) || !mTxInlineFrameUsed,
  1.1447 +               "Transmit Frame should be all or nothing");
  1.1448 +
  1.1449 +    LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
  1.1450 +          "Header is %d Body is %d.",
  1.1451 +          rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
  1.1452 +
  1.1453 +    // normalize a partial write with a WOULD_BLOCK into just a partial write
  1.1454 +    // as some code will take WOULD_BLOCK to mean an error with nothing
  1.1455 +    // written (e.g. nsHttpTransaction::ReadRequestSegment()
  1.1456 +    if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
  1.1457 +      rv = NS_OK;
  1.1458 +
  1.1459 +    // If that frame was all sent, look for another one
  1.1460 +    if (!mTxInlineFrameUsed)
  1.1461 +      ChangeState(GENERATING_REQUEST_BODY);
  1.1462 +    break;
  1.1463 +
  1.1464 +  case SENDING_FIN_STREAM:
  1.1465 +    MOZ_ASSERT(false, "resuming partial fin stream out of OnReadSegment");
  1.1466 +    break;
  1.1467 +
  1.1468 +  default:
  1.1469 +    MOZ_ASSERT(false, "SpdyStream31::OnReadSegment non-write state");
  1.1470 +    break;
  1.1471 +  }
  1.1472 +
  1.1473 +  return rv;
  1.1474 +}
  1.1475 +
  1.1476 +//-----------------------------------------------------------------------------
  1.1477 +// nsAHttpSegmentWriter
  1.1478 +//-----------------------------------------------------------------------------
  1.1479 +
  1.1480 +nsresult
  1.1481 +SpdyStream31::OnWriteSegment(char *buf,
  1.1482 +                             uint32_t count,
  1.1483 +                             uint32_t *countWritten)
  1.1484 +{
  1.1485 +  LOG3(("SpdyStream31::OnWriteSegment %p count=%d state=%x 0x%X\n",
  1.1486 +        this, count, mUpstreamState, mStreamID));
  1.1487 +
  1.1488 +  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1489 +  MOZ_ASSERT(mSegmentWriter);
  1.1490 +
  1.1491 +  if (!mPushSource)
  1.1492 +    return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
  1.1493 +
  1.1494 +  nsresult rv;
  1.1495 +  rv = mPushSource->GetBufferedData(buf, count, countWritten);
  1.1496 +  if (NS_FAILED(rv))
  1.1497 +    return rv;
  1.1498 +
  1.1499 +  mSession->ConnectPushedStream(this);
  1.1500 +  return NS_OK;
  1.1501 +}
  1.1502 +
  1.1503 +} // namespace mozilla::net
  1.1504 +} // namespace mozilla
  1.1505 +

mercurial