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
|