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