LCOV - code coverage report
Current view: top level - dom/flyweb - HttpServer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 642 0.2 %
Date: 2017-07-14 16:53:18 Functions: 2 76 2.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/HttpServer.h"
       8             : #include "nsISocketTransport.h"
       9             : #include "nsWhitespaceTokenizer.h"
      10             : #include "nsNetUtil.h"
      11             : #include "nsIStreamTransportService.h"
      12             : #include "nsIAsyncStreamCopier2.h"
      13             : #include "nsIPipe.h"
      14             : #include "nsIOService.h"
      15             : #include "nsIHttpChannelInternal.h"
      16             : #include "Base64.h"
      17             : #include "WebSocketChannel.h"
      18             : #include "nsCharSeparatedTokenizer.h"
      19             : #include "nsIX509Cert.h"
      20             : 
      21             : static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
      22             : 
      23             : namespace mozilla {
      24             : namespace dom {
      25             : 
      26             : static LazyLogModule gHttpServerLog("HttpServer");
      27             : #undef LOG_I
      28             : #define LOG_I(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
      29             : #undef LOG_V
      30             : #define LOG_V(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Verbose, (__VA_ARGS__))
      31             : #undef LOG_E
      32             : #define LOG_E(...) MOZ_LOG(gHttpServerLog, mozilla::LogLevel::Error, (__VA_ARGS__))
      33             : 
      34             : 
      35           0 : NS_IMPL_ISUPPORTS(HttpServer,
      36             :                   nsIServerSocketListener,
      37             :                   nsILocalCertGetCallback)
      38             : 
      39           0 : HttpServer::HttpServer(nsISerialEventTarget* aEventTarget)
      40             :   : mPort()
      41             :   , mHttps()
      42           0 :   , mEventTarget(aEventTarget)
      43             : {
      44           0 : }
      45             : 
      46           0 : HttpServer::~HttpServer()
      47             : {
      48           0 : }
      49             : 
      50             : void
      51           0 : HttpServer::Init(int32_t aPort, bool aHttps, HttpServerListener* aListener)
      52             : {
      53           0 :   mPort = aPort;
      54           0 :   mHttps = aHttps;
      55           0 :   mListener = aListener;
      56             : 
      57           0 :   if (mHttps) {
      58             :     nsCOMPtr<nsILocalCertService> lcs =
      59           0 :       do_CreateInstance("@mozilla.org/security/local-cert-service;1");
      60           0 :     nsresult rv = lcs->GetOrCreateCert(NS_LITERAL_CSTRING("flyweb"), this);
      61           0 :     if (NS_FAILED(rv)) {
      62           0 :       NotifyStarted(rv);
      63             :     }
      64             :   } else {
      65             :     // Make sure to always have an async step before notifying callbacks
      66           0 :     HandleCert(nullptr, NS_OK);
      67             :   }
      68           0 : }
      69             : 
      70             : NS_IMETHODIMP
      71           0 : HttpServer::HandleCert(nsIX509Cert* aCert, nsresult aResult)
      72             : {
      73           0 :   nsresult rv = aResult;
      74           0 :   if (NS_SUCCEEDED(rv)) {
      75           0 :     rv = StartServerSocket(aCert);
      76             :   }
      77             : 
      78           0 :   if (NS_FAILED(rv) && mServerSocket) {
      79           0 :     mServerSocket->Close();
      80           0 :     mServerSocket = nullptr;
      81             :   }
      82             : 
      83           0 :   NotifyStarted(rv);
      84             : 
      85           0 :   return NS_OK;
      86             : }
      87             : 
      88             : void
      89           0 : HttpServer::NotifyStarted(nsresult aStatus)
      90             : {
      91           0 :   RefPtr<HttpServerListener> listener = mListener;
      92           0 :   nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
      93             :     "dom::HttpServer::NotifyStarted",
      94           0 :     [listener, aStatus]() { listener->OnServerStarted(aStatus); });
      95           0 :   NS_DispatchToCurrentThread(event);
      96           0 : }
      97             : 
      98             : nsresult
      99           0 : HttpServer::StartServerSocket(nsIX509Cert* aCert)
     100             : {
     101             :   nsresult rv;
     102             :   mServerSocket =
     103           0 :     do_CreateInstance(aCert ? "@mozilla.org/network/tls-server-socket;1"
     104           0 :                             : "@mozilla.org/network/server-socket;1", &rv);
     105           0 :   NS_ENSURE_SUCCESS(rv, rv);
     106             : 
     107           0 :   rv = mServerSocket->Init(mPort, false, -1);
     108           0 :   NS_ENSURE_SUCCESS(rv, rv);
     109             : 
     110           0 :   if (aCert) {
     111           0 :     nsCOMPtr<nsITLSServerSocket> tls = do_QueryInterface(mServerSocket);
     112           0 :     rv = tls->SetServerCert(aCert);
     113           0 :     NS_ENSURE_SUCCESS(rv, rv);
     114             : 
     115           0 :     rv = tls->SetSessionTickets(false);
     116           0 :     NS_ENSURE_SUCCESS(rv, rv);
     117             : 
     118           0 :     mCert = aCert;
     119             :   }
     120             : 
     121           0 :   rv = mServerSocket->AsyncListen(this);
     122           0 :   NS_ENSURE_SUCCESS(rv, rv);
     123             : 
     124           0 :   rv = mServerSocket->GetPort(&mPort);
     125           0 :   NS_ENSURE_SUCCESS(rv, rv);
     126             : 
     127           0 :   LOG_I("HttpServer::StartServerSocket(%p)", this);
     128             : 
     129           0 :   return NS_OK;
     130             : }
     131             : 
     132             : NS_IMETHODIMP
     133           0 : HttpServer::OnSocketAccepted(nsIServerSocket* aServ,
     134             :                              nsISocketTransport* aTransport)
     135             : {
     136           0 :   MOZ_ASSERT(SameCOMIdentity(aServ, mServerSocket));
     137             : 
     138             :   nsresult rv;
     139           0 :   RefPtr<Connection> conn = new Connection(aTransport, this, rv);
     140           0 :   NS_ENSURE_SUCCESS(rv, rv);
     141             : 
     142           0 :   LOG_I("HttpServer::OnSocketAccepted(%p) - Socket %p", this, conn.get());
     143             : 
     144           0 :   mConnections.AppendElement(conn.forget());
     145             : 
     146           0 :   return NS_OK;
     147             : }
     148             : 
     149             : NS_IMETHODIMP
     150           0 : HttpServer::OnStopListening(nsIServerSocket* aServ,
     151             :                             nsresult aStatus)
     152             : {
     153           0 :   MOZ_ASSERT(aServ == mServerSocket || !mServerSocket);
     154             : 
     155           0 :   LOG_I("HttpServer::OnStopListening(%p) - status 0x%" PRIx32,
     156             :         this, static_cast<uint32_t>(aStatus));
     157             : 
     158           0 :   Close();
     159             : 
     160           0 :   return NS_OK;
     161             : }
     162             : 
     163             : void
     164           0 : HttpServer::SendResponse(InternalRequest* aRequest, InternalResponse* aResponse)
     165             : {
     166           0 :   for (Connection* conn : mConnections) {
     167           0 :     if (conn->TryHandleResponse(aRequest, aResponse)) {
     168           0 :       return;
     169             :     }
     170             :   }
     171             : 
     172           0 :   MOZ_ASSERT(false, "Unknown request");
     173             : }
     174             : 
     175             : already_AddRefed<nsITransportProvider>
     176           0 : HttpServer::AcceptWebSocket(InternalRequest* aConnectRequest,
     177             :                             const Optional<nsAString>& aProtocol,
     178             :                             ErrorResult& aRv)
     179             : {
     180           0 :   for (Connection* conn : mConnections) {
     181           0 :     if (!conn->HasPendingWebSocketRequest(aConnectRequest)) {
     182           0 :       continue;
     183             :     }
     184             :     nsCOMPtr<nsITransportProvider> provider =
     185           0 :       conn->HandleAcceptWebSocket(aProtocol, aRv);
     186           0 :     if (aRv.Failed()) {
     187           0 :       conn->Close();
     188             :     }
     189             :     // This connection is now owned by the websocket, or we just closed it
     190           0 :     mConnections.RemoveElement(conn);
     191           0 :     return provider.forget();
     192             :   }
     193             : 
     194           0 :   MOZ_ASSERT(false, "Unknown request");
     195             :   aRv.Throw(NS_ERROR_UNEXPECTED);
     196             :   return nullptr;
     197             : }
     198             : 
     199             : void
     200           0 : HttpServer::SendWebSocketResponse(InternalRequest* aConnectRequest,
     201             :                                   InternalResponse* aResponse)
     202             : {
     203           0 :   for (Connection* conn : mConnections) {
     204           0 :     if (conn->HasPendingWebSocketRequest(aConnectRequest)) {
     205           0 :       conn->HandleWebSocketResponse(aResponse);
     206           0 :       return;
     207             :     }
     208             :   }
     209             : 
     210           0 :   MOZ_ASSERT(false, "Unknown request");
     211             : }
     212             : 
     213             : void
     214           0 : HttpServer::Close()
     215             : {
     216           0 :   if (mServerSocket) {
     217           0 :     mServerSocket->Close();
     218           0 :     mServerSocket = nullptr;
     219             :   }
     220             : 
     221           0 :   if (mListener) {
     222           0 :     RefPtr<HttpServerListener> listener = mListener.forget();
     223           0 :     listener->OnServerClose();
     224             :   }
     225             : 
     226           0 :   for (Connection* conn : mConnections) {
     227           0 :     conn->Close();
     228             :   }
     229           0 :   mConnections.Clear();
     230           0 : }
     231             : 
     232             : void
     233           0 : HttpServer::GetCertKey(nsACString& aKey)
     234             : {
     235           0 :   nsAutoString tmp;
     236           0 :   if (mCert) {
     237           0 :     mCert->GetSha256Fingerprint(tmp);
     238             :   }
     239           0 :   LossyCopyUTF16toASCII(tmp, aKey);
     240           0 : }
     241             : 
     242           0 : NS_IMPL_ISUPPORTS(HttpServer::TransportProvider,
     243             :                   nsITransportProvider)
     244             : 
     245           0 : HttpServer::TransportProvider::~TransportProvider()
     246             : {
     247           0 : }
     248             : 
     249             : NS_IMETHODIMP
     250           0 : HttpServer::TransportProvider::SetListener(nsIHttpUpgradeListener* aListener)
     251             : {
     252           0 :   MOZ_ASSERT(!mListener);
     253           0 :   MOZ_ASSERT(aListener);
     254             : 
     255           0 :   mListener = aListener;
     256             : 
     257           0 :   MaybeNotify();
     258             : 
     259           0 :   return NS_OK;
     260             : }
     261             : 
     262             : NS_IMETHODIMP
     263           0 : HttpServer::TransportProvider::GetIPCChild(PTransportProviderChild** aChild)
     264             : {
     265           0 :   MOZ_CRASH("Don't call this in parent process");
     266             :   *aChild = nullptr;
     267             :   return NS_OK;
     268             : }
     269             : 
     270             : void
     271           0 : HttpServer::TransportProvider::SetTransport(nsISocketTransport* aTransport,
     272             :                                             nsIAsyncInputStream* aInput,
     273             :                                             nsIAsyncOutputStream* aOutput)
     274             : {
     275           0 :   MOZ_ASSERT(!mTransport);
     276           0 :   MOZ_ASSERT(aTransport && aInput && aOutput);
     277             : 
     278           0 :   mTransport = aTransport;
     279           0 :   mInput = aInput;
     280           0 :   mOutput = aOutput;
     281             : 
     282           0 :   MaybeNotify();
     283           0 : }
     284             : 
     285             : void
     286           0 : HttpServer::TransportProvider::MaybeNotify()
     287             : {
     288           0 :   if (mTransport && mListener) {
     289           0 :     RefPtr<TransportProvider> self = this;
     290           0 :     nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
     291           0 :       "dom::HttpServer::TransportProvider::MaybeNotify", [self, this]() {
     292             :         DebugOnly<nsresult> rv =
     293           0 :           mListener->OnTransportAvailable(mTransport, mInput, mOutput);
     294           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     295           0 :       });
     296           0 :     NS_DispatchToCurrentThread(event);
     297             :   }
     298           0 : }
     299             : 
     300           0 : NS_IMPL_ISUPPORTS(HttpServer::Connection,
     301             :                   nsIInputStreamCallback,
     302             :                   nsIOutputStreamCallback)
     303             : 
     304           0 : HttpServer::Connection::Connection(nsISocketTransport* aTransport,
     305             :                                    HttpServer* aServer,
     306           0 :                                    nsresult& rv)
     307             :   : mServer(aServer)
     308             :   , mTransport(aTransport)
     309             :   , mState(eRequestLine)
     310             :   , mPendingReqVersion()
     311             :   , mRemainingBodySize()
     312           0 :   , mCloseAfterRequest(false)
     313             : {
     314           0 :   nsCOMPtr<nsIInputStream> input;
     315           0 :   rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(input));
     316           0 :   NS_ENSURE_SUCCESS_VOID(rv);
     317             : 
     318           0 :   mInput = do_QueryInterface(input);
     319             : 
     320           0 :   nsCOMPtr<nsIOutputStream> output;
     321           0 :   rv = mTransport->OpenOutputStream(0, 0, 0, getter_AddRefs(output));
     322           0 :   NS_ENSURE_SUCCESS_VOID(rv);
     323             : 
     324           0 :   mOutput = do_QueryInterface(output);
     325             : 
     326           0 :   if (mServer->mHttps) {
     327           0 :     SetSecurityObserver(true);
     328             :   } else {
     329           0 :     mInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
     330             :   }
     331             : }
     332             : 
     333             : NS_IMETHODIMP
     334           0 : HttpServer::Connection::OnHandshakeDone(nsITLSServerSocket* aServer,
     335             :                                         nsITLSClientStatus* aStatus)
     336             : {
     337           0 :   LOG_I("HttpServer::Connection::OnHandshakeDone(%p)", this);
     338             : 
     339             :   // XXX Verify connection security
     340             : 
     341           0 :   SetSecurityObserver(false);
     342           0 :   mInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
     343             : 
     344           0 :   return NS_OK;
     345             : }
     346             : 
     347             : void
     348           0 : HttpServer::Connection::SetSecurityObserver(bool aListen)
     349             : {
     350           0 :   LOG_I("HttpServer::Connection::SetSecurityObserver(%p) - %s", this,
     351             :     aListen ? "On" : "Off");
     352             : 
     353           0 :   nsCOMPtr<nsISupports> secInfo;
     354           0 :   mTransport->GetSecurityInfo(getter_AddRefs(secInfo));
     355             :   nsCOMPtr<nsITLSServerConnectionInfo> tlsConnInfo =
     356           0 :     do_QueryInterface(secInfo);
     357           0 :   MOZ_ASSERT(tlsConnInfo);
     358           0 :   tlsConnInfo->SetSecurityObserver(aListen ? this : nullptr);
     359           0 : }
     360             : 
     361           0 : HttpServer::Connection::~Connection()
     362             : {
     363           0 : }
     364             : 
     365             : NS_IMETHODIMP
     366           0 : HttpServer::Connection::OnInputStreamReady(nsIAsyncInputStream* aStream)
     367             : {
     368           0 :   MOZ_ASSERT(!mInput || aStream == mInput);
     369             : 
     370           0 :   LOG_I("HttpServer::Connection::OnInputStreamReady(%p)", this);
     371             : 
     372           0 :   if (!mInput || mState == ePause) {
     373           0 :     return NS_OK;
     374             :   }
     375             : 
     376             :   uint64_t avail;
     377           0 :   nsresult rv = mInput->Available(&avail);
     378           0 :   if (NS_FAILED(rv)) {
     379           0 :     LOG_I("HttpServer::Connection::OnInputStreamReady(%p) - Connection closed", this);
     380             : 
     381           0 :     mServer->mConnections.RemoveElement(this);
     382             :     // Connection closed. Handle errors here.
     383           0 :     return NS_OK;
     384             :   }
     385             : 
     386             :   uint32_t numRead;
     387           0 :   rv = mInput->ReadSegments(ReadSegmentsFunc,
     388             :                             this,
     389             :                             UINT32_MAX,
     390           0 :                             &numRead);
     391           0 :   NS_ENSURE_SUCCESS(rv, rv);
     392             : 
     393           0 :   rv = mInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
     394           0 :   NS_ENSURE_SUCCESS(rv, rv);
     395             : 
     396           0 :   return NS_OK;
     397             : }
     398             : 
     399             : nsresult
     400           0 : HttpServer::Connection::ReadSegmentsFunc(nsIInputStream* aIn,
     401             :                                          void* aClosure,
     402             :                                          const char* aBuffer,
     403             :                                          uint32_t aToOffset,
     404             :                                          uint32_t aCount,
     405             :                                          uint32_t* aWriteCount)
     406             : {
     407           0 :   const char* buffer = aBuffer;
     408             :   nsresult rv = static_cast<HttpServer::Connection*>(aClosure)->
     409           0 :     ConsumeInput(buffer, buffer + aCount);
     410             : 
     411           0 :   *aWriteCount = buffer - aBuffer;
     412           0 :   MOZ_ASSERT(*aWriteCount <= aCount);
     413             : 
     414           0 :   return rv;
     415             : }
     416             : 
     417             : static const char*
     418           0 : findCRLF(const char* aBuffer, const char* aEnd)
     419             : {
     420           0 :   if (aBuffer + 1 >= aEnd) {
     421           0 :     return nullptr;
     422             :   }
     423             : 
     424             :   const char* pos;
     425           0 :   while ((pos = static_cast<const char*>(memchr(aBuffer,
     426             :                                                 '\r',
     427           0 :                                                 aEnd - aBuffer - 1)))) {
     428           0 :     if (*(pos + 1) == '\n') {
     429           0 :       return pos;
     430             :     }
     431           0 :     aBuffer = pos + 1;
     432             :   }
     433           0 :   return nullptr;
     434             : }
     435             : 
     436             : nsresult
     437           0 : HttpServer::Connection::ConsumeInput(const char*& aBuffer,
     438             :                                      const char* aEnd)
     439             : {
     440             :   nsresult rv;
     441           0 :   while (mState == eRequestLine ||
     442           0 :          mState == eHeaders) {
     443             :     // Consume line-by-line
     444             : 
     445             :     // Check if buffer boundry ended up right between the CR and LF
     446           0 :     if (!mInputBuffer.IsEmpty() && mInputBuffer.Last() == '\r' &&
     447           0 :         *aBuffer == '\n') {
     448           0 :       aBuffer++;
     449           0 :       rv = ConsumeLine(mInputBuffer.BeginReading(), mInputBuffer.Length() - 1);
     450           0 :       NS_ENSURE_SUCCESS(rv, rv);
     451             : 
     452           0 :       mInputBuffer.Truncate();
     453             :     }
     454             : 
     455             :     // Look for a CRLF
     456           0 :     const char* pos = findCRLF(aBuffer, aEnd);
     457           0 :     if (!pos) {
     458           0 :       mInputBuffer.Append(aBuffer, aEnd - aBuffer);
     459           0 :       aBuffer = aEnd;
     460           0 :       return NS_OK;
     461             :     }
     462             : 
     463           0 :     if (!mInputBuffer.IsEmpty()) {
     464           0 :       mInputBuffer.Append(aBuffer, pos - aBuffer);
     465           0 :       aBuffer = pos + 2;
     466           0 :       rv = ConsumeLine(mInputBuffer.BeginReading(), mInputBuffer.Length() - 1);
     467           0 :       NS_ENSURE_SUCCESS(rv, rv);
     468             : 
     469           0 :       mInputBuffer.Truncate();
     470             :     } else {
     471           0 :       rv = ConsumeLine(aBuffer, pos - aBuffer);
     472           0 :       NS_ENSURE_SUCCESS(rv, rv);
     473             : 
     474           0 :       aBuffer = pos + 2;
     475             :     }
     476             :   }
     477             : 
     478           0 :   if (mState == eBody) {
     479             :     uint32_t size = std::min(mRemainingBodySize,
     480           0 :                              static_cast<uint32_t>(aEnd - aBuffer));
     481           0 :     uint32_t written = size;
     482             : 
     483           0 :     if (mCurrentRequestBody) {
     484           0 :       rv = mCurrentRequestBody->Write(aBuffer, size, &written);
     485             :       // Since we've given the pipe unlimited size, we should never
     486             :       // end up needing to block.
     487           0 :       MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK);
     488           0 :       if (NS_FAILED(rv)) {
     489           0 :         written = size;
     490           0 :         mCurrentRequestBody = nullptr;
     491             :       }
     492             :     }
     493             : 
     494           0 :     aBuffer += written;
     495           0 :     mRemainingBodySize -= written;
     496           0 :     if (!mRemainingBodySize) {
     497           0 :       mCurrentRequestBody->Close();
     498           0 :       mCurrentRequestBody = nullptr;
     499           0 :       mState = eRequestLine;
     500             :     }
     501             :   }
     502             : 
     503           0 :   return NS_OK;
     504             : }
     505             : 
     506             : bool
     507           0 : ContainsToken(const nsCString& aList, const nsCString& aToken)
     508             : {
     509           0 :   nsCCharSeparatedTokenizer tokens(aList, ',');
     510           0 :   bool found = false;
     511           0 :   while (!found && tokens.hasMoreTokens()) {
     512           0 :     found = tokens.nextToken().Equals(aToken);
     513             :   }
     514           0 :   return found;
     515             : }
     516             : 
     517             : static bool
     518           0 : IsWebSocketRequest(InternalRequest* aRequest, uint32_t aHttpVersion)
     519             : {
     520           0 :   if (aHttpVersion < 1) {
     521           0 :     return false;
     522             :   }
     523             : 
     524           0 :   nsAutoCString str;
     525           0 :   aRequest->GetMethod(str);
     526           0 :   if (!str.EqualsLiteral("GET")) {
     527           0 :     return false;
     528             :   }
     529             : 
     530           0 :   InternalHeaders* headers = aRequest->Headers();
     531           0 :   ErrorResult res;
     532             : 
     533           0 :   headers->GetFirst(NS_LITERAL_CSTRING("upgrade"), str, res);
     534           0 :   MOZ_ASSERT(!res.Failed());
     535           0 :   if (!str.EqualsLiteral("websocket")) {
     536           0 :     return false;
     537             :   }
     538             : 
     539           0 :   headers->GetFirst(NS_LITERAL_CSTRING("connection"), str, res);
     540           0 :   MOZ_ASSERT(!res.Failed());
     541           0 :   if (!ContainsToken(str, NS_LITERAL_CSTRING("Upgrade"))) {
     542           0 :     return false;
     543             :   }
     544             : 
     545           0 :   headers->GetFirst(NS_LITERAL_CSTRING("sec-websocket-key"), str, res);
     546           0 :   MOZ_ASSERT(!res.Failed());
     547           0 :   nsAutoCString binary;
     548           0 :   if (NS_FAILED(Base64Decode(str, binary)) || binary.Length() != 16) {
     549           0 :     return false;
     550             :   }
     551             : 
     552             :   nsresult rv;
     553           0 :   headers->GetFirst(NS_LITERAL_CSTRING("sec-websocket-version"), str, res);
     554           0 :   MOZ_ASSERT(!res.Failed());
     555           0 :   if (str.ToInteger(&rv) != 13 || NS_FAILED(rv)) {
     556           0 :     return false;
     557             :   }
     558             : 
     559           0 :   return true;
     560             : }
     561             : 
     562             : nsresult
     563           0 : HttpServer::Connection::ConsumeLine(const char* aBuffer,
     564             :                                     size_t aLength)
     565             : {
     566           0 :   MOZ_ASSERT(mState == eRequestLine ||
     567             :              mState == eHeaders);
     568             : 
     569           0 :   if (MOZ_LOG_TEST(gHttpServerLog, mozilla::LogLevel::Verbose)) {
     570           0 :     nsCString line(aBuffer, aLength);
     571           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - \"%s\"", this, line.get());
     572             :   }
     573             : 
     574           0 :   if (mState == eRequestLine) {
     575           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsing request line", this);
     576           0 :     NS_ENSURE_FALSE(mCloseAfterRequest, NS_ERROR_UNEXPECTED);
     577             : 
     578           0 :     if (aLength == 0) {
     579             :       // Ignore empty lines before the request line
     580           0 :       return NS_OK;
     581             :     }
     582           0 :     MOZ_ASSERT(!mPendingReq);
     583             : 
     584             :     // Process request line
     585           0 :     nsCWhitespaceTokenizer tokens(Substring(aBuffer, aLength));
     586             : 
     587           0 :     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     588           0 :     nsDependentCSubstring method = tokens.nextToken();
     589           0 :     NS_ENSURE_TRUE(NS_IsValidHTTPToken(method), NS_ERROR_UNEXPECTED);
     590           0 :     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     591           0 :     nsDependentCSubstring url = tokens.nextToken();
     592             :     // Seems like it's also allowed to pass full urls with scheme+host+port.
     593             :     // May need to support that.
     594           0 :     NS_ENSURE_TRUE(url.First() == '/', NS_ERROR_UNEXPECTED);
     595           0 :     mPendingReq = new InternalRequest(url, /* aURLFragment */ EmptyCString());
     596           0 :     mPendingReq->SetMethod(method);
     597           0 :     NS_ENSURE_TRUE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     598           0 :     nsDependentCSubstring version = tokens.nextToken();
     599           0 :     NS_ENSURE_TRUE(StringBeginsWith(version, NS_LITERAL_CSTRING("HTTP/1.")),
     600             :                    NS_ERROR_UNEXPECTED);
     601             :     nsresult rv;
     602             :     // This integer parsing is likely not strict enough.
     603           0 :     nsCString reqVersion;
     604           0 :     reqVersion = Substring(version, MOZ_ARRAY_LENGTH("HTTP/1.") - 1);
     605           0 :     mPendingReqVersion = reqVersion.ToInteger(&rv);
     606           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
     607             : 
     608           0 :     NS_ENSURE_FALSE(tokens.hasMoreTokens(), NS_ERROR_UNEXPECTED);
     609             : 
     610           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsed request line", this);
     611             : 
     612           0 :     mState = eHeaders;
     613             : 
     614           0 :     return NS_OK;
     615             :   }
     616             : 
     617           0 :   if (aLength == 0) {
     618           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Found end of headers", this);
     619             : 
     620           0 :     MaybeAddPendingHeader();
     621             : 
     622           0 :     ErrorResult res;
     623           0 :     mPendingReq->Headers()->SetGuard(HeadersGuardEnum::Immutable, res);
     624             : 
     625             :     // Check for WebSocket
     626           0 :     if (IsWebSocketRequest(mPendingReq, mPendingReqVersion)) {
     627           0 :       LOG_V("HttpServer::Connection::ConsumeLine(%p) - Fire OnWebSocket", this);
     628             : 
     629           0 :       mState = ePause;
     630           0 :       mPendingWebSocketRequest = mPendingReq.forget();
     631           0 :       mPendingReqVersion = 0;
     632             : 
     633           0 :       RefPtr<HttpServerListener> listener = mServer->mListener;
     634           0 :       RefPtr<InternalRequest> request = mPendingWebSocketRequest;
     635           0 :       nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
     636             :         "dom::HttpServer::Connection::ConsumeLine",
     637           0 :         [listener, request]() { listener->OnWebSocket(request); });
     638           0 :       NS_DispatchToCurrentThread(event);
     639             : 
     640           0 :       return NS_OK;
     641             :     }
     642             : 
     643           0 :     nsAutoCString header;
     644           0 :     mPendingReq->Headers()->GetFirst(NS_LITERAL_CSTRING("connection"),
     645             :                                      header,
     646           0 :                                      res);
     647           0 :     MOZ_ASSERT(!res.Failed());
     648             :     // 1.0 defaults to closing connections.
     649             :     // 1.1 and higher defaults to keep-alive.
     650           0 :     if (ContainsToken(header, NS_LITERAL_CSTRING("close")) ||
     651           0 :         (mPendingReqVersion == 0 &&
     652           0 :          !ContainsToken(header, NS_LITERAL_CSTRING("keep-alive")))) {
     653           0 :       mCloseAfterRequest = true;
     654             :     }
     655             : 
     656           0 :     mPendingReq->Headers()->GetFirst(NS_LITERAL_CSTRING("content-length"),
     657             :                                      header,
     658           0 :                                      res);
     659           0 :     MOZ_ASSERT(!res.Failed());
     660             : 
     661           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - content-length is \"%s\"",
     662             :           this, header.get());
     663             : 
     664           0 :     if (!header.IsEmpty()) {
     665             :       nsresult rv;
     666           0 :       mRemainingBodySize = header.ToInteger(&rv);
     667           0 :       NS_ENSURE_SUCCESS(rv, rv);
     668             :     } else {
     669           0 :       mRemainingBodySize = 0;
     670             :     }
     671             : 
     672           0 :     if (mRemainingBodySize) {
     673           0 :       LOG_V("HttpServer::Connection::ConsumeLine(%p) - Starting consume body", this);
     674           0 :       mState = eBody;
     675             : 
     676             :       // We use an unlimited buffer size here to ensure
     677             :       // that we get to the next request even if the webpage hangs on
     678             :       // to the request indefinitely without consuming the body.
     679           0 :       nsCOMPtr<nsIInputStream> input;
     680           0 :       nsCOMPtr<nsIOutputStream> output;
     681           0 :       nsresult rv = NS_NewPipe(getter_AddRefs(input),
     682           0 :                                getter_AddRefs(output),
     683             :                                0,          // Segment size
     684             :                                UINT32_MAX, // Unlimited buffer size
     685             :                                false,      // not nonBlockingInput
     686           0 :                                true);      // nonBlockingOutput
     687           0 :       NS_ENSURE_SUCCESS(rv, rv);
     688             : 
     689           0 :       mCurrentRequestBody = do_QueryInterface(output);
     690           0 :       mPendingReq->SetBody(input);
     691             :     } else {
     692           0 :       LOG_V("HttpServer::Connection::ConsumeLine(%p) - No body", this);
     693           0 :       mState = eRequestLine;
     694             :     }
     695             : 
     696           0 :     mPendingRequests.AppendElement(PendingRequest(mPendingReq, nullptr));
     697             : 
     698           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Fire OnRequest", this);
     699             : 
     700           0 :     RefPtr<HttpServerListener> listener = mServer->mListener;
     701           0 :     RefPtr<InternalRequest> request = mPendingReq.forget();
     702           0 :     nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
     703             :       "dom::HttpServer::Connection::ConsumeLine",
     704           0 :       [listener, request]() { listener->OnRequest(request); });
     705           0 :     NS_DispatchToCurrentThread(event);
     706             : 
     707           0 :     mPendingReqVersion = 0;
     708             : 
     709           0 :     return NS_OK;
     710             :   }
     711             : 
     712             :   // Parse header line
     713           0 :   if (aBuffer[0] == ' ' || aBuffer[0] == '\t') {
     714           0 :     LOG_V("HttpServer::Connection::ConsumeLine(%p) - Add to header %s",
     715             :           this,
     716             :           mPendingHeaderName.get());
     717             : 
     718           0 :     NS_ENSURE_FALSE(mPendingHeaderName.IsEmpty(),
     719             :                     NS_ERROR_UNEXPECTED);
     720             : 
     721             :     // We might need to do whitespace trimming/compression here.
     722           0 :     mPendingHeaderValue.Append(aBuffer, aLength);
     723           0 :     return NS_OK;
     724             :   }
     725             : 
     726           0 :   MaybeAddPendingHeader();
     727             : 
     728           0 :   const char* colon = static_cast<const char*>(memchr(aBuffer, ':', aLength));
     729           0 :   NS_ENSURE_TRUE(colon, NS_ERROR_UNEXPECTED);
     730             : 
     731           0 :   ToLowerCase(Substring(aBuffer, colon - aBuffer), mPendingHeaderName);
     732           0 :   mPendingHeaderValue.Assign(colon + 1, aLength - (colon - aBuffer) - 1);
     733             : 
     734           0 :   NS_ENSURE_TRUE(NS_IsValidHTTPToken(mPendingHeaderName),
     735             :                  NS_ERROR_UNEXPECTED);
     736             : 
     737           0 :   LOG_V("HttpServer::Connection::ConsumeLine(%p) - Parsed header %s",
     738             :         this,
     739             :         mPendingHeaderName.get());
     740             : 
     741           0 :   return NS_OK;
     742             : }
     743             : 
     744             : void
     745           0 : HttpServer::Connection::MaybeAddPendingHeader()
     746             : {
     747           0 :   if (mPendingHeaderName.IsEmpty()) {
     748           0 :     return;
     749             :   }
     750             : 
     751             :   // We might need to do more whitespace trimming/compression here.
     752           0 :   mPendingHeaderValue.Trim(" \t");
     753             : 
     754           0 :   ErrorResult rv;
     755           0 :   mPendingReq->Headers()->Append(mPendingHeaderName, mPendingHeaderValue, rv);
     756           0 :   mPendingHeaderName.Truncate();
     757             : }
     758             : 
     759             : bool
     760           0 : HttpServer::Connection::TryHandleResponse(InternalRequest* aRequest,
     761             :                                           InternalResponse* aResponse)
     762             : {
     763           0 :   bool handledResponse = false;
     764           0 :   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
     765           0 :     PendingRequest& pending = mPendingRequests[i];
     766           0 :     if (pending.first() == aRequest) {
     767           0 :       MOZ_ASSERT(!handledResponse);
     768           0 :       MOZ_ASSERT(!pending.second());
     769             : 
     770           0 :       pending.second() = aResponse;
     771           0 :       if (i != 0) {
     772           0 :         return true;
     773             :       }
     774           0 :       handledResponse = true;
     775             :     }
     776             : 
     777           0 :     if (handledResponse && !pending.second()) {
     778             :       // Shortcut if we've handled the response, and
     779             :       // we don't have more responses to send
     780           0 :       return true;
     781             :     }
     782             : 
     783           0 :     if (i == 0 && pending.second()) {
     784           0 :       RefPtr<InternalResponse> resp = pending.second().forget();
     785           0 :       mPendingRequests.RemoveElementAt(0);
     786           0 :       QueueResponse(resp);
     787           0 :       --i;
     788             :     }
     789             :   }
     790             : 
     791           0 :   return handledResponse;
     792             : }
     793             : 
     794             : already_AddRefed<nsITransportProvider>
     795           0 : HttpServer::Connection::HandleAcceptWebSocket(const Optional<nsAString>& aProtocol,
     796             :                                               ErrorResult& aRv)
     797             : {
     798           0 :   MOZ_ASSERT(mPendingWebSocketRequest);
     799             : 
     800             :   RefPtr<InternalResponse> response =
     801           0 :     new InternalResponse(101, NS_LITERAL_CSTRING("Switching Protocols"));
     802             : 
     803           0 :   InternalHeaders* headers = response->Headers();
     804           0 :   headers->Set(NS_LITERAL_CSTRING("Upgrade"),
     805           0 :                NS_LITERAL_CSTRING("websocket"),
     806           0 :                aRv);
     807           0 :   headers->Set(NS_LITERAL_CSTRING("Connection"),
     808           0 :                NS_LITERAL_CSTRING("Upgrade"),
     809           0 :                aRv);
     810           0 :   if (aProtocol.WasPassed()) {
     811           0 :     NS_ConvertUTF16toUTF8 protocol(aProtocol.Value());
     812           0 :     nsAutoCString reqProtocols;
     813             :     mPendingWebSocketRequest->Headers()->
     814           0 :       GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv);
     815           0 :     if (!ContainsToken(reqProtocols, protocol)) {
     816             :       // Should throw a better error here
     817           0 :       aRv.Throw(NS_ERROR_FAILURE);
     818           0 :       return nullptr;
     819             :     }
     820             : 
     821           0 :     headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
     822           0 :                  protocol, aRv);
     823             :   }
     824             : 
     825           0 :   nsAutoCString key, hash;
     826             :   mPendingWebSocketRequest->Headers()->
     827           0 :     GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Key"), key, aRv);
     828           0 :   nsresult rv = mozilla::net::CalculateWebSocketHashedSecret(key, hash);
     829           0 :   if (NS_FAILED(rv)) {
     830           0 :     aRv.Throw(rv);
     831           0 :     return nullptr;
     832             :   }
     833           0 :   headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Accept"), hash, aRv);
     834             : 
     835           0 :   nsAutoCString extensions, negotiatedExtensions;
     836             :   mPendingWebSocketRequest->Headers()->
     837           0 :     GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions, aRv);
     838             :   mozilla::net::ProcessServerWebSocketExtensions(extensions,
     839           0 :                                                  negotiatedExtensions);
     840           0 :   if (!negotiatedExtensions.IsEmpty()) {
     841           0 :     headers->Set(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
     842           0 :                  negotiatedExtensions, aRv);
     843             :   }
     844             : 
     845           0 :   RefPtr<TransportProvider> result = new TransportProvider();
     846           0 :   mWebSocketTransportProvider = result;
     847             : 
     848           0 :   QueueResponse(response);
     849             : 
     850           0 :   return result.forget();
     851             : }
     852             : 
     853             : void
     854           0 : HttpServer::Connection::HandleWebSocketResponse(InternalResponse* aResponse)
     855             : {
     856           0 :   MOZ_ASSERT(mPendingWebSocketRequest);
     857             : 
     858           0 :   mState = eRequestLine;
     859           0 :   mPendingWebSocketRequest = nullptr;
     860           0 :   mInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
     861             : 
     862           0 :   QueueResponse(aResponse);
     863           0 : }
     864             : 
     865             : void
     866           0 : HttpServer::Connection::QueueResponse(InternalResponse* aResponse)
     867             : {
     868           0 :   bool chunked = false;
     869             : 
     870           0 :   RefPtr<InternalHeaders> headers = new InternalHeaders(*aResponse->Headers());
     871             :   {
     872           0 :     ErrorResult res;
     873           0 :     headers->SetGuard(HeadersGuardEnum::None, res);
     874             :   }
     875           0 :   nsCOMPtr<nsIInputStream> body;
     876             :   int64_t bodySize;
     877           0 :   aResponse->GetBody(getter_AddRefs(body), &bodySize);
     878             : 
     879           0 :   if (body && bodySize >= 0) {
     880           0 :     nsCString sizeStr;
     881           0 :     sizeStr.AppendInt(bodySize);
     882             : 
     883           0 :     LOG_V("HttpServer::Connection::QueueResponse(%p) - "
     884             :           "Setting content-length to %s",
     885             :           this, sizeStr.get());
     886             : 
     887           0 :     ErrorResult res;
     888           0 :     headers->Set(NS_LITERAL_CSTRING("content-length"), sizeStr, res);
     889           0 :   } else if (body) {
     890             :     // Use chunked transfer encoding
     891           0 :     LOG_V("HttpServer::Connection::QueueResponse(%p) - Chunked transfer-encoding",
     892             :           this);
     893             : 
     894           0 :     ErrorResult res;
     895           0 :     headers->Set(NS_LITERAL_CSTRING("transfer-encoding"),
     896           0 :                  NS_LITERAL_CSTRING("chunked"),
     897           0 :                  res);
     898           0 :     headers->Delete(NS_LITERAL_CSTRING("content-length"), res);
     899           0 :     chunked = true;
     900             : 
     901             :   } else {
     902           0 :     LOG_V("HttpServer::Connection::QueueResponse(%p) - "
     903             :           "No body - setting content-length to 0", this);
     904             : 
     905           0 :     ErrorResult res;
     906           0 :     headers->Set(NS_LITERAL_CSTRING("content-length"),
     907           0 :                  NS_LITERAL_CSTRING("0"), res);
     908             :   }
     909             : 
     910           0 :   nsCString head(NS_LITERAL_CSTRING("HTTP/1.1 "));
     911           0 :   head.AppendInt(aResponse->GetStatus());
     912             :   // XXX is the statustext security checked?
     913           0 :   head.Append(NS_LITERAL_CSTRING(" ") +
     914           0 :               aResponse->GetStatusText() +
     915           0 :               NS_LITERAL_CSTRING("\r\n"));
     916             : 
     917           0 :   AutoTArray<InternalHeaders::Entry, 16> entries;
     918           0 :   headers->GetEntries(entries);
     919             : 
     920           0 :   for (auto header : entries) {
     921           0 :     head.Append(header.mName +
     922           0 :                 NS_LITERAL_CSTRING(": ") +
     923           0 :                 header.mValue +
     924           0 :                 NS_LITERAL_CSTRING("\r\n"));
     925             :   }
     926             : 
     927           0 :   head.Append(NS_LITERAL_CSTRING("\r\n"));
     928             : 
     929           0 :   mOutputBuffers.AppendElement()->mString = head;
     930           0 :   if (body) {
     931           0 :     OutputBuffer* bodyBuffer = mOutputBuffers.AppendElement();
     932           0 :     bodyBuffer->mStream = body;
     933           0 :     bodyBuffer->mChunked = chunked;
     934             :   }
     935             : 
     936           0 :   OnOutputStreamReady(mOutput);
     937           0 : }
     938             : 
     939             : namespace {
     940             : 
     941             : typedef MozPromise<nsresult, bool, false> StreamCopyPromise;
     942             : 
     943             : class StreamCopier final : public nsIOutputStreamCallback
     944             :                          , public nsIInputStreamCallback
     945             :                          , public nsIRunnable
     946             : {
     947             : public:
     948             :   static RefPtr<StreamCopyPromise>
     949           0 :     Copy(nsIInputStream* aSource, nsIAsyncOutputStream* aSink,
     950             :          bool aChunked)
     951             :   {
     952           0 :     RefPtr<StreamCopier> copier = new StreamCopier(aSource, aSink, aChunked);
     953             : 
     954           0 :     RefPtr<StreamCopyPromise> p = copier->mPromise.Ensure(__func__);
     955             : 
     956           0 :     nsresult rv = copier->mTarget->Dispatch(copier, NS_DISPATCH_NORMAL);
     957           0 :     if (NS_FAILED(rv)) {
     958           0 :       copier->mPromise.Resolve(rv, __func__);
     959             :     }
     960             : 
     961           0 :     return p;
     962             :   }
     963             : 
     964             :   NS_DECL_THREADSAFE_ISUPPORTS
     965             :   NS_DECL_NSIINPUTSTREAMCALLBACK
     966             :   NS_DECL_NSIOUTPUTSTREAMCALLBACK
     967             :   NS_DECL_NSIRUNNABLE
     968             : 
     969             : private:
     970           0 :   StreamCopier(nsIInputStream* aSource, nsIAsyncOutputStream* aSink,
     971             :                bool aChunked)
     972           0 :     : mSource(aSource)
     973             :     , mAsyncSource(do_QueryInterface(aSource))
     974             :     , mSink(aSink)
     975             :     , mTarget(do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID))
     976             :     , mChunkRemaining(0)
     977             :     , mChunked(aChunked)
     978             :     , mAddedFinalSeparator(false)
     979           0 :     , mFirstChunk(aChunked)
     980             :     {
     981           0 :     }
     982           0 :   ~StreamCopier() {}
     983             : 
     984             :   static nsresult FillOutputBufferHelper(nsIOutputStream* aOutStr,
     985             :                                          void* aClosure,
     986             :                                          char* aBuffer,
     987             :                                          uint32_t aOffset,
     988             :                                          uint32_t aCount,
     989             :                                          uint32_t* aCountRead);
     990             :   nsresult FillOutputBuffer(char* aBuffer,
     991             :                             uint32_t aCount,
     992             :                             uint32_t* aCountRead);
     993             : 
     994             :   nsCOMPtr<nsIInputStream> mSource;
     995             :   nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
     996             :   nsCOMPtr<nsIAsyncOutputStream> mSink;
     997             :   MozPromiseHolder<StreamCopyPromise> mPromise;
     998             :   nsCOMPtr<nsIEventTarget> mTarget; // XXX we should cache this somewhere
     999             :   uint32_t mChunkRemaining;
    1000             :   nsCString mSeparator;
    1001             :   bool mChunked;
    1002             :   bool mAddedFinalSeparator;
    1003             :   bool mFirstChunk;
    1004             : };
    1005             : 
    1006           0 : NS_IMPL_ISUPPORTS(StreamCopier,
    1007             :                   nsIOutputStreamCallback,
    1008             :                   nsIInputStreamCallback,
    1009             :                   nsIRunnable)
    1010             : 
    1011             : struct WriteState
    1012             : {
    1013             :   StreamCopier* copier;
    1014             :   nsresult sourceRv;
    1015             : };
    1016             : 
    1017             : // This function only exists to enable FillOutputBuffer to be a non-static
    1018             : // function where we can use member variables more easily.
    1019             : nsresult
    1020           0 : StreamCopier::FillOutputBufferHelper(nsIOutputStream* aOutStr,
    1021             :                                      void* aClosure,
    1022             :                                      char* aBuffer,
    1023             :                                      uint32_t aOffset,
    1024             :                                      uint32_t aCount,
    1025             :                                      uint32_t* aCountRead)
    1026             : {
    1027           0 :   WriteState* ws = static_cast<WriteState*>(aClosure);
    1028           0 :   ws->sourceRv = ws->copier->FillOutputBuffer(aBuffer, aCount, aCountRead);
    1029           0 :   return ws->sourceRv;
    1030             : }
    1031             : 
    1032             : nsresult
    1033           0 : CheckForEOF(nsIInputStream* aIn,
    1034             :             void* aClosure,
    1035             :             const char* aBuffer,
    1036             :             uint32_t aToOffset,
    1037             :             uint32_t aCount,
    1038             :             uint32_t* aWriteCount)
    1039             : {
    1040           0 :   *static_cast<bool*>(aClosure) = true;
    1041           0 :   *aWriteCount = 0;
    1042           0 :   return NS_BINDING_ABORTED;
    1043             : }
    1044             : 
    1045             : nsresult
    1046           0 : StreamCopier::FillOutputBuffer(char* aBuffer,
    1047             :                                uint32_t aCount,
    1048             :                                uint32_t* aCountRead)
    1049             : {
    1050           0 :   nsresult rv = NS_OK;
    1051           0 :   while (mChunked && mSeparator.IsEmpty() && !mChunkRemaining &&
    1052           0 :          !mAddedFinalSeparator) {
    1053             :     uint64_t avail;
    1054           0 :     rv = mSource->Available(&avail);
    1055           0 :     if (rv == NS_BASE_STREAM_CLOSED) {
    1056           0 :       avail = 0;
    1057           0 :       rv = NS_OK;
    1058             :     }
    1059           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1060             : 
    1061           0 :     mChunkRemaining = avail > UINT32_MAX ? UINT32_MAX :
    1062             :                       static_cast<uint32_t>(avail);
    1063             : 
    1064           0 :     if (!mChunkRemaining) {
    1065             :       // Either it's an non-blocking stream without any data
    1066             :       // currently available, or we're at EOF. Sadly there's no way
    1067             :       // to tell other than to read from the stream.
    1068           0 :       bool hadData = false;
    1069             :       uint32_t numRead;
    1070           0 :       rv = mSource->ReadSegments(CheckForEOF, &hadData, 1, &numRead);
    1071           0 :       if (rv == NS_BASE_STREAM_CLOSED) {
    1072           0 :         avail = 0;
    1073           0 :         rv = NS_OK;
    1074             :       }
    1075           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1076           0 :       MOZ_ASSERT(numRead == 0);
    1077             : 
    1078           0 :       if (hadData) {
    1079             :         // The source received data between the call to Available and the
    1080             :         // call to ReadSegments. Restart with a new call to Available
    1081           0 :         continue;
    1082             :       }
    1083             : 
    1084             :       // We're at EOF, write a separator with 0
    1085           0 :       mAddedFinalSeparator = true;
    1086             :     }
    1087             : 
    1088           0 :     if (mFirstChunk) {
    1089           0 :       mFirstChunk = false;
    1090           0 :       MOZ_ASSERT(mSeparator.IsEmpty());
    1091             :     } else {
    1092             :       // For all chunks except the first, add the newline at the end
    1093             :       // of the previous chunk of data
    1094           0 :       mSeparator.AssignLiteral("\r\n");
    1095             :     }
    1096           0 :     mSeparator.AppendInt(mChunkRemaining, 16);
    1097           0 :     mSeparator.AppendLiteral("\r\n");
    1098             : 
    1099           0 :     if (mAddedFinalSeparator) {
    1100           0 :       mSeparator.AppendLiteral("\r\n");
    1101             :     }
    1102             : 
    1103           0 :     break;
    1104             :   }
    1105             : 
    1106             :   // If we're doing chunked encoding, we should either have a chunk size,
    1107             :   // or we should have reached the end of the input stream.
    1108           0 :   MOZ_ASSERT_IF(mChunked, mChunkRemaining || mAddedFinalSeparator);
    1109             :   // We should only have a separator if we're doing chunked encoding
    1110           0 :   MOZ_ASSERT_IF(!mSeparator.IsEmpty(), mChunked);
    1111             : 
    1112           0 :   if (!mSeparator.IsEmpty()) {
    1113           0 :     *aCountRead = std::min(mSeparator.Length(), aCount);
    1114           0 :     memcpy(aBuffer, mSeparator.BeginReading(), *aCountRead);
    1115           0 :     mSeparator.Cut(0, *aCountRead);
    1116           0 :     rv = NS_OK;
    1117           0 :   } else if (mChunked) {
    1118           0 :     *aCountRead = 0;
    1119           0 :     if (mChunkRemaining) {
    1120           0 :       rv = mSource->Read(aBuffer,
    1121           0 :                          std::min(aCount, mChunkRemaining),
    1122           0 :                          aCountRead);
    1123           0 :       mChunkRemaining -= *aCountRead;
    1124             :     }
    1125             :   } else {
    1126           0 :     rv = mSource->Read(aBuffer, aCount, aCountRead);
    1127             :   }
    1128             : 
    1129           0 :   if (NS_SUCCEEDED(rv) && *aCountRead == 0) {
    1130           0 :     rv = NS_BASE_STREAM_CLOSED;
    1131             :   }
    1132             : 
    1133           0 :   return rv;
    1134             : }
    1135             : 
    1136             : NS_IMETHODIMP
    1137           0 : StreamCopier::Run()
    1138             : {
    1139             :   nsresult rv;
    1140             :   while (1) {
    1141           0 :     WriteState state = { this, NS_OK };
    1142             :     uint32_t written;
    1143           0 :     rv = mSink->WriteSegments(FillOutputBufferHelper, &state,
    1144             :                               mozilla::net::nsIOService::gDefaultSegmentSize,
    1145           0 :                               &written);
    1146           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv) || NS_SUCCEEDED(state.sourceRv));
    1147           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    1148           0 :       mSink->AsyncWait(this, 0, 0, mTarget);
    1149           0 :       return NS_OK;
    1150             :     }
    1151           0 :     if (NS_FAILED(rv)) {
    1152           0 :       mPromise.Resolve(rv, __func__);
    1153           0 :       return NS_OK;
    1154             :     }
    1155             : 
    1156           0 :     if (state.sourceRv == NS_BASE_STREAM_WOULD_BLOCK) {
    1157           0 :       MOZ_ASSERT(mAsyncSource);
    1158           0 :       mAsyncSource->AsyncWait(this, 0, 0, mTarget);
    1159           0 :       mSink->AsyncWait(this, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
    1160           0 :                        0, mTarget);
    1161             : 
    1162           0 :       return NS_OK;
    1163             :     }
    1164           0 :     if (state.sourceRv == NS_BASE_STREAM_CLOSED) {
    1165             :       // We're done!
    1166             :       // No longer interested in callbacks about either stream closing
    1167           0 :       mSink->AsyncWait(nullptr, 0, 0, nullptr);
    1168           0 :       if (mAsyncSource) {
    1169           0 :         mAsyncSource->AsyncWait(nullptr, 0, 0, nullptr);
    1170             :       }
    1171             : 
    1172           0 :       mSource->Close();
    1173           0 :       mSource = nullptr;
    1174           0 :       mAsyncSource = nullptr;
    1175           0 :       mSink = nullptr;
    1176             : 
    1177           0 :       mPromise.Resolve(NS_OK, __func__);
    1178             : 
    1179           0 :       return NS_OK;
    1180             :     }
    1181             : 
    1182           0 :     if (NS_FAILED(state.sourceRv)) {
    1183           0 :       mPromise.Resolve(state.sourceRv, __func__);
    1184           0 :       return NS_OK;
    1185             :     }
    1186           0 :   }
    1187             : 
    1188             :   MOZ_ASSUME_UNREACHABLE_MARKER();
    1189             : }
    1190             : 
    1191             : NS_IMETHODIMP
    1192           0 : StreamCopier::OnInputStreamReady(nsIAsyncInputStream* aStream)
    1193             : {
    1194           0 :   MOZ_ASSERT(aStream == mAsyncSource ||
    1195             :              (!mSource && !mAsyncSource && !mSink));
    1196           0 :   return mSource ? Run() : NS_OK;
    1197             : }
    1198             : 
    1199             : NS_IMETHODIMP
    1200           0 : StreamCopier::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
    1201             : {
    1202           0 :   MOZ_ASSERT(aStream == mSink ||
    1203             :              (!mSource && !mAsyncSource && !mSink));
    1204           0 :   return mSource ? Run() : NS_OK;
    1205             : }
    1206             : 
    1207             : } // namespace
    1208             : 
    1209             : NS_IMETHODIMP
    1210           0 : HttpServer::Connection::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
    1211             : {
    1212           0 :   MOZ_ASSERT(aStream == mOutput || !mOutput);
    1213           0 :   if (!mOutput) {
    1214           0 :     return NS_OK;
    1215             :   }
    1216             : 
    1217             :   nsresult rv;
    1218             : 
    1219           0 :   while (!mOutputBuffers.IsEmpty()) {
    1220           0 :     if (!mOutputBuffers[0].mStream) {
    1221           0 :       nsCString& buffer = mOutputBuffers[0].mString;
    1222           0 :       while (!buffer.IsEmpty()) {
    1223           0 :         uint32_t written = 0;
    1224           0 :         rv = mOutput->Write(buffer.BeginReading(),
    1225             :                             buffer.Length(),
    1226           0 :                             &written);
    1227             : 
    1228           0 :         buffer.Cut(0, written);
    1229             : 
    1230           0 :         if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
    1231           0 :           return mOutput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
    1232             :         }
    1233             : 
    1234           0 :         if (NS_FAILED(rv)) {
    1235           0 :           Close();
    1236           0 :           return NS_OK;
    1237             :         }
    1238             :       }
    1239           0 :       mOutputBuffers.RemoveElementAt(0);
    1240             :     } else {
    1241           0 :       if (mOutputCopy) {
    1242             :         // we're already copying the stream
    1243           0 :         return NS_OK;
    1244             :       }
    1245             : 
    1246             :       mOutputCopy =
    1247           0 :         StreamCopier::Copy(mOutputBuffers[0].mStream,
    1248             :                            mOutput,
    1249           0 :                            mOutputBuffers[0].mChunked);
    1250             : 
    1251           0 :       RefPtr<Connection> self = this;
    1252             : 
    1253             :       mOutputCopy->
    1254           0 :         Then(mServer->mEventTarget,
    1255             :              __func__,
    1256           0 :              [self, this] (nsresult aStatus) {
    1257           0 :                MOZ_ASSERT(mOutputBuffers[0].mStream);
    1258           0 :                LOG_V("HttpServer::Connection::OnOutputStreamReady(%p) - "
    1259             :                      "Sent body. Status 0x%" PRIx32,
    1260             :                      this, static_cast<uint32_t>(aStatus));
    1261             : 
    1262           0 :                mOutputBuffers.RemoveElementAt(0);
    1263           0 :                mOutputCopy = nullptr;
    1264           0 :                OnOutputStreamReady(mOutput);
    1265           0 :              },
    1266           0 :              [] (bool) { MOZ_ASSERT_UNREACHABLE("Reject unexpected"); });
    1267             :     }
    1268             :   }
    1269             : 
    1270           0 :   if (mPendingRequests.IsEmpty()) {
    1271           0 :     if (mCloseAfterRequest) {
    1272           0 :       LOG_V("HttpServer::Connection::OnOutputStreamReady(%p) - Closing channel",
    1273             :             this);
    1274           0 :       Close();
    1275           0 :     } else if (mWebSocketTransportProvider) {
    1276           0 :       mInput->AsyncWait(nullptr, 0, 0, nullptr);
    1277           0 :       mOutput->AsyncWait(nullptr, 0, 0, nullptr);
    1278             : 
    1279           0 :       mWebSocketTransportProvider->SetTransport(mTransport, mInput, mOutput);
    1280           0 :       mTransport = nullptr;
    1281           0 :       mInput = nullptr;
    1282           0 :       mOutput = nullptr;
    1283           0 :       mWebSocketTransportProvider = nullptr;
    1284             :     }
    1285             :   }
    1286             : 
    1287           0 :   return NS_OK;
    1288             : }
    1289             : 
    1290             : void
    1291           0 : HttpServer::Connection::Close()
    1292             : {
    1293           0 :   if (!mTransport) {
    1294           0 :     MOZ_ASSERT(!mOutput && !mInput);
    1295           0 :     return;
    1296             :   }
    1297             : 
    1298           0 :   mTransport->Close(NS_BINDING_ABORTED);
    1299           0 :   if (mInput) {
    1300           0 :     mInput->Close();
    1301           0 :     mInput = nullptr;
    1302             :   }
    1303           0 :   if (mOutput) {
    1304           0 :     mOutput->Close();
    1305           0 :     mOutput = nullptr;
    1306             :   }
    1307             : 
    1308           0 :   mTransport = nullptr;
    1309             : 
    1310           0 :   mInputBuffer.Truncate();
    1311           0 :   mOutputBuffers.Clear();
    1312           0 :   mPendingRequests.Clear();
    1313             : }
    1314             : 
    1315             : 
    1316             : } // namespace net
    1317           9 : } // namespace mozilla

Generated by: LCOV version 1.13