LCOV - code coverage report
Current view: directory - netwerk/protocol/http - SpdyStream.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 365 0 0.0 %
Date: 2012-06-02 Functions: 22 0 0.0 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Patrick McManus <mcmanus@ducksong.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsHttp.h"
      41                 : #include "SpdySession.h"
      42                 : #include "SpdyStream.h"
      43                 : #include "nsAlgorithm.h"
      44                 : #include "prnetdb.h"
      45                 : #include "nsHttpRequestHead.h"
      46                 : #include "mozilla/Telemetry.h"
      47                 : #include "nsISocketTransport.h"
      48                 : #include "nsISupportsPriority.h"
      49                 : 
      50                 : #ifdef DEBUG
      51                 : // defined by the socket transport service while active
      52                 : extern PRThread *gSocketThread;
      53                 : #endif
      54                 : 
      55                 : namespace mozilla {
      56                 : namespace net {
      57                 : 
      58               0 : SpdyStream::SpdyStream(nsAHttpTransaction *httpTransaction,
      59                 :                        SpdySession *spdySession,
      60                 :                        nsISocketTransport *socketTransport,
      61                 :                        PRUint32 chunkSize,
      62                 :                        z_stream *compressionContext,
      63                 :                        PRInt32 priority)
      64                 :   : mUpstreamState(GENERATING_SYN_STREAM),
      65                 :     mTransaction(httpTransaction),
      66                 :     mSession(spdySession),
      67                 :     mSocketTransport(socketTransport),
      68                 :     mSegmentReader(nsnull),
      69                 :     mSegmentWriter(nsnull),
      70                 :     mStreamID(0),
      71                 :     mChunkSize(chunkSize),
      72                 :     mSynFrameComplete(0),
      73                 :     mRequestBlockedOnRead(0),
      74                 :     mSentFinOnData(0),
      75                 :     mRecvdFin(0),
      76                 :     mFullyOpen(0),
      77                 :     mSentWaitingFor(0),
      78                 :     mTxInlineFrameSize(SpdySession::kDefaultBufferSize),
      79                 :     mTxInlineFrameUsed(0),
      80                 :     mTxStreamFrameSize(0),
      81                 :     mZlib(compressionContext),
      82                 :     mRequestBodyLenRemaining(0),
      83                 :     mPriority(priority),
      84                 :     mTotalSent(0),
      85               0 :     mTotalRead(0)
      86                 : {
      87               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
      88                 : 
      89               0 :   LOG3(("SpdyStream::SpdyStream %p", this));
      90                 : 
      91               0 :   mTxInlineFrame = new char[mTxInlineFrameSize];
      92               0 : }
      93                 : 
      94               0 : SpdyStream::~SpdyStream()
      95                 : {
      96               0 : }
      97                 : 
      98                 : // ReadSegments() is used to write data down the socket. Generally, HTTP
      99                 : // request data is pulled from the approriate transaction and
     100                 : // converted to SPDY data. Sometimes control data like a window-update is
     101                 : // generated instead.
     102                 : 
     103                 : nsresult
     104               0 : SpdyStream::ReadSegments(nsAHttpSegmentReader *reader,
     105                 :                          PRUint32 count,
     106                 :                          PRUint32 *countRead)
     107                 : {
     108               0 :   LOG3(("SpdyStream %p ReadSegments reader=%p count=%d state=%x",
     109                 :         this, reader, count, mUpstreamState));
     110                 : 
     111               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     112                 :   
     113               0 :   nsresult rv = NS_ERROR_UNEXPECTED;
     114               0 :   mRequestBlockedOnRead = 0;
     115                 : 
     116               0 :   switch (mUpstreamState) {
     117                 :   case GENERATING_SYN_STREAM:
     118                 :   case GENERATING_REQUEST_BODY:
     119                 :   case SENDING_REQUEST_BODY:
     120                 :     // Call into the HTTP Transaction to generate the HTTP request
     121                 :     // stream. That stream will show up in OnReadSegment().
     122               0 :     mSegmentReader = reader;
     123               0 :     rv = mTransaction->ReadSegments(this, count, countRead);
     124               0 :     mSegmentReader = nsnull;
     125                 : 
     126                 :     // Check to see if the transaction's request could be written out now.
     127                 :     // If not, mark the stream for callback when writing can proceed.
     128               0 :     if (NS_SUCCEEDED(rv) &&
     129                 :         mUpstreamState == GENERATING_SYN_STREAM &&
     130               0 :         !mSynFrameComplete)
     131               0 :       mSession->TransactionHasDataToWrite(this);
     132                 :     
     133                 :     // mTxinlineFrameUsed represents any queued un-sent frame. It might
     134                 :     // be 0 if there is no such frame, which is not a gurantee that we
     135                 :     // don't have more request body to send - just that any data that was
     136                 :     // sent comprised a complete SPDY frame. Likewise, a non 0 value is
     137                 :     // a queued, but complete, spdy frame length.
     138                 : 
     139                 :     // Mark that we are blocked on read if the http transaction needs to
     140                 :     // provide more of the request message body and there is nothing queued
     141                 :     // for writing
     142               0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
     143               0 :       mRequestBlockedOnRead = 1;
     144                 : 
     145               0 :     if (!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
     146               0 :       LOG3(("ReadSegments %p: Sending request data complete, mUpstreamState=%x",
     147                 :             this, mUpstreamState));
     148               0 :       if (mSentFinOnData) {
     149               0 :         ChangeState(UPSTREAM_COMPLETE);
     150                 :       }
     151                 :       else {
     152               0 :         GenerateDataFrameHeader(0, true);
     153               0 :         ChangeState(SENDING_FIN_STREAM);
     154               0 :         mSession->TransactionHasDataToWrite(this);
     155               0 :         rv = NS_BASE_STREAM_WOULD_BLOCK;
     156                 :       }
     157                 :     }
     158                 : 
     159               0 :     break;
     160                 : 
     161                 :   case SENDING_SYN_STREAM:
     162                 :     // We were trying to send the SYN-STREAM but were blocked from trying
     163                 :     // to transmit it the first time(s).
     164               0 :     mSegmentReader = reader;
     165               0 :     rv = TransmitFrame(nsnull, nsnull);
     166               0 :     mSegmentReader = nsnull;
     167               0 :     *countRead = 0;
     168               0 :     if (NS_SUCCEEDED(rv)) {
     169               0 :       NS_ABORT_IF_FALSE(!mTxInlineFrameUsed,
     170                 :                         "Transmit Frame should be all or nothing");
     171                 :     
     172               0 :       if (mSentFinOnData) {
     173               0 :         ChangeState(UPSTREAM_COMPLETE);
     174               0 :         rv = NS_OK;
     175                 :       }
     176                 :       else {
     177               0 :         rv = NS_BASE_STREAM_WOULD_BLOCK;
     178               0 :         ChangeState(GENERATING_REQUEST_BODY);
     179               0 :         mSession->TransactionHasDataToWrite(this);
     180                 :       }
     181                 :     }
     182               0 :     break;
     183                 : 
     184                 :   case SENDING_FIN_STREAM:
     185                 :     // We were trying to send the FIN-STREAM but were blocked from
     186                 :     // sending it out - try again.
     187               0 :     if (!mSentFinOnData) {
     188               0 :       mSegmentReader = reader;
     189               0 :       rv = TransmitFrame(nsnull, nsnull);
     190               0 :       mSegmentReader = nsnull;
     191               0 :       NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
     192                 :                         "Transmit Frame should be all or nothing");
     193               0 :       if (NS_SUCCEEDED(rv))
     194               0 :         ChangeState(UPSTREAM_COMPLETE);
     195                 :     }
     196                 :     else {
     197               0 :       rv = NS_OK;
     198               0 :       mTxInlineFrameUsed = 0;         // cancel fin data packet
     199               0 :       ChangeState(UPSTREAM_COMPLETE);
     200                 :     }
     201                 :     
     202               0 :     *countRead = 0;
     203                 : 
     204                 :     // don't change OK to WOULD BLOCK. we are really done sending if OK
     205               0 :     break;
     206                 : 
     207                 :   case UPSTREAM_COMPLETE:
     208               0 :     *countRead = 0;
     209               0 :     rv = NS_OK;
     210               0 :     break;
     211                 : 
     212                 :   default:
     213               0 :     NS_ABORT_IF_FALSE(false, "SpdyStream::ReadSegments unknown state");
     214               0 :     break;
     215                 :   }
     216                 : 
     217               0 :   return rv;
     218                 : }
     219                 : 
     220                 : // WriteSegments() is used to read data off the socket. Generally this is
     221                 : // just the SPDY frame header and from there the appropriate SPDYStream
     222                 : // is identified from the Stream-ID. The http transaction associated with
     223                 : // that read then pulls in the data directly.
     224                 : 
     225                 : nsresult
     226               0 : SpdyStream::WriteSegments(nsAHttpSegmentWriter *writer,
     227                 :                           PRUint32 count,
     228                 :                           PRUint32 *countWritten)
     229                 : {
     230               0 :   LOG3(("SpdyStream::WriteSegments %p count=%d state=%x",
     231                 :         this, count, mUpstreamState));
     232                 :   
     233               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     234               0 :   NS_ABORT_IF_FALSE(!mSegmentWriter, "segment writer in progress");
     235                 : 
     236               0 :   mSegmentWriter = writer;
     237               0 :   nsresult rv = mTransaction->WriteSegments(writer, count, countWritten);
     238               0 :   mSegmentWriter = nsnull;
     239               0 :   return rv;
     240                 : }
     241                 : 
     242                 : PLDHashOperator
     243               0 : SpdyStream::hdrHashEnumerate(const nsACString &key,
     244                 :                              nsAutoPtr<nsCString> &value,
     245                 :                              void *closure)
     246                 : {
     247               0 :   SpdyStream *self = static_cast<SpdyStream *>(closure);
     248                 : 
     249               0 :   self->CompressToFrame(key);
     250               0 :   self->CompressToFrame(value.get());
     251               0 :   return PL_DHASH_NEXT;
     252                 : }
     253                 : 
     254                 : nsresult
     255               0 : SpdyStream::ParseHttpRequestHeaders(const char *buf,
     256                 :                                     PRUint32 avail,
     257                 :                                     PRUint32 *countUsed)
     258                 : {
     259                 :   // Returns NS_OK even if the headers are incomplete
     260                 :   // set mSynFrameComplete flag if they are complete
     261                 : 
     262               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     263               0 :   NS_ABORT_IF_FALSE(mUpstreamState == GENERATING_SYN_STREAM, "wrong state");
     264                 : 
     265               0 :   LOG3(("SpdyStream::ParseHttpRequestHeaders %p avail=%d state=%x",
     266                 :         this, avail, mUpstreamState));
     267                 : 
     268               0 :   mFlatHttpRequestHeaders.Append(buf, avail);
     269                 : 
     270                 :   // We can use the simple double crlf because firefox is the
     271                 :   // only client we are parsing
     272               0 :   PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
     273                 :   
     274               0 :   if (endHeader == kNotFound) {
     275                 :     // We don't have all the headers yet
     276               0 :     LOG3(("SpdyStream::ParseHttpRequestHeaders %p "
     277                 :           "Need more header bytes. Len = %d",
     278                 :           this, mFlatHttpRequestHeaders.Length()));
     279               0 :     *countUsed = avail;
     280               0 :     return NS_OK;
     281                 :   }
     282                 :            
     283                 :   // We have recvd all the headers, trim the local
     284                 :   // buffer of the final empty line, and set countUsed to reflect
     285                 :   // the whole header has been consumed.
     286               0 :   PRUint32 oldLen = mFlatHttpRequestHeaders.Length();
     287               0 :   mFlatHttpRequestHeaders.SetLength(endHeader + 2);
     288               0 :   *countUsed = avail - (oldLen - endHeader) + 4;
     289               0 :   mSynFrameComplete = 1;
     290                 : 
     291                 :   // It is now OK to assign a streamID that we are assured will
     292                 :   // be monotonically increasing amongst syn-streams on this
     293                 :   // session
     294               0 :   mStreamID = mSession->RegisterStreamID(this);
     295               0 :   NS_ABORT_IF_FALSE(mStreamID & 1,
     296                 :                     "Spdy Stream Channel ID must be odd");
     297                 : 
     298               0 :   if (mStreamID >= 0x80000000) {
     299                 :     // streamID must fit in 31 bits. This is theoretically possible
     300                 :     // because stream ID assignment is asynchronous to stream creation
     301                 :     // because of the protocol requirement that the ID in syn-stream
     302                 :     // be monotonically increasing. In reality this is really not possible
     303                 :     // because new streams stop being added to a session with 0x10000000 / 2
     304                 :     // IDs still available and no race condition is going to bridge that gap,
     305                 :     // so we can be comfortable on just erroring out for correctness in that
     306                 :     // case.
     307               0 :     LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
     308               0 :     return NS_ERROR_UNEXPECTED;
     309                 :   }
     310                 : 
     311                 :   // Now we need to convert the flat http headers into a set
     312                 :   // of SPDY headers..  writing to mTxInlineFrame{sz}
     313                 : 
     314               0 :   mTxInlineFrame[0] = SpdySession::kFlag_Control;
     315               0 :   mTxInlineFrame[1] = 2;                          /* version */
     316               0 :   mTxInlineFrame[2] = 0;
     317               0 :   mTxInlineFrame[3] = SpdySession::CONTROL_TYPE_SYN_STREAM;
     318                 :   // 4 to 7 are length and flags, we'll fill that in later
     319                 :   
     320               0 :   PRUint32 networkOrderID = PR_htonl(mStreamID);
     321               0 :   memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
     322                 :   
     323                 :   // this is the associated-to field, which is not used sending
     324                 :   // from the client in the http binding
     325               0 :   memset (mTxInlineFrame + 12, 0, 4);
     326                 : 
     327                 :   // Priority flags are the C0 mask of byte 16.
     328                 :   //
     329                 :   // The other 6 bits of 16 are unused. Spdy/3 will expand
     330                 :   // priority to 4 bits.
     331                 :   //
     332                 :   // When Spdy/3 implements WINDOW_UPDATE the lowest priority
     333                 :   // streams over a threshold (32?) should be given tiny
     334                 :   // receive windows, separate from their spdy priority
     335                 :   //
     336               0 :   if (mPriority >= nsISupportsPriority::PRIORITY_LOW)
     337               0 :     mTxInlineFrame[16] = SpdySession::kPri03;
     338               0 :   else if (mPriority >= nsISupportsPriority::PRIORITY_NORMAL)
     339               0 :     mTxInlineFrame[16] = SpdySession::kPri02;
     340               0 :   else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH)
     341               0 :     mTxInlineFrame[16] = SpdySession::kPri01;
     342                 :   else
     343               0 :     mTxInlineFrame[16] = SpdySession::kPri00;
     344                 : 
     345               0 :   mTxInlineFrame[17] = 0;                         /* unused */
     346                 :   
     347               0 :   const char *methodHeader = mTransaction->RequestHead()->Method().get();
     348                 : 
     349               0 :   nsCString hostHeader;
     350               0 :   mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
     351                 : 
     352               0 :   nsCString versionHeader;
     353               0 :   if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
     354               0 :     versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
     355                 :   else
     356               0 :     versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
     357                 : 
     358               0 :   nsClassHashtable<nsCStringHashKey, nsCString> hdrHash;
     359                 :   
     360                 :   // use mRequestHead() to get a sense of how big to make the hash,
     361                 :   // even though we are parsing the actual text stream because
     362                 :   // it is legit to append headers.
     363               0 :   hdrHash.Init(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
     364                 :   
     365               0 :   const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
     366                 : 
     367                 :   // need to hash all the headers together to remove duplicates, special
     368                 :   // headers, etc..
     369                 : 
     370               0 :   PRInt32 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
     371               0 :   while (true) {
     372               0 :     PRInt32 startIndex = crlfIndex + 2;
     373                 : 
     374               0 :     crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
     375               0 :     if (crlfIndex == -1)
     376               0 :       break;
     377                 :     
     378                 :     PRInt32 colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
     379               0 :                                                       crlfIndex - startIndex);
     380               0 :     if (colonIndex == -1)
     381               0 :       break;
     382                 :     
     383                 :     nsDependentCSubstring name = Substring(beginBuffer + startIndex,
     384               0 :                                            beginBuffer + colonIndex);
     385                 :     // all header names are lower case in spdy
     386               0 :     ToLowerCase(name);
     387                 : 
     388               0 :     if (name.Equals("method") ||
     389               0 :         name.Equals("version") ||
     390               0 :         name.Equals("scheme") ||
     391               0 :         name.Equals("keep-alive") ||
     392               0 :         name.Equals("accept-encoding") ||
     393               0 :         name.Equals("te") ||
     394               0 :         name.Equals("connection") ||
     395               0 :         name.Equals("proxy-connection") ||
     396               0 :         name.Equals("url"))
     397               0 :       continue;
     398                 :     
     399               0 :     nsCString *val = hdrHash.Get(name);
     400               0 :     if (!val) {
     401               0 :       val = new nsCString();
     402               0 :       hdrHash.Put(name, val);
     403                 :     }
     404                 : 
     405               0 :     PRInt32 valueIndex = colonIndex + 1;
     406               0 :     while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
     407               0 :       ++valueIndex;
     408                 :     
     409                 :     nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
     410               0 :                                         beginBuffer + crlfIndex);
     411               0 :     if (!val->IsEmpty())
     412               0 :       val->Append(static_cast<char>(0));
     413               0 :     val->Append(v);
     414                 : 
     415               0 :     if (name.Equals("content-length")) {
     416                 :       PRInt64 len;
     417               0 :       if (nsHttp::ParseInt64(val->get(), nsnull, &len))
     418               0 :         mRequestBodyLenRemaining = len;
     419                 :     }
     420                 :   }
     421                 :   
     422               0 :   mTxInlineFrameUsed = 18;
     423                 : 
     424                 :   // Do not naively log the request headers here beacuse they might
     425                 :   // contain auth. The http transaction already logs the sanitized request
     426                 :   // headers at this same level so it is not necessary to do so here.
     427                 : 
     428                 :   // The header block length
     429               0 :   PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */
     430               0 :   CompressToFrame(count);
     431                 : 
     432                 :   // method, scheme, url, and version headers for request line
     433                 : 
     434               0 :   CompressToFrame(NS_LITERAL_CSTRING("method"));
     435               0 :   CompressToFrame(methodHeader, strlen(methodHeader));
     436               0 :   CompressToFrame(NS_LITERAL_CSTRING("scheme"));
     437               0 :   CompressToFrame(NS_LITERAL_CSTRING("https"));
     438               0 :   CompressToFrame(NS_LITERAL_CSTRING("url"));
     439               0 :   CompressToFrame(mTransaction->RequestHead()->RequestURI());
     440               0 :   CompressToFrame(NS_LITERAL_CSTRING("version"));
     441               0 :   CompressToFrame(versionHeader);
     442                 :   
     443               0 :   hdrHash.Enumerate(hdrHashEnumerate, this);
     444               0 :   CompressFlushFrame();
     445                 :   
     446                 :   // 4 to 7 are length and flags, which we can now fill in
     447               0 :   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
     448               0 :     PR_htonl(mTxInlineFrameUsed - 8);
     449                 : 
     450               0 :   NS_ABORT_IF_FALSE(!mTxInlineFrame[4],
     451                 :                     "Size greater than 24 bits");
     452                 :   
     453                 :   // Determine whether to put the fin bit on the syn stream frame or whether
     454                 :   // to wait for a data packet to put it on.
     455                 : 
     456               0 :   if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
     457               0 :       mTransaction->RequestHead()->Method() == nsHttp::Connect ||
     458               0 :       mTransaction->RequestHead()->Method() == nsHttp::Head) {
     459                 :     // for GET, CONNECT, and HEAD place the fin bit right on the
     460                 :     // syn stream packet
     461                 : 
     462               0 :     mSentFinOnData = 1;
     463               0 :     mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN;
     464                 :   }
     465               0 :   else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
     466               0 :            mTransaction->RequestHead()->Method() == nsHttp::Put ||
     467               0 :            mTransaction->RequestHead()->Method() == nsHttp::Options) {
     468                 :     // place fin in a data frame even for 0 length messages, I've seen
     469                 :     // the google gateway be unhappy with fin-on-syn for 0 length POST
     470                 :   }
     471               0 :   else if (!mRequestBodyLenRemaining) {
     472                 :     // for other HTTP extension methods, rely on the content-length
     473                 :     // to determine whether or not to put fin on syn
     474               0 :     mSentFinOnData = 1;
     475               0 :     mTxInlineFrame[4] = SpdySession::kFlag_Data_FIN;
     476                 :   }
     477                 :   
     478               0 :   Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
     479                 : 
     480                 :   // The size of the input headers is approximate
     481                 :   PRUint32 ratio =
     482                 :     (mTxInlineFrameUsed - 18) * 100 /
     483               0 :     (11 + mTransaction->RequestHead()->RequestURI().Length() +
     484               0 :      mFlatHttpRequestHeaders.Length());
     485                 :   
     486               0 :   Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
     487               0 :   return NS_OK;
     488                 : }
     489                 : 
     490                 : void
     491               0 : SpdyStream::UpdateTransportReadEvents(PRUint32 count)
     492                 : {
     493               0 :   mTotalRead += count;
     494                 : 
     495               0 :   mTransaction->OnTransportStatus(mSocketTransport,
     496                 :                                   NS_NET_STATUS_RECEIVING_FROM,
     497               0 :                                   mTotalRead);
     498               0 : }
     499                 : 
     500                 : void
     501               0 : SpdyStream::UpdateTransportSendEvents(PRUint32 count)
     502                 : {
     503               0 :   mTotalSent += count;
     504                 : 
     505               0 :   if (mUpstreamState != SENDING_FIN_STREAM)
     506               0 :     mTransaction->OnTransportStatus(mSocketTransport,
     507                 :                                     NS_NET_STATUS_SENDING_TO,
     508               0 :                                     mTotalSent);
     509                 : 
     510               0 :   if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
     511               0 :     mSentWaitingFor = 1;
     512               0 :     mTransaction->OnTransportStatus(mSocketTransport,
     513                 :                                     NS_NET_STATUS_WAITING_FOR,
     514               0 :                                     LL_ZERO);
     515                 :   }
     516               0 : }
     517                 : 
     518                 : nsresult
     519               0 : SpdyStream::TransmitFrame(const char *buf,
     520                 :                           PRUint32 *countUsed)
     521                 : {
     522                 :   // If TransmitFrame returns SUCCESS than all the data is sent (or at least
     523                 :   // buffered at the session level), if it returns WOULD_BLOCK then none of
     524                 :   // the data is sent.
     525                 : 
     526                 :   // You can call this function with no data and no out parameter in order to
     527                 :   // flush internal buffers that were previously blocked on writing. You can
     528                 :   // of course feed new data to it as well.
     529                 : 
     530               0 :   NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "empty stream frame in transmit");
     531               0 :   NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
     532               0 :   NS_ABORT_IF_FALSE((buf && countUsed) || (!buf && !countUsed),
     533                 :                     "TransmitFrame arguments inconsistent");
     534                 : 
     535                 :   PRUint32 transmittedCount;
     536                 :   nsresult rv;
     537                 :   
     538               0 :   LOG3(("SpdyStream::TransmitFrame %p inline=%d stream=%d",
     539                 :         this, mTxInlineFrameUsed, mTxStreamFrameSize));
     540               0 :   if (countUsed)
     541               0 :     *countUsed = 0;
     542                 : 
     543                 :   // In the (relatively common) event that we have a small amount of data
     544                 :   // split between the inlineframe and the streamframe, then move the stream
     545                 :   // data into the inlineframe via copy in order to coalesce into one write.
     546                 :   // Given the interaction with ssl this is worth the small copy cost.
     547               0 :   if (mTxStreamFrameSize && mTxInlineFrameUsed &&
     548                 :       mTxStreamFrameSize < SpdySession::kDefaultBufferSize &&
     549                 :       mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
     550               0 :     LOG3(("Coalesce Transmit"));
     551               0 :     memcpy (mTxInlineFrame + mTxInlineFrameUsed,
     552               0 :             buf, mTxStreamFrameSize);
     553               0 :     if (countUsed)
     554               0 :       *countUsed += mTxStreamFrameSize;
     555               0 :     mTxInlineFrameUsed += mTxStreamFrameSize;
     556               0 :     mTxStreamFrameSize = 0;
     557                 :   }
     558                 : 
     559                 :   rv = mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize +
     560               0 :                                            mTxInlineFrameUsed);
     561               0 :   if (rv == NS_BASE_STREAM_WOULD_BLOCK)
     562               0 :     mSession->TransactionHasDataToWrite(this);
     563               0 :   if (NS_FAILED(rv))     // this will include WOULD_BLOCK
     564               0 :     return rv;
     565                 : 
     566                 :   // This function calls mSegmentReader->OnReadSegment to report the actual SPDY
     567                 :   // bytes through to the SpdySession and then the HttpConnection which calls
     568                 :   // the socket write function. It will accept all of the inline and stream
     569                 :   // data because of the above 'commitment' even if it has to buffer
     570                 :   
     571                 :   rv = mSegmentReader->OnReadSegment(mTxInlineFrame, mTxInlineFrameUsed,
     572               0 :                                      &transmittedCount);
     573               0 :   LOG3(("SpdyStream::TransmitFrame for inline session=%p "
     574                 :         "stream=%p result %x len=%d",
     575                 :         mSession, this, rv, transmittedCount));
     576                 : 
     577               0 :   NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
     578                 :                     "inconsistent inline commitment result");
     579                 : 
     580               0 :   if (NS_FAILED(rv))
     581               0 :     return rv;
     582                 : 
     583               0 :   NS_ABORT_IF_FALSE(transmittedCount == mTxInlineFrameUsed,
     584                 :                     "inconsistent inline commitment count");
     585                 :     
     586                 :   SpdySession::LogIO(mSession, this, "Writing from Inline Buffer",
     587               0 :                      mTxInlineFrame, transmittedCount);
     588                 : 
     589               0 :   if (mTxStreamFrameSize) {
     590               0 :     if (!buf) {
     591                 :       // this cannot happen
     592               0 :       NS_ABORT_IF_FALSE(false, "Stream transmit with null buf argument to "
     593                 :                         "TransmitFrame()");
     594               0 :       LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
     595               0 :       return NS_ERROR_UNEXPECTED;
     596                 :     }
     597                 : 
     598                 :     rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize,
     599               0 :                                        &transmittedCount);
     600                 : 
     601               0 :     LOG3(("SpdyStream::TransmitFrame for regular session=%p "
     602                 :           "stream=%p result %x len=%d",
     603                 :           mSession, this, rv, transmittedCount));
     604                 :   
     605               0 :     NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
     606                 :                       "inconsistent stream commitment result");
     607                 : 
     608               0 :     if (NS_FAILED(rv))
     609               0 :       return rv;
     610                 : 
     611               0 :     NS_ABORT_IF_FALSE(transmittedCount == mTxStreamFrameSize,
     612                 :                       "inconsistent stream commitment count");
     613                 :     
     614                 :     SpdySession::LogIO(mSession, this, "Writing from Transaction Buffer",
     615               0 :                        buf, transmittedCount);
     616                 : 
     617               0 :     *countUsed += mTxStreamFrameSize;
     618                 :   }
     619                 :   
     620                 :   // calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
     621               0 :   UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
     622                 : 
     623               0 :   mTxInlineFrameUsed = 0;
     624               0 :   mTxStreamFrameSize = 0;
     625                 : 
     626               0 :   return NS_OK;
     627                 : }
     628                 : 
     629                 : void
     630               0 : SpdyStream::ChangeState(enum stateType newState)
     631                 : {
     632               0 :   LOG3(("SpdyStream::ChangeState() %p from %X to %X",
     633                 :         this, mUpstreamState, newState));
     634               0 :   mUpstreamState = newState;
     635                 :   return;
     636                 : }
     637                 : 
     638                 : void
     639               0 : SpdyStream::GenerateDataFrameHeader(PRUint32 dataLength, bool lastFrame)
     640                 : {
     641               0 :   LOG3(("SpdyStream::GenerateDataFrameHeader %p len=%d last=%d",
     642                 :         this, dataLength, lastFrame));
     643                 : 
     644               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     645               0 :   NS_ABORT_IF_FALSE(!mTxInlineFrameUsed, "inline frame not empty");
     646               0 :   NS_ABORT_IF_FALSE(!mTxStreamFrameSize, "stream frame not empty");
     647               0 :   NS_ABORT_IF_FALSE(!(dataLength & 0xff000000), "datalength > 24 bits");
     648                 :   
     649               0 :   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
     650               0 :   (reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
     651               0 :     PR_htonl(dataLength);
     652                 :   
     653               0 :   NS_ABORT_IF_FALSE(!(mTxInlineFrame[0] & 0x80),
     654                 :                     "control bit set unexpectedly");
     655               0 :   NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "flag bits set unexpectedly");
     656                 :   
     657               0 :   mTxInlineFrameUsed = 8;
     658               0 :   mTxStreamFrameSize = dataLength;
     659                 : 
     660               0 :   if (lastFrame) {
     661               0 :     mTxInlineFrame[4] |= SpdySession::kFlag_Data_FIN;
     662               0 :     if (dataLength)
     663               0 :       mSentFinOnData = 1;
     664                 :   }
     665               0 : }
     666                 : 
     667                 : void
     668               0 : SpdyStream::CompressToFrame(const nsACString &str)
     669                 : {
     670               0 :   CompressToFrame(str.BeginReading(), str.Length());
     671               0 : }
     672                 : 
     673                 : void
     674               0 : SpdyStream::CompressToFrame(const nsACString *str)
     675                 : {
     676               0 :   CompressToFrame(str->BeginReading(), str->Length());
     677               0 : }
     678                 : 
     679                 : // Dictionary taken from
     680                 : // http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2 
     681                 : // Name/Value Header Block Format
     682                 : // spec indicates that the compression dictionary is not null terminated
     683                 : // but in reality it is. see:
     684                 : // https://groups.google.com/forum/#!topic/spdy-dev/2pWxxOZEIcs
     685                 : 
     686                 : const char *SpdyStream::kDictionary =
     687                 :   "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
     688                 :   "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
     689                 :   "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
     690                 :   "-agent10010120020120220320420520630030130230330430530630740040140240340440"
     691                 :   "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
     692                 :   "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
     693                 :   "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
     694                 :   "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
     695                 :   "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
     696                 :   "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
     697                 :   "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
     698                 :   "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
     699                 :   ".1statusversionurl";
     700                 : 
     701                 : // use for zlib data types
     702                 : void *
     703               0 : SpdyStream::zlib_allocator(void *opaque, uInt items, uInt size)
     704                 : {
     705               0 :   return moz_xmalloc(items * size);
     706                 : }
     707                 : 
     708                 : // use for zlib data types
     709                 : void
     710               0 : SpdyStream::zlib_destructor(void *opaque, void *addr)
     711                 : {
     712               0 :   moz_free(addr);
     713               0 : }
     714                 : 
     715                 : void
     716               0 : SpdyStream::ExecuteCompress(PRUint32 flushMode)
     717                 : {
     718                 :   // Expect mZlib->avail_in and mZlib->next_in to be set.
     719                 :   // Append the compressed version of next_in to mTxInlineFrame
     720                 : 
     721               0 :   do
     722                 :   {
     723               0 :     PRUint32 avail = mTxInlineFrameSize - mTxInlineFrameUsed;
     724               0 :     if (avail < 1) {
     725                 :       SpdySession::EnsureBuffer(mTxInlineFrame,
     726                 :                                 mTxInlineFrameSize + 2000,
     727                 :                                 mTxInlineFrameUsed,
     728               0 :                                 mTxInlineFrameSize);
     729               0 :       avail = mTxInlineFrameSize - mTxInlineFrameUsed;
     730                 :     }
     731                 : 
     732               0 :     mZlib->next_out = reinterpret_cast<unsigned char *> (mTxInlineFrame.get()) +
     733               0 :       mTxInlineFrameUsed;
     734               0 :     mZlib->avail_out = avail;
     735               0 :     deflate(mZlib, flushMode);
     736               0 :     mTxInlineFrameUsed += avail - mZlib->avail_out;
     737               0 :   } while (mZlib->avail_in > 0 || !mZlib->avail_out);
     738               0 : }
     739                 : 
     740                 : void
     741               0 : SpdyStream::CompressToFrame(PRUint16 data)
     742                 : {
     743                 :   // convert the data to network byte order and write that
     744                 :   // to the compressed stream
     745                 :   
     746               0 :   data = PR_htons(data);
     747                 : 
     748               0 :   mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
     749               0 :   mZlib->avail_in = 2;
     750               0 :   ExecuteCompress(Z_NO_FLUSH);
     751               0 : }
     752                 : 
     753                 : 
     754                 : void
     755               0 : SpdyStream::CompressToFrame(const char *data, PRUint32 len)
     756                 : {
     757                 :   // Format calls for a network ordered 16 bit length
     758                 :   // followed by the utf8 string
     759                 : 
     760                 :   // for now, silently truncate headers greater than 64KB. Spdy/3 will
     761                 :   // fix this by making the len a 32 bit quantity
     762               0 :   if (len > 0xffff)
     763               0 :     len = 0xffff;
     764                 : 
     765               0 :   PRUint16 networkLen = PR_htons(len);
     766                 :   
     767                 :   // write out the length
     768               0 :   mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
     769               0 :   mZlib->avail_in = 2;
     770               0 :   ExecuteCompress(Z_NO_FLUSH);
     771                 :   
     772                 :   // write out the data
     773               0 :   mZlib->next_in = (unsigned char *)data;
     774               0 :   mZlib->avail_in = len;
     775               0 :   ExecuteCompress(Z_NO_FLUSH);
     776               0 : }
     777                 : 
     778                 : void
     779               0 : SpdyStream::CompressFlushFrame()
     780                 : {
     781               0 :   mZlib->next_in = (unsigned char *) "";
     782               0 :   mZlib->avail_in = 0;
     783               0 :   ExecuteCompress(Z_SYNC_FLUSH);
     784               0 : }
     785                 : 
     786                 : void
     787               0 : SpdyStream::Close(nsresult reason)
     788                 : {
     789               0 :   mTransaction->Close(reason);
     790               0 : }
     791                 : 
     792                 : //-----------------------------------------------------------------------------
     793                 : // nsAHttpSegmentReader
     794                 : //-----------------------------------------------------------------------------
     795                 : 
     796                 : nsresult
     797               0 : SpdyStream::OnReadSegment(const char *buf,
     798                 :                           PRUint32 count,
     799                 :                           PRUint32 *countRead)
     800                 : {
     801               0 :   LOG3(("SpdyStream::OnReadSegment %p count=%d state=%x",
     802                 :         this, count, mUpstreamState));
     803                 : 
     804               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     805               0 :   NS_ABORT_IF_FALSE(mSegmentReader, "OnReadSegment with null mSegmentReader");
     806                 :   
     807               0 :   nsresult rv = NS_ERROR_UNEXPECTED;
     808                 :   PRUint32 dataLength;
     809                 : 
     810               0 :   switch (mUpstreamState) {
     811                 :   case GENERATING_SYN_STREAM:
     812                 :     // The buffer is the HTTP request stream, including at least part of the
     813                 :     // HTTP request header. This state's job is to build a SYN_STREAM frame
     814                 :     // from the header information. count is the number of http bytes available
     815                 :     // (which may include more than the header), and in countRead we return
     816                 :     // the number of those bytes that we consume (i.e. the portion that are
     817                 :     // header bytes)
     818                 : 
     819               0 :     rv = ParseHttpRequestHeaders(buf, count, countRead);
     820               0 :     if (NS_FAILED(rv))
     821               0 :       return rv;
     822               0 :     LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
     823                 :           this, *countRead, count, mSynFrameComplete));
     824               0 :     if (mSynFrameComplete) {
     825               0 :       NS_ABORT_IF_FALSE(mTxInlineFrameUsed,
     826                 :                         "OnReadSegment SynFrameComplete 0b");
     827               0 :       rv = TransmitFrame(nsnull, nsnull);
     828               0 :       NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
     829                 :                         "Transmit Frame should be all or nothing");
     830                 : 
     831                 :       // normalize a blocked write into an ok one if we have consumed the data
     832                 :       // while parsing headers as some code will take WOULD_BLOCK to mean an
     833                 :       // error with nothing processed.
     834                 :       // (e.g. nsHttpTransaction::ReadRequestSegment())
     835               0 :       if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
     836               0 :         rv = NS_OK;
     837                 : 
     838                 :       // mTxInlineFrameUsed > 0 means the current frame is in progress
     839                 :       // of sending.  mTxInlineFrameUsed is dropped to 0 after both the frame
     840                 :       // and its payload (if any) are completely sent out.  Here during
     841                 :       // GENERATING_SYN_STREAM state we are sending just the http headers.
     842                 :       // Only when the frame is completely sent out do we proceed to
     843                 :       // GENERATING_REQUEST_BODY state.
     844                 : 
     845               0 :       if (mTxInlineFrameUsed)
     846               0 :         ChangeState(SENDING_SYN_STREAM);
     847                 :       else
     848               0 :         ChangeState(GENERATING_REQUEST_BODY);
     849               0 :       break;
     850                 :     }
     851               0 :     NS_ABORT_IF_FALSE(*countRead == count,
     852                 :                       "Header parsing not complete but unused data");
     853               0 :     break;
     854                 : 
     855                 :   case GENERATING_REQUEST_BODY:
     856               0 :     dataLength = NS_MIN(count, mChunkSize);
     857               0 :     LOG3(("SpdyStream %p id %x request len remaining %d, "
     858                 :           "count avail %d, chunk used %d",
     859                 :           this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
     860               0 :     if (dataLength > mRequestBodyLenRemaining)
     861               0 :       return NS_ERROR_UNEXPECTED;
     862               0 :     mRequestBodyLenRemaining -= dataLength;
     863               0 :     GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
     864               0 :     ChangeState(SENDING_REQUEST_BODY);
     865                 :     // NO BREAK
     866                 : 
     867                 :   case SENDING_REQUEST_BODY:
     868               0 :     NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
     869               0 :     rv = TransmitFrame(buf, countRead);
     870               0 :     NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
     871                 :                       "Transmit Frame should be all or nothing");
     872                 : 
     873               0 :     LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
     874                 :           "Header is %d Body is %d.",
     875                 :           rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
     876                 : 
     877                 :     // normalize a partial write with a WOULD_BLOCK into just a partial write
     878                 :     // as some code will take WOULD_BLOCK to mean an error with nothing
     879                 :     // written (e.g. nsHttpTransaction::ReadRequestSegment()
     880               0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
     881               0 :       rv = NS_OK;
     882                 : 
     883                 :     // If that frame was all sent, look for another one
     884               0 :     if (!mTxInlineFrameUsed)
     885               0 :         ChangeState(GENERATING_REQUEST_BODY);
     886               0 :     break;
     887                 : 
     888                 :   case SENDING_SYN_STREAM:
     889               0 :     rv = NS_BASE_STREAM_WOULD_BLOCK;
     890               0 :     break;
     891                 : 
     892                 :   case SENDING_FIN_STREAM:
     893               0 :     NS_ABORT_IF_FALSE(false,
     894                 :                       "resuming partial fin stream out of OnReadSegment");
     895               0 :     break;
     896                 :     
     897                 :   default:
     898               0 :     NS_ABORT_IF_FALSE(false, "SpdyStream::OnReadSegment non-write state");
     899               0 :     break;
     900                 :   }
     901                 :   
     902               0 :   return rv;
     903                 : }
     904                 : 
     905                 : //-----------------------------------------------------------------------------
     906                 : // nsAHttpSegmentWriter
     907                 : //-----------------------------------------------------------------------------
     908                 : 
     909                 : nsresult
     910               0 : SpdyStream::OnWriteSegment(char *buf,
     911                 :                            PRUint32 count,
     912                 :                            PRUint32 *countWritten)
     913                 : {
     914               0 :   LOG3(("SpdyStream::OnWriteSegment %p count=%d state=%x",
     915                 :         this, count, mUpstreamState));
     916                 : 
     917               0 :   NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     918               0 :   NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
     919                 : 
     920               0 :   return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
     921                 : }
     922                 : 
     923                 : } // namespace mozilla::net
     924                 : } // namespace mozilla
     925                 : 

Generated by: LCOV version 1.7