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