LCOV - app.info - netwerk/protocol/http/SpdyStream.cpp

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-07-07 | 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