netwerk/protocol/http/SpdyStream3.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial