1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
25 : * Patrick McManus <mcmanus@ducksong.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "WebSocketLog.h"
42 : #include "WebSocketChannel.h"
43 :
44 : #include "nsISocketTransportService.h"
45 : #include "nsIURI.h"
46 : #include "nsIChannel.h"
47 : #include "nsICryptoHash.h"
48 : #include "nsIRunnable.h"
49 : #include "nsIPrefBranch.h"
50 : #include "nsIPrefService.h"
51 : #include "nsICancelable.h"
52 : #include "nsIDNSRecord.h"
53 : #include "nsIDNSService.h"
54 : #include "nsIStreamConverterService.h"
55 : #include "nsIIOService2.h"
56 : #include "nsIProtocolProxyService.h"
57 :
58 : #include "nsAutoPtr.h"
59 : #include "nsStandardURL.h"
60 : #include "nsNetCID.h"
61 : #include "nsServiceManagerUtils.h"
62 : #include "nsXPIDLString.h"
63 : #include "nsCRT.h"
64 : #include "nsThreadUtils.h"
65 : #include "nsNetError.h"
66 : #include "nsStringStream.h"
67 : #include "nsAlgorithm.h"
68 : #include "nsProxyRelease.h"
69 : #include "nsNetUtil.h"
70 :
71 : #include "plbase64.h"
72 : #include "prmem.h"
73 : #include "prnetdb.h"
74 : #include "prbit.h"
75 : #include "zlib.h"
76 :
77 : extern PRThread *gSocketThread;
78 :
79 : namespace mozilla {
80 : namespace net {
81 :
82 0 : NS_IMPL_THREADSAFE_ISUPPORTS11(WebSocketChannel,
83 : nsIWebSocketChannel,
84 : nsIHttpUpgradeListener,
85 : nsIRequestObserver,
86 : nsIStreamListener,
87 : nsIProtocolHandler,
88 : nsIInputStreamCallback,
89 : nsIOutputStreamCallback,
90 : nsITimerCallback,
91 : nsIDNSListener,
92 : nsIInterfaceRequestor,
93 : nsIChannelEventSink)
94 :
95 : // We implement RFC 6455, which uses Sec-WebSocket-Version: 13 on the wire.
96 : #define SEC_WEBSOCKET_VERSION "13"
97 :
98 : /*
99 : * About SSL unsigned certificates
100 : *
101 : * wss will not work to a host using an unsigned certificate unless there
102 : * is already an exception (i.e. it cannot popup a dialog asking for
103 : * a security exception). This is similar to how an inlined img will
104 : * fail without a dialog if fails for the same reason. This should not
105 : * be a problem in practice as it is expected the websocket javascript
106 : * is served from the same host as the websocket server (or of course,
107 : * a valid cert could just be provided).
108 : *
109 : */
110 :
111 : // some helper classes
112 :
113 : //-----------------------------------------------------------------------------
114 : // CallOnMessageAvailable
115 : //-----------------------------------------------------------------------------
116 :
117 : class CallOnMessageAvailable : public nsIRunnable
118 : {
119 : public:
120 : NS_DECL_ISUPPORTS
121 :
122 0 : CallOnMessageAvailable(WebSocketChannel *aChannel,
123 : nsCString &aData,
124 : PRInt32 aLen)
125 : : mChannel(aChannel),
126 : mData(aData),
127 0 : mLen(aLen) {}
128 :
129 0 : NS_IMETHOD Run()
130 : {
131 0 : if (mLen < 0)
132 0 : mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData);
133 : else
134 0 : mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData);
135 0 : return NS_OK;
136 : }
137 :
138 : private:
139 0 : ~CallOnMessageAvailable() {}
140 :
141 : nsRefPtr<WebSocketChannel> mChannel;
142 : nsCString mData;
143 : PRInt32 mLen;
144 : };
145 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnMessageAvailable, nsIRunnable)
146 :
147 : //-----------------------------------------------------------------------------
148 : // CallOnStop
149 : //-----------------------------------------------------------------------------
150 :
151 : class CallOnStop : public nsIRunnable
152 : {
153 : public:
154 : NS_DECL_ISUPPORTS
155 :
156 0 : CallOnStop(WebSocketChannel *aChannel,
157 : nsresult aData)
158 : : mChannel(aChannel),
159 0 : mData(aData) {}
160 :
161 0 : NS_IMETHOD Run()
162 : {
163 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
164 0 : mChannel->mListener->OnStop(mChannel->mContext, mData);
165 0 : return NS_OK;
166 : }
167 :
168 : private:
169 0 : ~CallOnStop() {}
170 :
171 : nsRefPtr<WebSocketChannel> mChannel;
172 : nsresult mData;
173 : };
174 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnStop, nsIRunnable)
175 :
176 : //-----------------------------------------------------------------------------
177 : // CallOnServerClose
178 : //-----------------------------------------------------------------------------
179 :
180 : class CallOnServerClose : public nsIRunnable
181 : {
182 : public:
183 : NS_DECL_ISUPPORTS
184 :
185 0 : CallOnServerClose(WebSocketChannel *aChannel,
186 : PRUint16 aCode,
187 : nsCString &aReason)
188 : : mChannel(aChannel),
189 : mCode(aCode),
190 0 : mReason(aReason) {}
191 :
192 0 : NS_IMETHOD Run()
193 : {
194 0 : mChannel->mListener->OnServerClose(mChannel->mContext, mCode, mReason);
195 0 : return NS_OK;
196 : }
197 :
198 : private:
199 0 : ~CallOnServerClose() {}
200 :
201 : nsRefPtr<WebSocketChannel> mChannel;
202 : PRUint16 mCode;
203 : nsCString mReason;
204 : };
205 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnServerClose, nsIRunnable)
206 :
207 : //-----------------------------------------------------------------------------
208 : // CallAcknowledge
209 : //-----------------------------------------------------------------------------
210 :
211 : class CallAcknowledge : public nsIRunnable
212 : {
213 : public:
214 : NS_DECL_ISUPPORTS
215 :
216 0 : CallAcknowledge(WebSocketChannel *aChannel,
217 : PRUint32 aSize)
218 : : mChannel(aChannel),
219 0 : mSize(aSize) {}
220 :
221 0 : NS_IMETHOD Run()
222 : {
223 0 : LOG(("WebSocketChannel::CallAcknowledge: Size %u\n", mSize));
224 0 : mChannel->mListener->OnAcknowledge(mChannel->mContext, mSize);
225 0 : return NS_OK;
226 : }
227 :
228 : private:
229 0 : ~CallAcknowledge() {}
230 :
231 : nsRefPtr<WebSocketChannel> mChannel;
232 : PRUint32 mSize;
233 : };
234 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(CallAcknowledge, nsIRunnable)
235 :
236 : //-----------------------------------------------------------------------------
237 : // OutboundMessage
238 : //-----------------------------------------------------------------------------
239 :
240 : enum WsMsgType {
241 : kMsgTypeString = 0,
242 : kMsgTypeBinaryString,
243 : kMsgTypeStream,
244 : kMsgTypePing,
245 : kMsgTypePong,
246 : kMsgTypeFin
247 : };
248 :
249 : static const char* msgNames[] = {
250 : "text",
251 : "binaryString",
252 : "binaryStream",
253 : "ping",
254 : "pong",
255 : "close"
256 : };
257 :
258 : class OutboundMessage
259 : {
260 : public:
261 0 : OutboundMessage(WsMsgType type, nsCString *str)
262 0 : : mMsgType(type)
263 : {
264 0 : MOZ_COUNT_CTOR(OutboundMessage);
265 0 : mMsg.pString = str;
266 0 : mLength = str ? str->Length() : 0;
267 0 : }
268 :
269 0 : OutboundMessage(nsIInputStream *stream, PRUint32 length)
270 0 : : mMsgType(kMsgTypeStream), mLength(length)
271 : {
272 0 : MOZ_COUNT_CTOR(OutboundMessage);
273 0 : mMsg.pStream = stream;
274 0 : mMsg.pStream->AddRef();
275 0 : }
276 :
277 0 : ~OutboundMessage() {
278 0 : MOZ_COUNT_DTOR(OutboundMessage);
279 0 : switch (mMsgType) {
280 : case kMsgTypeString:
281 : case kMsgTypeBinaryString:
282 : case kMsgTypePing:
283 : case kMsgTypePong:
284 0 : delete mMsg.pString;
285 0 : break;
286 : case kMsgTypeStream:
287 : // for now this only gets hit if msg deleted w/o being sent
288 0 : if (mMsg.pStream) {
289 0 : mMsg.pStream->Close();
290 0 : mMsg.pStream->Release();
291 : }
292 0 : break;
293 : case kMsgTypeFin:
294 0 : break; // do-nothing: avoid compiler warning
295 : }
296 0 : }
297 :
298 0 : WsMsgType GetMsgType() const { return mMsgType; }
299 0 : PRInt32 Length() const { return mLength; }
300 :
301 0 : PRUint8* BeginWriting() {
302 0 : NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
303 : "Stream should have been converted to string by now");
304 0 : return (PRUint8 *)(mMsg.pString ? mMsg.pString->BeginWriting() : nsnull);
305 : }
306 :
307 0 : PRUint8* BeginReading() {
308 0 : NS_ABORT_IF_FALSE(mMsgType != kMsgTypeStream,
309 : "Stream should have been converted to string by now");
310 0 : return (PRUint8 *)(mMsg.pString ? mMsg.pString->BeginReading() : nsnull);
311 : }
312 :
313 0 : nsresult ConvertStreamToString()
314 : {
315 0 : NS_ABORT_IF_FALSE(mMsgType == kMsgTypeStream, "Not a stream!");
316 :
317 : #ifdef DEBUG
318 : // Make sure we got correct length from Blob
319 : PRUint32 bytes;
320 0 : mMsg.pStream->Available(&bytes);
321 0 : NS_ASSERTION(bytes == mLength, "Stream length != blob length!");
322 : #endif
323 :
324 0 : nsAutoPtr<nsCString> temp(new nsCString());
325 0 : nsresult rv = NS_ReadInputStreamToString(mMsg.pStream, *temp, mLength);
326 :
327 0 : NS_ENSURE_SUCCESS(rv, rv);
328 :
329 0 : mMsg.pStream->Close();
330 0 : mMsg.pStream->Release();
331 0 : mMsg.pString = temp.forget();
332 0 : mMsgType = kMsgTypeBinaryString;
333 :
334 0 : return NS_OK;
335 : }
336 :
337 : private:
338 : union {
339 : nsCString *pString;
340 : nsIInputStream *pStream;
341 : } mMsg;
342 : WsMsgType mMsgType;
343 : PRUint32 mLength;
344 : };
345 :
346 : //-----------------------------------------------------------------------------
347 : // OutboundEnqueuer
348 : //-----------------------------------------------------------------------------
349 :
350 : class OutboundEnqueuer : public nsIRunnable
351 : {
352 : public:
353 : NS_DECL_ISUPPORTS
354 :
355 0 : OutboundEnqueuer(WebSocketChannel *aChannel, OutboundMessage *aMsg)
356 0 : : mChannel(aChannel), mMessage(aMsg) {}
357 :
358 0 : NS_IMETHOD Run()
359 : {
360 0 : mChannel->EnqueueOutgoingMessage(mChannel->mOutgoingMessages, mMessage);
361 0 : return NS_OK;
362 : }
363 :
364 : private:
365 0 : ~OutboundEnqueuer() {}
366 :
367 : nsRefPtr<WebSocketChannel> mChannel;
368 : OutboundMessage *mMessage;
369 : };
370 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(OutboundEnqueuer, nsIRunnable)
371 :
372 : //-----------------------------------------------------------------------------
373 : // nsWSAdmissionManager
374 : //-----------------------------------------------------------------------------
375 :
376 : // Section 4.1 requires that only a single websocket at a time can be CONNECTING
377 : // to any given IP address (or hostname, if proxy doing DNS for us). This class
378 : // ensures that we delay connecting until any pending connection for the same
379 : // IP/addr is complete (i.e. until before the 101 upgrade complete response
380 : // comes back and an 'open' javascript event is created)
381 :
382 : class nsWSAdmissionManager
383 : {
384 : public:
385 0 : nsWSAdmissionManager() : mSessionCount(0)
386 : {
387 0 : MOZ_COUNT_CTOR(nsWSAdmissionManager);
388 0 : }
389 :
390 : class nsOpenConn
391 : {
392 : public:
393 0 : nsOpenConn(nsCString &addr, WebSocketChannel *channel)
394 0 : : mAddress(addr), mChannel(channel) { MOZ_COUNT_CTOR(nsOpenConn); }
395 0 : ~nsOpenConn() { MOZ_COUNT_DTOR(nsOpenConn); }
396 :
397 : nsCString mAddress;
398 : WebSocketChannel *mChannel;
399 : };
400 :
401 0 : ~nsWSAdmissionManager()
402 0 : {
403 0 : MOZ_COUNT_DTOR(nsWSAdmissionManager);
404 0 : for (PRUint32 i = 0; i < mData.Length(); i++)
405 0 : delete mData[i];
406 0 : }
407 :
408 0 : bool ConditionallyConnect(nsCString &aStr, WebSocketChannel *ws)
409 : {
410 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
411 :
412 : // if aStr is not in mData then we return true, else false.
413 : // in either case aStr is then added to mData - meaning
414 : // there will be duplicates when this function has been
415 : // called with the same parameter multiple times.
416 :
417 : // we could hash this, but the dataset is expected to be
418 : // small
419 :
420 : // There may already be another WS channel connecting to this IP address, in
421 : // which case we'll still create a new nsOpenConn but defer BeginOpen until
422 : // that channel completes connecting.
423 0 : bool found = (IndexOf(aStr) >= 0);
424 0 : nsOpenConn *newdata = new nsOpenConn(aStr, ws);
425 0 : mData.AppendElement(newdata);
426 :
427 0 : NS_ABORT_IF_FALSE (!ws->mOpenRunning && !ws->mOpenBlocked,
428 : "opening state");
429 :
430 0 : if (!found) {
431 0 : ws->mOpenRunning = 1;
432 0 : ws->BeginOpen();
433 : } else {
434 0 : ws->mOpenBlocked = 1;
435 : }
436 :
437 0 : return !found;
438 : }
439 :
440 0 : bool Complete(WebSocketChannel *aChannel)
441 : {
442 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
443 0 : NS_ABORT_IF_FALSE(!aChannel->mOpenBlocked,
444 : "blocked, but complete nsOpenConn");
445 :
446 : // It is possible this has already been canceled
447 0 : if (!aChannel->mOpenRunning)
448 0 : return false;
449 :
450 0 : PRInt32 index = IndexOf(aChannel);
451 0 : NS_ABORT_IF_FALSE(index >= 0, "completed connection not in open list");
452 :
453 0 : aChannel->mOpenRunning = 0;
454 0 : nsOpenConn *olddata = mData[index];
455 0 : mData.RemoveElementAt(index);
456 0 : delete olddata;
457 :
458 : // are there more of the same address pending dispatch?
459 0 : return ConnectNext(aChannel->mAddress);
460 : }
461 :
462 0 : bool Cancel(WebSocketChannel *aChannel)
463 : {
464 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
465 0 : PRInt32 index = IndexOf(aChannel);
466 0 : NS_ABORT_IF_FALSE(index >= 0, "Cancelled connection not in open list");
467 0 : NS_ABORT_IF_FALSE(aChannel->mOpenRunning ^ aChannel->mOpenBlocked,
468 : "cancel without running xor blocked");
469 :
470 0 : bool wasRunning = aChannel->mOpenRunning;
471 0 : aChannel->mOpenRunning = 0;
472 0 : aChannel->mOpenBlocked = 0;
473 0 : nsOpenConn *olddata = mData[index];
474 0 : mData.RemoveElementAt(index);
475 0 : delete olddata;
476 :
477 : // if we are running we can run another one
478 0 : if (wasRunning)
479 0 : return ConnectNext(aChannel->mAddress);
480 :
481 0 : return false;
482 : }
483 :
484 0 : bool ConnectNext(nsCString &hostName)
485 : {
486 0 : PRInt32 index = IndexOf(hostName);
487 0 : if (index >= 0) {
488 0 : WebSocketChannel *chan = mData[index]->mChannel;
489 :
490 0 : NS_ABORT_IF_FALSE(chan->mOpenBlocked,
491 : "transaction not blocked but in queue");
492 0 : NS_ABORT_IF_FALSE(!chan->mOpenRunning, "transaction already running");
493 :
494 0 : chan->mOpenBlocked = 0;
495 0 : chan->mOpenRunning = 1;
496 0 : chan->BeginOpen();
497 0 : return true;
498 : }
499 :
500 0 : return false;
501 : }
502 :
503 0 : void IncrementSessionCount()
504 : {
505 0 : PR_ATOMIC_INCREMENT(&mSessionCount);
506 0 : }
507 :
508 0 : void DecrementSessionCount()
509 : {
510 0 : PR_ATOMIC_DECREMENT(&mSessionCount);
511 0 : }
512 :
513 0 : PRInt32 SessionCount()
514 : {
515 0 : return mSessionCount;
516 : }
517 :
518 : private:
519 : nsTArray<nsOpenConn *> mData;
520 :
521 0 : PRInt32 IndexOf(nsCString &aStr)
522 : {
523 0 : for (PRUint32 i = 0; i < mData.Length(); i++)
524 0 : if (aStr == (mData[i])->mAddress)
525 0 : return i;
526 0 : return -1;
527 : }
528 :
529 0 : PRInt32 IndexOf(WebSocketChannel *aChannel)
530 : {
531 0 : for (PRUint32 i = 0; i < mData.Length(); i++)
532 0 : if (aChannel == (mData[i])->mChannel)
533 0 : return i;
534 0 : return -1;
535 : }
536 :
537 : // SessionCount might be decremented from the main or the socket
538 : // thread, so manage it with atomic counters
539 : PRInt32 mSessionCount;
540 : };
541 :
542 : //-----------------------------------------------------------------------------
543 : // nsWSCompression
544 : //
545 : // similar to nsDeflateConverter except for the mandatory FLUSH calls
546 : // required by websocket and the absence of the deflate termination
547 : // block which is appropriate because it would create data bytes after
548 : // sending the websockets CLOSE message.
549 : //-----------------------------------------------------------------------------
550 :
551 : class nsWSCompression
552 : {
553 : public:
554 0 : nsWSCompression(nsIStreamListener *aListener,
555 : nsISupports *aContext)
556 : : mActive(false),
557 : mContext(aContext),
558 0 : mListener(aListener)
559 : {
560 0 : MOZ_COUNT_CTOR(nsWSCompression);
561 :
562 0 : mZlib.zalloc = allocator;
563 0 : mZlib.zfree = destructor;
564 0 : mZlib.opaque = Z_NULL;
565 :
566 : // Initialize the compressor - these are all the normal zlib
567 : // defaults except window size is set to -15 instead of +15.
568 : // This is the zlib way of specifying raw RFC 1951 output instead
569 : // of the zlib rfc 1950 format which has a 2 byte header and
570 : // adler checksum as a trailer
571 :
572 : nsresult rv;
573 0 : mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
574 0 : if (NS_SUCCEEDED(rv) && aContext && aListener &&
575 0 : deflateInit2(&mZlib, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8,
576 : Z_DEFAULT_STRATEGY) == Z_OK) {
577 0 : mActive = true;
578 : }
579 0 : }
580 :
581 0 : ~nsWSCompression()
582 0 : {
583 0 : MOZ_COUNT_DTOR(nsWSCompression);
584 :
585 0 : if (mActive)
586 0 : deflateEnd(&mZlib);
587 0 : }
588 :
589 0 : bool Active()
590 : {
591 0 : return mActive;
592 : }
593 :
594 0 : nsresult Deflate(PRUint8 *buf1, PRUint32 buf1Len,
595 : PRUint8 *buf2, PRUint32 buf2Len)
596 : {
597 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
598 : "not socket thread");
599 0 : NS_ABORT_IF_FALSE(mActive, "not active");
600 :
601 0 : mZlib.avail_out = kBufferLen;
602 0 : mZlib.next_out = mBuffer;
603 0 : mZlib.avail_in = buf1Len;
604 0 : mZlib.next_in = buf1;
605 :
606 : nsresult rv;
607 :
608 0 : while (mZlib.avail_in > 0) {
609 0 : deflate(&mZlib, (buf2Len > 0) ? Z_NO_FLUSH : Z_SYNC_FLUSH);
610 0 : rv = PushData();
611 0 : if (NS_FAILED(rv))
612 0 : return rv;
613 0 : mZlib.avail_out = kBufferLen;
614 0 : mZlib.next_out = mBuffer;
615 : }
616 :
617 0 : mZlib.avail_in = buf2Len;
618 0 : mZlib.next_in = buf2;
619 :
620 0 : while (mZlib.avail_in > 0) {
621 0 : deflate(&mZlib, Z_SYNC_FLUSH);
622 0 : rv = PushData();
623 0 : if (NS_FAILED(rv))
624 0 : return rv;
625 0 : mZlib.avail_out = kBufferLen;
626 0 : mZlib.next_out = mBuffer;
627 : }
628 :
629 0 : return NS_OK;
630 : }
631 :
632 : private:
633 :
634 : // use zlib data types
635 0 : static void *allocator(void *opaque, uInt items, uInt size)
636 : {
637 0 : return moz_xmalloc(items * size);
638 : }
639 :
640 0 : static void destructor(void *opaque, void *addr)
641 : {
642 0 : moz_free(addr);
643 0 : }
644 :
645 0 : nsresult PushData()
646 : {
647 0 : PRUint32 bytesToWrite = kBufferLen - mZlib.avail_out;
648 0 : if (bytesToWrite > 0) {
649 0 : mStream->ShareData(reinterpret_cast<char *>(mBuffer), bytesToWrite);
650 : nsresult rv =
651 0 : mListener->OnDataAvailable(nsnull, mContext, mStream, 0, bytesToWrite);
652 0 : if (NS_FAILED(rv))
653 0 : return rv;
654 : }
655 0 : return NS_OK;
656 : }
657 :
658 : bool mActive;
659 : z_stream mZlib;
660 : nsCOMPtr<nsIStringInputStream> mStream;
661 :
662 : nsISupports *mContext; /* weak ref */
663 : nsIStreamListener *mListener; /* weak ref */
664 :
665 : const static PRInt32 kBufferLen = 4096;
666 : PRUint8 mBuffer[kBufferLen];
667 : };
668 :
669 : static nsWSAdmissionManager *sWebSocketAdmissions = nsnull;
670 :
671 : //-----------------------------------------------------------------------------
672 : // WebSocketChannel
673 : //-----------------------------------------------------------------------------
674 :
675 0 : WebSocketChannel::WebSocketChannel() :
676 : mCloseTimeout(20000),
677 : mOpenTimeout(20000),
678 : mPingTimeout(0),
679 : mPingResponseTimeout(10000),
680 : mMaxConcurrentConnections(200),
681 : mRecvdHttpOnStartRequest(0),
682 : mRecvdHttpUpgradeTransport(0),
683 : mRequestedClose(0),
684 : mClientClosed(0),
685 : mServerClosed(0),
686 : mStopped(0),
687 : mCalledOnStop(0),
688 : mPingOutstanding(0),
689 : mAllowCompression(1),
690 : mAutoFollowRedirects(0),
691 : mReleaseOnTransmit(0),
692 : mTCPClosed(0),
693 : mOpenBlocked(0),
694 : mOpenRunning(0),
695 : mChannelWasOpened(0),
696 : mMaxMessageSize(PR_INT32_MAX),
697 : mStopOnClose(NS_OK),
698 : mServerCloseCode(CLOSE_ABNORMAL),
699 : mScriptCloseCode(0),
700 : mFragmentOpcode(kContinuation),
701 : mFragmentAccumulator(0),
702 : mBuffered(0),
703 : mBufferSize(kIncomingBufferInitialSize),
704 : mCurrentOut(nsnull),
705 : mCurrentOutSent(0),
706 : mCompressor(nsnull),
707 : mDynamicOutputSize(0),
708 0 : mDynamicOutput(nsnull)
709 : {
710 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
711 :
712 0 : LOG(("WebSocketChannel::WebSocketChannel() %p\n", this));
713 :
714 0 : if (!sWebSocketAdmissions)
715 0 : sWebSocketAdmissions = new nsWSAdmissionManager();
716 :
717 : // The active session limit is enforced in AsyncOpen()
718 0 : sWebSocketAdmissions->IncrementSessionCount();
719 :
720 0 : mFramePtr = mBuffer = static_cast<PRUint8 *>(moz_xmalloc(mBufferSize));
721 0 : }
722 :
723 0 : WebSocketChannel::~WebSocketChannel()
724 : {
725 0 : LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
726 :
727 0 : if (sWebSocketAdmissions)
728 0 : sWebSocketAdmissions->DecrementSessionCount();
729 :
730 : // this stop is a nop if the normal connect/close is followed
731 0 : mStopped = 1;
732 0 : StopSession(NS_ERROR_UNEXPECTED);
733 0 : NS_ABORT_IF_FALSE(!mOpenRunning && !mOpenBlocked, "op");
734 :
735 0 : moz_free(mBuffer);
736 0 : moz_free(mDynamicOutput);
737 0 : delete mCompressor;
738 0 : delete mCurrentOut;
739 :
740 0 : while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
741 0 : delete mCurrentOut;
742 0 : while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
743 0 : delete mCurrentOut;
744 0 : while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
745 0 : delete mCurrentOut;
746 :
747 0 : nsCOMPtr<nsIThread> mainThread;
748 : nsIURI *forgettable;
749 0 : NS_GetMainThread(getter_AddRefs(mainThread));
750 :
751 0 : if (mURI) {
752 0 : mURI.forget(&forgettable);
753 0 : NS_ProxyRelease(mainThread, forgettable, false);
754 : }
755 :
756 0 : if (mOriginalURI) {
757 0 : mOriginalURI.forget(&forgettable);
758 0 : NS_ProxyRelease(mainThread, forgettable, false);
759 : }
760 :
761 0 : if (mListener) {
762 : nsIWebSocketListener *forgettableListener;
763 0 : mListener.forget(&forgettableListener);
764 0 : NS_ProxyRelease(mainThread, forgettableListener, false);
765 : }
766 :
767 0 : if (mContext) {
768 : nsISupports *forgettableContext;
769 0 : mContext.forget(&forgettableContext);
770 0 : NS_ProxyRelease(mainThread, forgettableContext, false);
771 : }
772 :
773 0 : if (mLoadGroup) {
774 : nsILoadGroup *forgettableGroup;
775 0 : mLoadGroup.forget(&forgettableGroup);
776 0 : NS_ProxyRelease(mainThread, forgettableGroup, false);
777 : }
778 0 : }
779 :
780 : void
781 1419 : WebSocketChannel::Shutdown()
782 : {
783 1419 : delete sWebSocketAdmissions;
784 1419 : sWebSocketAdmissions = nsnull;
785 1419 : }
786 :
787 : nsresult
788 0 : WebSocketChannel::BeginOpen()
789 : {
790 0 : LOG(("WebSocketChannel::BeginOpen() %p\n", this));
791 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
792 :
793 : nsresult rv;
794 :
795 0 : if (mRedirectCallback) {
796 0 : LOG(("WebSocketChannel::BeginOpen: Resuming Redirect\n"));
797 0 : rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
798 0 : mRedirectCallback = nsnull;
799 0 : return rv;
800 : }
801 :
802 0 : nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
803 0 : if (NS_FAILED(rv)) {
804 0 : LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
805 0 : AbortSession(NS_ERROR_CONNECTION_REFUSED);
806 0 : return rv;
807 : }
808 :
809 0 : rv = localChannel->AsyncOpen(this, mHttpChannel);
810 0 : if (NS_FAILED(rv)) {
811 0 : LOG(("WebSocketChannel::BeginOpen: cannot async open\n"));
812 0 : AbortSession(NS_ERROR_CONNECTION_REFUSED);
813 0 : return rv;
814 : }
815 0 : mChannelWasOpened = 1;
816 :
817 0 : mOpenTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
818 0 : if (NS_SUCCEEDED(rv))
819 0 : mOpenTimer->InitWithCallback(this, mOpenTimeout, nsITimer::TYPE_ONE_SHOT);
820 :
821 0 : return rv;
822 : }
823 :
824 : bool
825 0 : WebSocketChannel::IsPersistentFramePtr()
826 : {
827 0 : return (mFramePtr >= mBuffer && mFramePtr < mBuffer + mBufferSize);
828 : }
829 :
830 : // Extends the internal buffer by count and returns the total
831 : // amount of data available for read
832 : //
833 : // Accumulated fragment size is passed in instead of using the member
834 : // variable beacuse when transitioning from the stack to the persistent
835 : // read buffer we want to explicitly include them in the buffer instead
836 : // of as already existing data.
837 : bool
838 0 : WebSocketChannel::UpdateReadBuffer(PRUint8 *buffer, PRUint32 count,
839 : PRUint32 accumulatedFragments,
840 : PRUint32 *available)
841 : {
842 0 : LOG(("WebSocketChannel::UpdateReadBuffer() %p [%p %u]\n",
843 : this, buffer, count));
844 :
845 0 : if (!mBuffered)
846 0 : mFramePtr = mBuffer;
847 :
848 0 : NS_ABORT_IF_FALSE(IsPersistentFramePtr(), "update read buffer bad mFramePtr");
849 0 : NS_ABORT_IF_FALSE(mFramePtr - accumulatedFragments >= mBuffer,
850 : "reserved FramePtr bad");
851 :
852 0 : if (mBuffered + count <= mBufferSize) {
853 : // append to existing buffer
854 0 : LOG(("WebSocketChannel: update read buffer absorbed %u\n", count));
855 0 : } else if (mBuffered + count -
856 0 : (mFramePtr - accumulatedFragments - mBuffer) <= mBufferSize) {
857 : // make room in existing buffer by shifting unused data to start
858 0 : mBuffered -= (mFramePtr - mBuffer - accumulatedFragments);
859 0 : LOG(("WebSocketChannel: update read buffer shifted %u\n", mBuffered));
860 0 : ::memmove(mBuffer, mFramePtr - accumulatedFragments, mBuffered);
861 0 : mFramePtr = mBuffer + accumulatedFragments;
862 : } else {
863 : // existing buffer is not sufficient, extend it
864 0 : mBufferSize += count + 8192 + mBufferSize/3;
865 0 : LOG(("WebSocketChannel: update read buffer extended to %u\n", mBufferSize));
866 0 : PRUint8 *old = mBuffer;
867 0 : mBuffer = (PRUint8 *)moz_realloc(mBuffer, mBufferSize);
868 0 : if (!mBuffer) {
869 0 : mBuffer = old;
870 0 : return false;
871 : }
872 0 : mFramePtr = mBuffer + (mFramePtr - old);
873 : }
874 :
875 0 : ::memcpy(mBuffer + mBuffered, buffer, count);
876 0 : mBuffered += count;
877 :
878 0 : if (available)
879 0 : *available = mBuffered - (mFramePtr - mBuffer);
880 :
881 0 : return true;
882 : }
883 :
884 : nsresult
885 0 : WebSocketChannel::ProcessInput(PRUint8 *buffer, PRUint32 count)
886 : {
887 0 : LOG(("WebSocketChannel::ProcessInput %p [%d %d]\n", this, count, mBuffered));
888 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
889 :
890 : // reset the ping timer
891 0 : if (mPingTimer) {
892 : // The purpose of ping/pong is to actively probe the peer so that an
893 : // unreachable peer is not mistaken for a period of idleness. This
894 : // implementation accepts any application level read activity as a sign of
895 : // life, it does not necessarily have to be a pong.
896 0 : mPingOutstanding = 0;
897 0 : mPingTimer->SetDelay(mPingTimeout);
898 : }
899 :
900 : PRUint32 avail;
901 :
902 0 : if (!mBuffered) {
903 : // Most of the time we can process right off the stack buffer without
904 : // having to accumulate anything
905 0 : mFramePtr = buffer;
906 0 : avail = count;
907 : } else {
908 0 : if (!UpdateReadBuffer(buffer, count, mFragmentAccumulator, &avail)) {
909 0 : AbortSession(NS_ERROR_FILE_TOO_BIG);
910 0 : return NS_ERROR_FILE_TOO_BIG;
911 : }
912 : }
913 :
914 : PRUint8 *payload;
915 0 : PRUint32 totalAvail = avail;
916 :
917 0 : while (avail >= 2) {
918 0 : PRInt64 payloadLength = mFramePtr[1] & 0x7F;
919 0 : PRUint8 finBit = mFramePtr[0] & kFinalFragBit;
920 0 : PRUint8 rsvBits = mFramePtr[0] & 0x70;
921 0 : PRUint8 maskBit = mFramePtr[1] & kMaskBit;
922 0 : PRUint8 opcode = mFramePtr[0] & 0x0F;
923 :
924 0 : PRUint32 framingLength = 2;
925 0 : if (maskBit)
926 0 : framingLength += 4;
927 :
928 0 : if (payloadLength < 126) {
929 0 : if (avail < framingLength)
930 0 : break;
931 0 : } else if (payloadLength == 126) {
932 : // 16 bit length field
933 0 : framingLength += 2;
934 0 : if (avail < framingLength)
935 0 : break;
936 :
937 0 : payloadLength = mFramePtr[2] << 8 | mFramePtr[3];
938 : } else {
939 : // 64 bit length
940 0 : framingLength += 8;
941 0 : if (avail < framingLength)
942 0 : break;
943 :
944 0 : if (mFramePtr[2] & 0x80) {
945 : // Section 4.2 says that the most significant bit MUST be
946 : // 0. (i.e. this is really a 63 bit value)
947 0 : LOG(("WebSocketChannel:: high bit of 64 bit length set"));
948 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
949 0 : return NS_ERROR_ILLEGAL_VALUE;
950 : }
951 :
952 : // copy this in case it is unaligned
953 : PRUint64 tempLen;
954 0 : memcpy(&tempLen, mFramePtr + 2, 8);
955 0 : payloadLength = PR_ntohll(tempLen);
956 : }
957 :
958 0 : payload = mFramePtr + framingLength;
959 0 : avail -= framingLength;
960 :
961 0 : LOG(("WebSocketChannel::ProcessInput: payload %lld avail %lu\n",
962 : payloadLength, avail));
963 :
964 0 : if (payloadLength + mFragmentAccumulator > mMaxMessageSize) {
965 0 : AbortSession(NS_ERROR_FILE_TOO_BIG);
966 0 : return NS_ERROR_FILE_TOO_BIG;
967 : }
968 :
969 0 : if (avail < payloadLength)
970 0 : break;
971 :
972 0 : LOG(("WebSocketChannel::ProcessInput: Frame accumulated - opcode %d\n",
973 : opcode));
974 :
975 0 : if (maskBit) {
976 : // This is unexpected - the server does not generally send masked
977 : // frames to the client, but it is allowed
978 0 : LOG(("WebSocketChannel:: Client RECEIVING masked frame."));
979 :
980 : PRUint32 mask;
981 0 : memcpy(&mask, payload - 4, 4);
982 0 : mask = PR_ntohl(mask);
983 0 : ApplyMask(mask, payload, payloadLength);
984 : }
985 :
986 : // Control codes are required to have the fin bit set
987 0 : if (!finBit && (opcode & kControlFrameMask)) {
988 0 : LOG(("WebSocketChannel:: fragmented control frame code %d\n", opcode));
989 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
990 0 : return NS_ERROR_ILLEGAL_VALUE;
991 : }
992 :
993 0 : if (rsvBits) {
994 0 : LOG(("WebSocketChannel:: unexpected reserved bits %x\n", rsvBits));
995 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
996 0 : return NS_ERROR_ILLEGAL_VALUE;
997 : }
998 :
999 0 : if (!finBit || opcode == kContinuation) {
1000 : // This is part of a fragment response
1001 :
1002 : // Only the first frame has a non zero op code: Make sure we don't see a
1003 : // first frame while some old fragments are open
1004 0 : if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
1005 0 : LOG(("WebSocketChannel:: nested fragments\n"));
1006 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1007 0 : return NS_ERROR_ILLEGAL_VALUE;
1008 : }
1009 :
1010 0 : LOG(("WebSocketChannel:: Accumulating Fragment %lld\n", payloadLength));
1011 :
1012 0 : if (opcode == kContinuation) {
1013 :
1014 : // Make sure this continuation fragment isn't the first fragment
1015 0 : if (mFragmentOpcode == kContinuation) {
1016 0 : LOG(("WebSocketHeandler:: continuation code in first fragment\n"));
1017 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1018 0 : return NS_ERROR_ILLEGAL_VALUE;
1019 : }
1020 :
1021 : // For frag > 1 move the data body back on top of the headers
1022 : // so we have contiguous stream of data
1023 0 : NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
1024 : "payload offset from frameptr wrong");
1025 0 : ::memmove(mFramePtr, payload, avail);
1026 0 : payload = mFramePtr;
1027 0 : if (mBuffered)
1028 0 : mBuffered -= framingLength;
1029 : } else {
1030 0 : mFragmentOpcode = opcode;
1031 : }
1032 :
1033 0 : if (finBit) {
1034 0 : LOG(("WebSocketChannel:: Finalizing Fragment\n"));
1035 0 : payload -= mFragmentAccumulator;
1036 0 : payloadLength += mFragmentAccumulator;
1037 0 : avail += mFragmentAccumulator;
1038 0 : mFragmentAccumulator = 0;
1039 0 : opcode = mFragmentOpcode;
1040 : // reset to detect if next message illegally starts with continuation
1041 0 : mFragmentOpcode = kContinuation;
1042 : } else {
1043 0 : opcode = kContinuation;
1044 0 : mFragmentAccumulator += payloadLength;
1045 : }
1046 0 : } else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
1047 : // This frame is not part of a fragment sequence but we
1048 : // have an open fragment.. it must be a control code or else
1049 : // we have a problem
1050 0 : LOG(("WebSocketChannel:: illegal fragment sequence\n"));
1051 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1052 0 : return NS_ERROR_ILLEGAL_VALUE;
1053 : }
1054 :
1055 0 : if (mServerClosed) {
1056 0 : LOG(("WebSocketChannel:: ignoring read frame code %d after close\n",
1057 : opcode));
1058 : // nop
1059 0 : } else if (mStopped) {
1060 0 : LOG(("WebSocketChannel:: ignoring read frame code %d after completion\n",
1061 : opcode));
1062 0 : } else if (opcode == kText) {
1063 0 : LOG(("WebSocketChannel:: text frame received\n"));
1064 0 : if (mListener) {
1065 0 : nsCString utf8Data((const char *)payload, payloadLength);
1066 :
1067 : // Section 8.1 says to fail connection if invalid utf-8 in text message
1068 0 : if (!IsUTF8(utf8Data, false)) {
1069 0 : LOG(("WebSocketChannel:: text frame invalid utf-8\n"));
1070 0 : AbortSession(NS_ERROR_CANNOT_CONVERT_DATA);
1071 0 : return NS_ERROR_ILLEGAL_VALUE;
1072 : }
1073 :
1074 0 : NS_DispatchToMainThread(new CallOnMessageAvailable(this, utf8Data, -1));
1075 : }
1076 0 : } else if (opcode & kControlFrameMask) {
1077 : // control frames
1078 0 : if (payloadLength > 125) {
1079 0 : LOG(("WebSocketChannel:: bad control frame code %d length %d\n",
1080 : opcode, payloadLength));
1081 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1082 0 : return NS_ERROR_ILLEGAL_VALUE;
1083 : }
1084 :
1085 0 : if (opcode == kClose) {
1086 0 : LOG(("WebSocketChannel:: close received\n"));
1087 0 : mServerClosed = 1;
1088 :
1089 0 : mServerCloseCode = CLOSE_NO_STATUS;
1090 0 : if (payloadLength >= 2) {
1091 0 : memcpy(&mServerCloseCode, payload, 2);
1092 0 : mServerCloseCode = PR_ntohs(mServerCloseCode);
1093 0 : LOG(("WebSocketChannel:: close recvd code %u\n", mServerCloseCode));
1094 0 : PRUint16 msglen = payloadLength - 2;
1095 0 : if (msglen > 0) {
1096 0 : mServerCloseReason.SetLength(msglen);
1097 0 : memcpy(mServerCloseReason.BeginWriting(),
1098 0 : (const char *)payload + 2, msglen);
1099 :
1100 : // section 8.1 says to replace received non utf-8 sequences
1101 : // (which are non-conformant to send) with u+fffd,
1102 : // but secteam feels that silently rewriting messages is
1103 : // inappropriate - so we will fail the connection instead.
1104 0 : if (!IsUTF8(mServerCloseReason, false)) {
1105 0 : LOG(("WebSocketChannel:: close frame invalid utf-8\n"));
1106 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1107 0 : return NS_ERROR_ILLEGAL_VALUE;
1108 : }
1109 :
1110 0 : LOG(("WebSocketChannel:: close msg %s\n",
1111 : mServerCloseReason.get()));
1112 : }
1113 : }
1114 :
1115 0 : if (mCloseTimer) {
1116 0 : mCloseTimer->Cancel();
1117 0 : mCloseTimer = nsnull;
1118 : }
1119 0 : if (mListener) {
1120 : NS_DispatchToMainThread(new CallOnServerClose(this, mServerCloseCode,
1121 0 : mServerCloseReason));
1122 : }
1123 :
1124 0 : if (mClientClosed)
1125 0 : ReleaseSession();
1126 0 : } else if (opcode == kPing) {
1127 0 : LOG(("WebSocketChannel:: ping received\n"));
1128 0 : GeneratePong(payload, payloadLength);
1129 0 : } else if (opcode == kPong) {
1130 : // opcode kPong: the mere act of receiving the packet is all we need
1131 : // to do for the pong to trigger the activity timers
1132 0 : LOG(("WebSocketChannel:: pong received\n"));
1133 : } else {
1134 : /* unknown control frame opcode */
1135 0 : LOG(("WebSocketChannel:: unknown control op code %d\n", opcode));
1136 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1137 0 : return NS_ERROR_ILLEGAL_VALUE;
1138 : }
1139 :
1140 0 : if (mFragmentAccumulator) {
1141 : // Remove the control frame from the stream so we have a contiguous
1142 : // data buffer of reassembled fragments
1143 0 : LOG(("WebSocketChannel:: Removing Control From Read buffer\n"));
1144 0 : NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
1145 : "payload offset from frameptr wrong");
1146 0 : ::memmove(mFramePtr, payload + payloadLength, avail - payloadLength);
1147 0 : payload = mFramePtr;
1148 0 : avail -= payloadLength;
1149 0 : if (mBuffered)
1150 0 : mBuffered -= framingLength + payloadLength;
1151 0 : payloadLength = 0;
1152 : }
1153 0 : } else if (opcode == kBinary) {
1154 0 : LOG(("WebSocketChannel:: binary frame received\n"));
1155 0 : if (mListener) {
1156 0 : nsCString binaryData((const char *)payload, payloadLength);
1157 : NS_DispatchToMainThread(new CallOnMessageAvailable(this, binaryData,
1158 0 : payloadLength));
1159 : }
1160 0 : } else if (opcode != kContinuation) {
1161 : /* unknown opcode */
1162 0 : LOG(("WebSocketChannel:: unknown op code %d\n", opcode));
1163 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1164 0 : return NS_ERROR_ILLEGAL_VALUE;
1165 : }
1166 :
1167 0 : mFramePtr = payload + payloadLength;
1168 0 : avail -= payloadLength;
1169 0 : totalAvail = avail;
1170 : }
1171 :
1172 : // Adjust the stateful buffer. If we were operating off the stack and
1173 : // now have a partial message then transition to the buffer, or if
1174 : // we were working off the buffer but no longer have any active state
1175 : // then transition to the stack
1176 0 : if (!IsPersistentFramePtr()) {
1177 0 : mBuffered = 0;
1178 :
1179 0 : if (mFragmentAccumulator) {
1180 0 : LOG(("WebSocketChannel:: Setup Buffer due to fragment"));
1181 :
1182 0 : if (!UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
1183 0 : totalAvail + mFragmentAccumulator, 0, nsnull)) {
1184 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1185 0 : return NS_ERROR_ILLEGAL_VALUE;
1186 : }
1187 :
1188 : // UpdateReadBuffer will reset the frameptr to the beginning
1189 : // of new saved state, so we need to skip past processed framgents
1190 0 : mFramePtr += mFragmentAccumulator;
1191 0 : } else if (totalAvail) {
1192 0 : LOG(("WebSocketChannel:: Setup Buffer due to partial frame"));
1193 0 : if (!UpdateReadBuffer(mFramePtr, totalAvail, 0, nsnull)) {
1194 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1195 0 : return NS_ERROR_ILLEGAL_VALUE;
1196 : }
1197 : }
1198 0 : } else if (!mFragmentAccumulator && !totalAvail) {
1199 : // If we were working off a saved buffer state and there is no partial
1200 : // frame or fragment in process, then revert to stack behavior
1201 0 : LOG(("WebSocketChannel:: Internal buffering not needed anymore"));
1202 0 : mBuffered = 0;
1203 :
1204 : // release memory if we've been processing a large message
1205 0 : if (mBufferSize > kIncomingBufferStableSize) {
1206 0 : mBufferSize = kIncomingBufferStableSize;
1207 0 : moz_free(mBuffer);
1208 0 : mBuffer = (PRUint8 *)moz_xmalloc(mBufferSize);
1209 : }
1210 : }
1211 0 : return NS_OK;
1212 : }
1213 :
1214 : void
1215 0 : WebSocketChannel::ApplyMask(PRUint32 mask, PRUint8 *data, PRUint64 len)
1216 : {
1217 0 : if (!data || len == 0)
1218 0 : return;
1219 :
1220 : // Optimally we want to apply the mask 32 bits at a time,
1221 : // but the buffer might not be alligned. So we first deal with
1222 : // 0 to 3 bytes of preamble individually
1223 :
1224 0 : while (len && (reinterpret_cast<PRUptrdiff>(data) & 3)) {
1225 0 : *data ^= mask >> 24;
1226 0 : mask = PR_ROTATE_LEFT32(mask, 8);
1227 0 : data++;
1228 0 : len--;
1229 : }
1230 :
1231 : // perform mask on full words of data
1232 :
1233 0 : PRUint32 *iData = (PRUint32 *) data;
1234 0 : PRUint32 *end = iData + (len / 4);
1235 0 : mask = PR_htonl(mask);
1236 0 : for (; iData < end; iData++)
1237 0 : *iData ^= mask;
1238 0 : mask = PR_ntohl(mask);
1239 0 : data = (PRUint8 *)iData;
1240 0 : len = len % 4;
1241 :
1242 : // There maybe up to 3 trailing bytes that need to be dealt with
1243 : // individually
1244 :
1245 0 : while (len) {
1246 0 : *data ^= mask >> 24;
1247 0 : mask = PR_ROTATE_LEFT32(mask, 8);
1248 0 : data++;
1249 0 : len--;
1250 : }
1251 : }
1252 :
1253 : void
1254 0 : WebSocketChannel::GeneratePing()
1255 : {
1256 0 : nsCString *buf = new nsCString();
1257 0 : buf->Assign("PING");
1258 : EnqueueOutgoingMessage(mOutgoingPingMessages,
1259 0 : new OutboundMessage(kMsgTypePing, buf));
1260 0 : }
1261 :
1262 : void
1263 0 : WebSocketChannel::GeneratePong(PRUint8 *payload, PRUint32 len)
1264 : {
1265 0 : nsCString *buf = new nsCString();
1266 0 : buf->SetLength(len);
1267 0 : if (buf->Length() < len) {
1268 0 : LOG(("WebSocketChannel::GeneratePong Allocation Failure\n"));
1269 0 : delete buf;
1270 0 : return;
1271 : }
1272 :
1273 0 : memcpy(buf->BeginWriting(), payload, len);
1274 : EnqueueOutgoingMessage(mOutgoingPongMessages,
1275 0 : new OutboundMessage(kMsgTypePong, buf));
1276 : }
1277 :
1278 : void
1279 0 : WebSocketChannel::EnqueueOutgoingMessage(nsDeque &aQueue,
1280 : OutboundMessage *aMsg)
1281 : {
1282 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
1283 :
1284 0 : LOG(("WebSocketChannel::EnqueueOutgoingMessage %p "
1285 : "queueing msg %p [type=%s len=%d]\n",
1286 : this, aMsg, msgNames[aMsg->GetMsgType()], aMsg->Length()));
1287 :
1288 0 : aQueue.Push(aMsg);
1289 0 : OnOutputStreamReady(mSocketOut);
1290 0 : }
1291 :
1292 :
1293 : PRUint16
1294 0 : WebSocketChannel::ResultToCloseCode(nsresult resultCode)
1295 : {
1296 0 : if (NS_SUCCEEDED(resultCode))
1297 0 : return CLOSE_NORMAL;
1298 0 : if (resultCode == NS_ERROR_FILE_TOO_BIG)
1299 0 : return CLOSE_TOO_LARGE;
1300 0 : if (resultCode == NS_BASE_STREAM_CLOSED ||
1301 : resultCode == NS_ERROR_NET_TIMEOUT ||
1302 : resultCode == NS_ERROR_CONNECTION_REFUSED) {
1303 0 : return CLOSE_ABNORMAL;
1304 : }
1305 0 : if (resultCode == NS_ERROR_CANNOT_CONVERT_DATA)
1306 0 : return CLOSE_INVALID_PAYLOAD;
1307 :
1308 0 : return CLOSE_PROTOCOL_ERROR;
1309 : }
1310 :
1311 : void
1312 0 : WebSocketChannel::PrimeNewOutgoingMessage()
1313 : {
1314 0 : LOG(("WebSocketChannel::PrimeNewOutgoingMessage() %p\n", this));
1315 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
1316 0 : NS_ABORT_IF_FALSE(!mCurrentOut, "Current message in progress");
1317 :
1318 0 : nsresult rv = NS_OK;
1319 :
1320 0 : mCurrentOut = (OutboundMessage *)mOutgoingPongMessages.PopFront();
1321 0 : if (mCurrentOut) {
1322 0 : NS_ABORT_IF_FALSE(mCurrentOut->GetMsgType() == kMsgTypePong,
1323 : "Not pong message!");
1324 : } else {
1325 0 : mCurrentOut = (OutboundMessage *)mOutgoingPingMessages.PopFront();
1326 0 : if (mCurrentOut)
1327 0 : NS_ABORT_IF_FALSE(mCurrentOut->GetMsgType() == kMsgTypePing,
1328 : "Not ping message!");
1329 : else
1330 0 : mCurrentOut = (OutboundMessage *)mOutgoingMessages.PopFront();
1331 : }
1332 :
1333 0 : if (!mCurrentOut)
1334 0 : return;
1335 :
1336 0 : WsMsgType msgType = mCurrentOut->GetMsgType();
1337 :
1338 0 : LOG(("WebSocketChannel::PrimeNewOutgoingMessage "
1339 : "%p found queued msg %p [type=%s len=%d]\n",
1340 : this, mCurrentOut, msgNames[msgType], mCurrentOut->Length()));
1341 :
1342 0 : mCurrentOutSent = 0;
1343 0 : mHdrOut = mOutHeader;
1344 :
1345 0 : PRUint8 *payload = nsnull;
1346 :
1347 0 : if (msgType == kMsgTypeFin) {
1348 : // This is a demand to create a close message
1349 0 : if (mClientClosed) {
1350 0 : DeleteCurrentOutGoingMessage();
1351 0 : PrimeNewOutgoingMessage();
1352 0 : return;
1353 : }
1354 :
1355 0 : mClientClosed = 1;
1356 0 : mOutHeader[0] = kFinalFragBit | kClose;
1357 0 : mOutHeader[1] = 0x02; // payload len = 2, maybe more for reason
1358 0 : mOutHeader[1] |= kMaskBit;
1359 :
1360 : // payload is offset 6 including 4 for the mask
1361 0 : payload = mOutHeader + 6;
1362 :
1363 : // length is 8 plus any reason information
1364 0 : mHdrOutToSend = 8;
1365 :
1366 : // The close reason code sits in the first 2 bytes of payload
1367 : // If the channel user provided a code and reason during Close()
1368 : // and there isn't an internal error, use that.
1369 0 : if (NS_SUCCEEDED(mStopOnClose) && mScriptCloseCode) {
1370 0 : *((PRUint16 *)payload) = PR_htons(mScriptCloseCode);
1371 0 : if (!mScriptCloseReason.IsEmpty()) {
1372 0 : NS_ABORT_IF_FALSE(mScriptCloseReason.Length() <= 123,
1373 : "Close Reason Too Long");
1374 0 : mOutHeader[1] += mScriptCloseReason.Length();
1375 0 : mHdrOutToSend += mScriptCloseReason.Length();
1376 0 : memcpy (payload + 2,
1377 0 : mScriptCloseReason.BeginReading(),
1378 0 : mScriptCloseReason.Length());
1379 : }
1380 : } else {
1381 0 : *((PRUint16 *)payload) = PR_htons(ResultToCloseCode(mStopOnClose));
1382 : }
1383 :
1384 0 : if (mServerClosed) {
1385 : /* bidi close complete */
1386 0 : mReleaseOnTransmit = 1;
1387 0 : } else if (NS_FAILED(mStopOnClose)) {
1388 : /* result of abort session - give up */
1389 0 : StopSession(mStopOnClose);
1390 : } else {
1391 : /* wait for reciprocal close from server */
1392 0 : mCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1393 0 : if (NS_SUCCEEDED(rv)) {
1394 0 : mCloseTimer->InitWithCallback(this, mCloseTimeout,
1395 0 : nsITimer::TYPE_ONE_SHOT);
1396 : } else {
1397 0 : StopSession(rv);
1398 : }
1399 : }
1400 : } else {
1401 0 : switch (msgType) {
1402 : case kMsgTypePong:
1403 0 : mOutHeader[0] = kFinalFragBit | kPong;
1404 0 : break;
1405 : case kMsgTypePing:
1406 0 : mOutHeader[0] = kFinalFragBit | kPing;
1407 0 : break;
1408 : case kMsgTypeString:
1409 0 : mOutHeader[0] = kFinalFragBit | kText;
1410 0 : break;
1411 : case kMsgTypeStream:
1412 : // HACK ALERT: read in entire stream into string.
1413 : // Will block socket transport thread if file is blocking.
1414 : // TODO: bug 704447: don't block socket thread!
1415 0 : rv = mCurrentOut->ConvertStreamToString();
1416 0 : if (NS_FAILED(rv)) {
1417 0 : AbortSession(rv);
1418 0 : return;
1419 : }
1420 : // Now we're a binary string
1421 0 : msgType = kMsgTypeBinaryString;
1422 :
1423 : // no break: fall down into binary string case
1424 :
1425 : case kMsgTypeBinaryString:
1426 0 : mOutHeader[0] = kFinalFragBit | kBinary;
1427 0 : break;
1428 : case kMsgTypeFin:
1429 0 : NS_ABORT_IF_FALSE(false, "unreachable"); // avoid compiler warning
1430 0 : break;
1431 : }
1432 :
1433 0 : if (mCurrentOut->Length() < 126) {
1434 0 : mOutHeader[1] = mCurrentOut->Length() | kMaskBit;
1435 0 : mHdrOutToSend = 6;
1436 0 : } else if (mCurrentOut->Length() <= 0xffff) {
1437 0 : mOutHeader[1] = 126 | kMaskBit;
1438 0 : ((PRUint16 *)mOutHeader)[1] =
1439 0 : PR_htons(mCurrentOut->Length());
1440 0 : mHdrOutToSend = 8;
1441 : } else {
1442 0 : mOutHeader[1] = 127 | kMaskBit;
1443 0 : PRUint64 tempLen = mCurrentOut->Length();
1444 0 : tempLen = PR_htonll(tempLen);
1445 0 : memcpy(mOutHeader + 2, &tempLen, 8);
1446 0 : mHdrOutToSend = 14;
1447 : }
1448 0 : payload = mOutHeader + mHdrOutToSend;
1449 : }
1450 :
1451 0 : NS_ABORT_IF_FALSE(payload, "payload offset not found");
1452 :
1453 : // Perform the sending mask. Never use a zero mask
1454 : PRUint32 mask;
1455 0 : do {
1456 : PRUint8 *buffer;
1457 0 : nsresult rv = mRandomGenerator->GenerateRandomBytes(4, &buffer);
1458 0 : if (NS_FAILED(rv)) {
1459 0 : LOG(("WebSocketChannel::PrimeNewOutgoingMessage(): "
1460 : "GenerateRandomBytes failure %x\n", rv));
1461 0 : StopSession(rv);
1462 0 : return;
1463 : }
1464 0 : mask = * reinterpret_cast<PRUint32 *>(buffer);
1465 0 : NS_Free(buffer);
1466 0 : } while (!mask);
1467 0 : *(((PRUint32 *)payload) - 1) = PR_htonl(mask);
1468 :
1469 0 : LOG(("WebSocketChannel::PrimeNewOutgoingMessage() using mask %08x\n", mask));
1470 :
1471 : // We don't mask the framing, but occasionally we stick a little payload
1472 : // data in the buffer used for the framing. Close frames are the current
1473 : // example. This data needs to be masked, but it is never more than a
1474 : // handful of bytes and might rotate the mask, so we can just do it locally.
1475 : // For real data frames we ship the bulk of the payload off to ApplyMask()
1476 :
1477 0 : while (payload < (mOutHeader + mHdrOutToSend)) {
1478 0 : *payload ^= mask >> 24;
1479 0 : mask = PR_ROTATE_LEFT32(mask, 8);
1480 0 : payload++;
1481 : }
1482 :
1483 : // Mask the real message payloads
1484 :
1485 0 : ApplyMask(mask, mCurrentOut->BeginWriting(), mCurrentOut->Length());
1486 :
1487 : // for small frames, copy it all together for a contiguous write
1488 0 : if (mCurrentOut->Length() <= kCopyBreak) {
1489 0 : memcpy(mOutHeader + mHdrOutToSend, mCurrentOut->BeginWriting(),
1490 0 : mCurrentOut->Length());
1491 0 : mHdrOutToSend += mCurrentOut->Length();
1492 0 : mCurrentOutSent = mCurrentOut->Length();
1493 : }
1494 :
1495 0 : if (mCompressor) {
1496 : // assume a 1/3 reduction in size for sizing the buffer
1497 : // the buffer is used multiple times if necessary
1498 0 : PRUint32 currentHeaderSize = mHdrOutToSend;
1499 0 : mHdrOutToSend = 0;
1500 :
1501 : EnsureHdrOut(32 +
1502 0 : (currentHeaderSize + mCurrentOut->Length() - mCurrentOutSent)
1503 0 : / 2 * 3);
1504 : mCompressor->Deflate(mOutHeader, currentHeaderSize,
1505 0 : mCurrentOut->BeginReading() + mCurrentOutSent,
1506 0 : mCurrentOut->Length() - mCurrentOutSent);
1507 :
1508 : // All of the compressed data now resides in {mHdrOut, mHdrOutToSend}
1509 : // so do not send the body again
1510 0 : mCurrentOutSent = mCurrentOut->Length();
1511 : }
1512 :
1513 : // Transmitting begins - mHdrOutToSend bytes from mOutHeader and
1514 : // mCurrentOut->Length() bytes from mCurrentOut. The latter may be
1515 : // coaleseced into the former for small messages or as the result of the
1516 : // compression process,
1517 : }
1518 :
1519 : void
1520 0 : WebSocketChannel::DeleteCurrentOutGoingMessage()
1521 : {
1522 0 : delete mCurrentOut;
1523 0 : mCurrentOut = nsnull;
1524 0 : mCurrentOutSent = 0;
1525 0 : }
1526 :
1527 : void
1528 0 : WebSocketChannel::EnsureHdrOut(PRUint32 size)
1529 : {
1530 0 : LOG(("WebSocketChannel::EnsureHdrOut() %p [%d]\n", this, size));
1531 :
1532 0 : if (mDynamicOutputSize < size) {
1533 0 : mDynamicOutputSize = size;
1534 : mDynamicOutput =
1535 0 : (PRUint8 *) moz_xrealloc(mDynamicOutput, mDynamicOutputSize);
1536 : }
1537 :
1538 0 : mHdrOut = mDynamicOutput;
1539 0 : }
1540 :
1541 : void
1542 0 : WebSocketChannel::CleanupConnection()
1543 : {
1544 0 : LOG(("WebSocketChannel::CleanupConnection() %p", this));
1545 :
1546 0 : if (mLingeringCloseTimer) {
1547 0 : mLingeringCloseTimer->Cancel();
1548 0 : mLingeringCloseTimer = nsnull;
1549 : }
1550 :
1551 0 : if (mSocketIn) {
1552 0 : mSocketIn->AsyncWait(nsnull, 0, 0, nsnull);
1553 0 : mSocketIn = nsnull;
1554 : }
1555 :
1556 0 : if (mSocketOut) {
1557 0 : mSocketOut->AsyncWait(nsnull, 0, 0, nsnull);
1558 0 : mSocketOut = nsnull;
1559 : }
1560 :
1561 0 : if (mTransport) {
1562 0 : mTransport->SetSecurityCallbacks(nsnull);
1563 0 : mTransport->SetEventSink(nsnull, nsnull);
1564 0 : mTransport->Close(NS_BASE_STREAM_CLOSED);
1565 0 : mTransport = nsnull;
1566 : }
1567 0 : }
1568 :
1569 : void
1570 0 : WebSocketChannel::StopSession(nsresult reason)
1571 : {
1572 0 : LOG(("WebSocketChannel::StopSession() %p [%x]\n", this, reason));
1573 :
1574 : // normally this should be called on socket thread, but it is ok to call it
1575 : // from OnStartRequest before the socket thread machine has gotten underway
1576 :
1577 0 : NS_ABORT_IF_FALSE(mStopped,
1578 : "stopsession() has not transitioned through abort or close");
1579 :
1580 0 : if (!mChannelWasOpened) {
1581 : // The HTTP channel information will never be used in this case
1582 0 : mChannel = nsnull;
1583 0 : mHttpChannel = nsnull;
1584 0 : mLoadGroup = nsnull;
1585 0 : mCallbacks = nsnull;
1586 : }
1587 :
1588 0 : if (mOpenRunning || mOpenBlocked)
1589 0 : sWebSocketAdmissions->Cancel(this);
1590 :
1591 0 : if (mCloseTimer) {
1592 0 : mCloseTimer->Cancel();
1593 0 : mCloseTimer = nsnull;
1594 : }
1595 :
1596 0 : if (mOpenTimer) {
1597 0 : mOpenTimer->Cancel();
1598 0 : mOpenTimer = nsnull;
1599 : }
1600 :
1601 0 : if (mPingTimer) {
1602 0 : mPingTimer->Cancel();
1603 0 : mPingTimer = nsnull;
1604 : }
1605 :
1606 0 : if (mSocketIn && !mTCPClosed) {
1607 : // Drain, within reason, this socket. if we leave any data
1608 : // unconsumed (including the tcp fin) a RST will be generated
1609 : // The right thing to do here is shutdown(SHUT_WR) and then wait
1610 : // a little while to see if any data comes in.. but there is no
1611 : // reason to delay things for that when the websocket handshake
1612 : // is supposed to guarantee a quiet connection except for that fin.
1613 :
1614 : char buffer[512];
1615 0 : PRUint32 count = 0;
1616 0 : PRUint32 total = 0;
1617 : nsresult rv;
1618 0 : do {
1619 0 : total += count;
1620 0 : rv = mSocketIn->Read(buffer, 512, &count);
1621 0 : if (rv != NS_BASE_STREAM_WOULD_BLOCK &&
1622 0 : (NS_FAILED(rv) || count == 0))
1623 0 : mTCPClosed = true;
1624 0 : } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000);
1625 : }
1626 :
1627 0 : if (!mTCPClosed && mTransport && sWebSocketAdmissions &&
1628 0 : sWebSocketAdmissions->SessionCount() < kLingeringCloseThreshold) {
1629 :
1630 : // 7.1.1 says that the client SHOULD wait for the server to close the TCP
1631 : // connection. This is so we can reuse port numbers before 2 MSL expires,
1632 : // which is not really as much of a concern for us as the amount of state
1633 : // that might be accrued by keeping this channel object around waiting for
1634 : // the server. We handle the SHOULD by waiting a short time in the common
1635 : // case, but not waiting in the case of high concurrency.
1636 : //
1637 : // Normally this will be taken care of in AbortSession() after mTCPClosed
1638 : // is set when the server close arrives without waiting for the timeout to
1639 : // expire.
1640 :
1641 0 : LOG(("WebSocketChannel::StopSession: Wait for Server TCP close"));
1642 :
1643 : nsresult rv;
1644 0 : mLingeringCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1645 0 : if (NS_SUCCEEDED(rv))
1646 0 : mLingeringCloseTimer->InitWithCallback(this, kLingeringCloseTimeout,
1647 0 : nsITimer::TYPE_ONE_SHOT);
1648 : else
1649 0 : CleanupConnection();
1650 : } else {
1651 0 : CleanupConnection();
1652 : }
1653 :
1654 0 : if (mDNSRequest) {
1655 0 : mDNSRequest->Cancel(NS_ERROR_UNEXPECTED);
1656 0 : mDNSRequest = nsnull;
1657 : }
1658 :
1659 0 : mInflateReader = nsnull;
1660 0 : mInflateStream = nsnull;
1661 :
1662 0 : delete mCompressor;
1663 0 : mCompressor = nsnull;
1664 :
1665 0 : if (!mCalledOnStop) {
1666 0 : mCalledOnStop = 1;
1667 0 : if (mListener)
1668 0 : NS_DispatchToMainThread(new CallOnStop(this, reason));
1669 : }
1670 :
1671 : return;
1672 : }
1673 :
1674 : void
1675 0 : WebSocketChannel::AbortSession(nsresult reason)
1676 : {
1677 0 : LOG(("WebSocketChannel::AbortSession() %p [reason %x] stopped = %d\n",
1678 : this, reason, mStopped));
1679 :
1680 : // normally this should be called on socket thread, but it is ok to call it
1681 : // from the main thread before StartWebsocketData() has completed
1682 :
1683 : // When we are failing we need to close the TCP connection immediately
1684 : // as per 7.1.1
1685 0 : mTCPClosed = true;
1686 :
1687 0 : if (mLingeringCloseTimer) {
1688 0 : NS_ABORT_IF_FALSE(mStopped, "Lingering without Stop");
1689 0 : LOG(("WebSocketChannel:: Cleanup connection based on TCP Close"));
1690 0 : CleanupConnection();
1691 0 : return;
1692 : }
1693 :
1694 0 : if (mStopped)
1695 0 : return;
1696 0 : mStopped = 1;
1697 :
1698 0 : if (mTransport && reason != NS_BASE_STREAM_CLOSED &&
1699 0 : !mRequestedClose && !mClientClosed && !mServerClosed) {
1700 0 : mRequestedClose = 1;
1701 0 : mStopOnClose = reason;
1702 0 : mSocketThread->Dispatch(
1703 0 : new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nsnull)),
1704 0 : nsIEventTarget::DISPATCH_NORMAL);
1705 : } else {
1706 0 : StopSession(reason);
1707 : }
1708 : }
1709 :
1710 : // ReleaseSession is called on orderly shutdown
1711 : void
1712 0 : WebSocketChannel::ReleaseSession()
1713 : {
1714 0 : LOG(("WebSocketChannel::ReleaseSession() %p stopped = %d\n",
1715 : this, mStopped));
1716 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
1717 :
1718 0 : if (mStopped)
1719 0 : return;
1720 0 : mStopped = 1;
1721 0 : StopSession(NS_OK);
1722 : }
1723 :
1724 : nsresult
1725 0 : WebSocketChannel::HandleExtensions()
1726 : {
1727 0 : LOG(("WebSocketChannel::HandleExtensions() %p\n", this));
1728 :
1729 : nsresult rv;
1730 0 : nsCAutoString extensions;
1731 :
1732 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1733 :
1734 0 : rv = mHttpChannel->GetResponseHeader(
1735 0 : NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions);
1736 0 : if (NS_SUCCEEDED(rv)) {
1737 0 : if (!extensions.IsEmpty()) {
1738 0 : if (!extensions.Equals(NS_LITERAL_CSTRING("deflate-stream"))) {
1739 0 : LOG(("WebSocketChannel::OnStartRequest: "
1740 : "HTTP Sec-WebSocket-Exensions negotiated unknown value %s\n",
1741 : extensions.get()));
1742 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1743 0 : return NS_ERROR_ILLEGAL_VALUE;
1744 : }
1745 :
1746 0 : if (!mAllowCompression) {
1747 0 : LOG(("WebSocketChannel::HandleExtensions: "
1748 : "Recvd Compression Extension that wasn't offered\n"));
1749 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
1750 0 : return NS_ERROR_ILLEGAL_VALUE;
1751 : }
1752 :
1753 : nsCOMPtr<nsIStreamConverterService> serv =
1754 0 : do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
1755 0 : if (NS_FAILED(rv)) {
1756 0 : LOG(("WebSocketChannel:: Cannot find compression service\n"));
1757 0 : AbortSession(NS_ERROR_UNEXPECTED);
1758 0 : return NS_ERROR_UNEXPECTED;
1759 : }
1760 :
1761 0 : rv = serv->AsyncConvertData("deflate", "uncompressed", this, nsnull,
1762 0 : getter_AddRefs(mInflateReader));
1763 :
1764 0 : if (NS_FAILED(rv)) {
1765 0 : LOG(("WebSocketChannel:: Cannot find inflate listener\n"));
1766 0 : AbortSession(NS_ERROR_UNEXPECTED);
1767 0 : return NS_ERROR_UNEXPECTED;
1768 : }
1769 :
1770 0 : mInflateStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
1771 :
1772 0 : if (NS_FAILED(rv)) {
1773 0 : LOG(("WebSocketChannel:: Cannot find inflate stream\n"));
1774 0 : AbortSession(NS_ERROR_UNEXPECTED);
1775 0 : return NS_ERROR_UNEXPECTED;
1776 : }
1777 :
1778 0 : mCompressor = new nsWSCompression(this, mSocketOut);
1779 0 : if (!mCompressor->Active()) {
1780 0 : LOG(("WebSocketChannel:: Cannot init deflate object\n"));
1781 0 : delete mCompressor;
1782 0 : mCompressor = nsnull;
1783 0 : AbortSession(NS_ERROR_UNEXPECTED);
1784 0 : return NS_ERROR_UNEXPECTED;
1785 : }
1786 0 : mNegotiatedExtensions = extensions;
1787 : }
1788 : }
1789 :
1790 0 : return NS_OK;
1791 : }
1792 :
1793 : nsresult
1794 0 : WebSocketChannel::SetupRequest()
1795 : {
1796 0 : LOG(("WebSocketChannel::SetupRequest() %p\n", this));
1797 :
1798 : nsresult rv;
1799 :
1800 0 : if (mLoadGroup) {
1801 0 : rv = mHttpChannel->SetLoadGroup(mLoadGroup);
1802 0 : NS_ENSURE_SUCCESS(rv, rv);
1803 : }
1804 :
1805 0 : rv = mHttpChannel->SetLoadFlags(nsIRequest::LOAD_BACKGROUND |
1806 : nsIRequest::INHIBIT_CACHING |
1807 0 : nsIRequest::LOAD_BYPASS_CACHE);
1808 0 : NS_ENSURE_SUCCESS(rv, rv);
1809 :
1810 : // draft-ietf-hybi-thewebsocketprotocol-07 illustrates Upgrade: websocket
1811 : // in lower case, so go with that. It is technically case insensitive.
1812 0 : rv = mChannel->HTTPUpgrade(NS_LITERAL_CSTRING("websocket"), this);
1813 0 : NS_ENSURE_SUCCESS(rv, rv);
1814 :
1815 0 : mHttpChannel->SetRequestHeader(
1816 0 : NS_LITERAL_CSTRING("Sec-WebSocket-Version"),
1817 0 : NS_LITERAL_CSTRING(SEC_WEBSOCKET_VERSION), false);
1818 :
1819 0 : if (!mOrigin.IsEmpty())
1820 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), mOrigin,
1821 0 : false);
1822 :
1823 0 : if (!mProtocol.IsEmpty())
1824 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
1825 0 : mProtocol, true);
1826 :
1827 0 : if (mAllowCompression)
1828 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
1829 0 : NS_LITERAL_CSTRING("deflate-stream"),
1830 0 : false);
1831 :
1832 : PRUint8 *secKey;
1833 0 : nsCAutoString secKeyString;
1834 :
1835 0 : rv = mRandomGenerator->GenerateRandomBytes(16, &secKey);
1836 0 : NS_ENSURE_SUCCESS(rv, rv);
1837 0 : char* b64 = PL_Base64Encode((const char *)secKey, 16, nsnull);
1838 0 : NS_Free(secKey);
1839 0 : if (!b64)
1840 0 : return NS_ERROR_OUT_OF_MEMORY;
1841 0 : secKeyString.Assign(b64);
1842 0 : PR_Free(b64);
1843 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Sec-WebSocket-Key"),
1844 0 : secKeyString, false);
1845 0 : LOG(("WebSocketChannel::SetupRequest: client key %s\n", secKeyString.get()));
1846 :
1847 : // prepare the value we expect to see in
1848 : // the sec-websocket-accept response header
1849 0 : secKeyString.AppendLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
1850 : nsCOMPtr<nsICryptoHash> hasher =
1851 0 : do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1852 0 : NS_ENSURE_SUCCESS(rv, rv);
1853 0 : rv = hasher->Init(nsICryptoHash::SHA1);
1854 0 : NS_ENSURE_SUCCESS(rv, rv);
1855 0 : rv = hasher->Update((const PRUint8 *) secKeyString.BeginWriting(),
1856 0 : secKeyString.Length());
1857 0 : NS_ENSURE_SUCCESS(rv, rv);
1858 0 : rv = hasher->Finish(true, mHashedSecret);
1859 0 : NS_ENSURE_SUCCESS(rv, rv);
1860 0 : LOG(("WebSocketChannel::SetupRequest: expected server key %s\n",
1861 : mHashedSecret.get()));
1862 :
1863 0 : return NS_OK;
1864 : }
1865 :
1866 : nsresult
1867 0 : WebSocketChannel::ApplyForAdmission()
1868 : {
1869 0 : LOG(("WebSocketChannel::ApplyForAdmission() %p\n", this));
1870 :
1871 : // Websockets has a policy of 1 session at a time being allowed in the
1872 : // CONNECTING state per server IP address (not hostname)
1873 :
1874 : nsresult rv;
1875 0 : nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
1876 0 : NS_ENSURE_SUCCESS(rv, rv);
1877 :
1878 0 : nsCString hostName;
1879 0 : rv = mURI->GetHost(hostName);
1880 0 : NS_ENSURE_SUCCESS(rv, rv);
1881 0 : mAddress = hostName;
1882 :
1883 : // expect the callback in ::OnLookupComplete
1884 0 : LOG(("WebSocketChannel::ApplyForAdmission: checking for concurrent open\n"));
1885 0 : nsCOMPtr<nsIThread> mainThread;
1886 0 : NS_GetMainThread(getter_AddRefs(mainThread));
1887 0 : dns->AsyncResolve(hostName, 0, this, mainThread, getter_AddRefs(mDNSRequest));
1888 0 : NS_ENSURE_SUCCESS(rv, rv);
1889 :
1890 0 : return NS_OK;
1891 : }
1892 :
1893 : // Called after both OnStartRequest and OnTransportAvailable have
1894 : // executed. This essentially ends the handshake and starts the websockets
1895 : // protocol state machine.
1896 : nsresult
1897 0 : WebSocketChannel::StartWebsocketData()
1898 : {
1899 0 : LOG(("WebSocketChannel::StartWebsocketData() %p", this));
1900 :
1901 0 : return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
1902 : }
1903 :
1904 : // nsIDNSListener
1905 :
1906 : NS_IMETHODIMP
1907 0 : WebSocketChannel::OnLookupComplete(nsICancelable *aRequest,
1908 : nsIDNSRecord *aRecord,
1909 : nsresult aStatus)
1910 : {
1911 0 : LOG(("WebSocketChannel::OnLookupComplete() %p [%p %p %x]\n",
1912 : this, aRequest, aRecord, aStatus));
1913 :
1914 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
1915 0 : NS_ABORT_IF_FALSE(aRequest == mDNSRequest || mStopped,
1916 : "wrong dns request");
1917 :
1918 0 : if (mStopped) {
1919 0 : LOG(("WebSocketChannel::OnLookupComplete: Request Already Stopped\n"));
1920 0 : return NS_OK;
1921 : }
1922 :
1923 0 : mDNSRequest = nsnull;
1924 :
1925 : // These failures are not fatal - we just use the hostname as the key
1926 0 : if (NS_FAILED(aStatus)) {
1927 0 : LOG(("WebSocketChannel::OnLookupComplete: No DNS Response\n"));
1928 : } else {
1929 0 : nsresult rv = aRecord->GetNextAddrAsString(mAddress);
1930 0 : if (NS_FAILED(rv))
1931 0 : LOG(("WebSocketChannel::OnLookupComplete: Failed GetNextAddr\n"));
1932 : }
1933 :
1934 0 : if (sWebSocketAdmissions->ConditionallyConnect(mAddress, this)) {
1935 0 : LOG(("WebSocketChannel::OnLookupComplete: Proceeding with Open\n"));
1936 : } else {
1937 0 : LOG(("WebSocketChannel::OnLookupComplete: Deferring Open\n"));
1938 : }
1939 :
1940 0 : return NS_OK;
1941 : }
1942 :
1943 : // nsIInterfaceRequestor
1944 :
1945 : NS_IMETHODIMP
1946 0 : WebSocketChannel::GetInterface(const nsIID & iid, void **result NS_OUTPARAM)
1947 : {
1948 0 : LOG(("WebSocketChannel::GetInterface() %p\n", this));
1949 :
1950 0 : if (iid.Equals(NS_GET_IID(nsIChannelEventSink)))
1951 0 : return QueryInterface(iid, result);
1952 :
1953 0 : if (mCallbacks)
1954 0 : return mCallbacks->GetInterface(iid, result);
1955 :
1956 0 : return NS_ERROR_FAILURE;
1957 : }
1958 :
1959 : // nsIChannelEventSink
1960 :
1961 : NS_IMETHODIMP
1962 0 : WebSocketChannel::AsyncOnChannelRedirect(
1963 : nsIChannel *oldChannel,
1964 : nsIChannel *newChannel,
1965 : PRUint32 flags,
1966 : nsIAsyncVerifyRedirectCallback *callback)
1967 : {
1968 0 : LOG(("WebSocketChannel::AsyncOnChannelRedirect() %p\n", this));
1969 : nsresult rv;
1970 :
1971 0 : nsCOMPtr<nsIURI> newuri;
1972 0 : rv = newChannel->GetURI(getter_AddRefs(newuri));
1973 0 : NS_ENSURE_SUCCESS(rv, rv);
1974 :
1975 : // newuri is expected to be http or https
1976 0 : bool newuriIsHttps = false;
1977 0 : rv = newuri->SchemeIs("https", &newuriIsHttps);
1978 0 : NS_ENSURE_SUCCESS(rv, rv);
1979 :
1980 0 : if (!mAutoFollowRedirects) {
1981 : // Even if redirects configured off, still allow them for HTTP Strict
1982 : // Transport Security (from ws://FOO to https://FOO (mapped to wss://FOO)
1983 :
1984 0 : nsCOMPtr<nsIURI> clonedNewURI;
1985 0 : rv = newuri->Clone(getter_AddRefs(clonedNewURI));
1986 0 : NS_ENSURE_SUCCESS(rv, rv);
1987 :
1988 0 : rv = clonedNewURI->SetScheme(NS_LITERAL_CSTRING("ws"));
1989 0 : NS_ENSURE_SUCCESS(rv, rv);
1990 :
1991 0 : nsCOMPtr<nsIURI> currentURI;
1992 0 : rv = GetURI(getter_AddRefs(currentURI));
1993 0 : NS_ENSURE_SUCCESS(rv, rv);
1994 :
1995 : // currentURI is expected to be ws or wss
1996 0 : bool currentIsHttps = false;
1997 0 : rv = currentURI->SchemeIs("wss", ¤tIsHttps);
1998 0 : NS_ENSURE_SUCCESS(rv, rv);
1999 :
2000 0 : bool uriEqual = false;
2001 0 : rv = clonedNewURI->Equals(currentURI, &uriEqual);
2002 0 : NS_ENSURE_SUCCESS(rv, rv);
2003 :
2004 : // It's only a HSTS redirect if we started with non-secure, are going to
2005 : // secure, and the new URI is otherwise the same as the old one.
2006 0 : if (!(!currentIsHttps && newuriIsHttps && uriEqual)) {
2007 0 : nsCAutoString newSpec;
2008 0 : rv = newuri->GetSpec(newSpec);
2009 0 : NS_ENSURE_SUCCESS(rv, rv);
2010 :
2011 0 : LOG(("WebSocketChannel: Redirect to %s denied by configuration\n",
2012 : newSpec.get()));
2013 0 : return NS_ERROR_FAILURE;
2014 : }
2015 : }
2016 :
2017 0 : if (mEncrypted && !newuriIsHttps) {
2018 0 : nsCAutoString spec;
2019 0 : if (NS_SUCCEEDED(newuri->GetSpec(spec)))
2020 0 : LOG(("WebSocketChannel: Redirect to %s violates encryption rule\n",
2021 : spec.get()));
2022 0 : return NS_ERROR_FAILURE;
2023 : }
2024 :
2025 0 : nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel, &rv);
2026 0 : if (NS_FAILED(rv)) {
2027 0 : LOG(("WebSocketChannel: Redirect could not QI to HTTP\n"));
2028 0 : return rv;
2029 : }
2030 :
2031 : nsCOMPtr<nsIHttpChannelInternal> newUpgradeChannel =
2032 0 : do_QueryInterface(newChannel, &rv);
2033 :
2034 0 : if (NS_FAILED(rv)) {
2035 0 : LOG(("WebSocketChannel: Redirect could not QI to HTTP Upgrade\n"));
2036 0 : return rv;
2037 : }
2038 :
2039 : // The redirect is likely OK
2040 :
2041 0 : newChannel->SetNotificationCallbacks(this);
2042 :
2043 0 : mEncrypted = newuriIsHttps;
2044 0 : newuri->Clone(getter_AddRefs(mURI));
2045 0 : if (mEncrypted)
2046 0 : rv = mURI->SetScheme(NS_LITERAL_CSTRING("wss"));
2047 : else
2048 0 : rv = mURI->SetScheme(NS_LITERAL_CSTRING("ws"));
2049 :
2050 0 : mHttpChannel = newHttpChannel;
2051 0 : mChannel = newUpgradeChannel;
2052 0 : rv = SetupRequest();
2053 0 : if (NS_FAILED(rv)) {
2054 0 : LOG(("WebSocketChannel: Redirect could not SetupRequest()\n"));
2055 0 : return rv;
2056 : }
2057 :
2058 : // We cannot just tell the callback OK right now due to the 1 connect at a
2059 : // time policy. First we need to complete the old location and then start the
2060 : // lookup chain for the new location - once that is complete and we have been
2061 : // admitted, OnRedirectVerifyCallback(NS_OK) will be called out of BeginOpen()
2062 :
2063 0 : sWebSocketAdmissions->Complete(this);
2064 0 : mAddress.Truncate();
2065 0 : mRedirectCallback = callback;
2066 :
2067 0 : mChannelWasOpened = 0;
2068 :
2069 0 : rv = ApplyForAdmission();
2070 0 : if (NS_FAILED(rv)) {
2071 0 : LOG(("WebSocketChannel: Redirect failed due to DNS failure\n"));
2072 0 : mRedirectCallback = nsnull;
2073 0 : return rv;
2074 : }
2075 :
2076 0 : return NS_OK;
2077 : }
2078 :
2079 : // nsITimerCallback
2080 :
2081 : NS_IMETHODIMP
2082 0 : WebSocketChannel::Notify(nsITimer *timer)
2083 : {
2084 0 : LOG(("WebSocketChannel::Notify() %p [%p]\n", this, timer));
2085 :
2086 0 : if (timer == mCloseTimer) {
2087 0 : NS_ABORT_IF_FALSE(mClientClosed, "Close Timeout without local close");
2088 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
2089 : "not socket thread");
2090 :
2091 0 : mCloseTimer = nsnull;
2092 0 : if (mStopped || mServerClosed) /* no longer relevant */
2093 0 : return NS_OK;
2094 :
2095 0 : LOG(("WebSocketChannel:: Expecting Server Close - Timed Out\n"));
2096 0 : AbortSession(NS_ERROR_NET_TIMEOUT);
2097 0 : } else if (timer == mOpenTimer) {
2098 0 : NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest,
2099 : "Open Timer after open complete");
2100 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2101 :
2102 0 : mOpenTimer = nsnull;
2103 0 : LOG(("WebSocketChannel:: Connection Timed Out\n"));
2104 0 : if (mStopped || mServerClosed) /* no longer relevant */
2105 0 : return NS_OK;
2106 :
2107 0 : AbortSession(NS_ERROR_NET_TIMEOUT);
2108 0 : } else if (timer == mPingTimer) {
2109 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
2110 : "not socket thread");
2111 :
2112 0 : if (mClientClosed || mServerClosed || mRequestedClose) {
2113 : // no point in worrying about ping now
2114 0 : mPingTimer = nsnull;
2115 0 : return NS_OK;
2116 : }
2117 :
2118 0 : if (!mPingOutstanding) {
2119 0 : LOG(("nsWebSocketChannel:: Generating Ping\n"));
2120 0 : mPingOutstanding = 1;
2121 0 : GeneratePing();
2122 0 : mPingTimer->InitWithCallback(this, mPingResponseTimeout,
2123 0 : nsITimer::TYPE_ONE_SHOT);
2124 : } else {
2125 0 : LOG(("nsWebSocketChannel:: Timed out Ping\n"));
2126 0 : mPingTimer = nsnull;
2127 0 : AbortSession(NS_ERROR_NET_TIMEOUT);
2128 : }
2129 0 : } else if (timer == mLingeringCloseTimer) {
2130 0 : LOG(("WebSocketChannel:: Lingering Close Timer"));
2131 0 : CleanupConnection();
2132 : } else {
2133 0 : NS_ABORT_IF_FALSE(0, "Unknown Timer");
2134 : }
2135 :
2136 0 : return NS_OK;
2137 : }
2138 :
2139 :
2140 : NS_IMETHODIMP
2141 0 : WebSocketChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
2142 : {
2143 0 : LOG(("WebSocketChannel::GetSecurityInfo() %p\n", this));
2144 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2145 :
2146 0 : if (mTransport) {
2147 0 : if (NS_FAILED(mTransport->GetSecurityInfo(aSecurityInfo)))
2148 0 : *aSecurityInfo = nsnull;
2149 : }
2150 0 : return NS_OK;
2151 : }
2152 :
2153 :
2154 : NS_IMETHODIMP
2155 0 : WebSocketChannel::AsyncOpen(nsIURI *aURI,
2156 : const nsACString &aOrigin,
2157 : nsIWebSocketListener *aListener,
2158 : nsISupports *aContext)
2159 : {
2160 0 : LOG(("WebSocketChannel::AsyncOpen() %p\n", this));
2161 :
2162 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2163 :
2164 0 : if (!aURI || !aListener) {
2165 0 : LOG(("WebSocketChannel::AsyncOpen() Uri or Listener null"));
2166 0 : return NS_ERROR_UNEXPECTED;
2167 : }
2168 :
2169 0 : if (mListener)
2170 0 : return NS_ERROR_ALREADY_OPENED;
2171 :
2172 : nsresult rv;
2173 :
2174 0 : mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
2175 0 : if (NS_FAILED(rv)) {
2176 0 : NS_WARNING("unable to continue without socket transport service");
2177 0 : return rv;
2178 : }
2179 :
2180 : mRandomGenerator =
2181 0 : do_GetService("@mozilla.org/security/random-generator;1", &rv);
2182 0 : if (NS_FAILED(rv)) {
2183 0 : NS_WARNING("unable to continue without random number generator");
2184 0 : return rv;
2185 : }
2186 :
2187 0 : nsCOMPtr<nsIPrefBranch> prefService;
2188 0 : prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
2189 :
2190 0 : if (prefService) {
2191 : PRInt32 intpref;
2192 : bool boolpref;
2193 0 : rv = prefService->GetIntPref("network.websocket.max-message-size",
2194 0 : &intpref);
2195 0 : if (NS_SUCCEEDED(rv)) {
2196 0 : mMaxMessageSize = clamped(intpref, 1024, PR_INT32_MAX);
2197 : }
2198 0 : rv = prefService->GetIntPref("network.websocket.timeout.close", &intpref);
2199 0 : if (NS_SUCCEEDED(rv)) {
2200 0 : mCloseTimeout = clamped(intpref, 1, 1800) * 1000;
2201 : }
2202 0 : rv = prefService->GetIntPref("network.websocket.timeout.open", &intpref);
2203 0 : if (NS_SUCCEEDED(rv)) {
2204 0 : mOpenTimeout = clamped(intpref, 1, 1800) * 1000;
2205 : }
2206 0 : rv = prefService->GetIntPref("network.websocket.timeout.ping.request",
2207 0 : &intpref);
2208 0 : if (NS_SUCCEEDED(rv)) {
2209 0 : mPingTimeout = clamped(intpref, 0, 86400) * 1000;
2210 : }
2211 0 : rv = prefService->GetIntPref("network.websocket.timeout.ping.response",
2212 0 : &intpref);
2213 0 : if (NS_SUCCEEDED(rv)) {
2214 0 : mPingResponseTimeout = clamped(intpref, 1, 3600) * 1000;
2215 : }
2216 0 : rv = prefService->GetBoolPref("network.websocket.extensions.stream-deflate",
2217 0 : &boolpref);
2218 0 : if (NS_SUCCEEDED(rv)) {
2219 0 : mAllowCompression = boolpref ? 1 : 0;
2220 : }
2221 0 : rv = prefService->GetBoolPref("network.websocket.auto-follow-http-redirects",
2222 0 : &boolpref);
2223 0 : if (NS_SUCCEEDED(rv)) {
2224 0 : mAutoFollowRedirects = boolpref ? 1 : 0;
2225 : }
2226 0 : rv = prefService->GetIntPref
2227 0 : ("network.websocket.max-connections", &intpref);
2228 0 : if (NS_SUCCEEDED(rv)) {
2229 0 : mMaxConcurrentConnections = clamped(intpref, 1, 0xffff);
2230 : }
2231 : }
2232 :
2233 0 : if (sWebSocketAdmissions)
2234 0 : LOG(("WebSocketChannel::AsyncOpen %p sessionCount=%d max=%d\n", this,
2235 : sWebSocketAdmissions->SessionCount(), mMaxConcurrentConnections));
2236 :
2237 0 : if (sWebSocketAdmissions &&
2238 0 : sWebSocketAdmissions->SessionCount() >= mMaxConcurrentConnections)
2239 : {
2240 0 : LOG(("WebSocketChannel: max concurrency %d exceeded (%d)",
2241 : mMaxConcurrentConnections,
2242 : sWebSocketAdmissions->SessionCount()));
2243 :
2244 : // WebSocket connections are expected to be long lived, so return
2245 : // an error here instead of queueing
2246 0 : return NS_ERROR_SOCKET_CREATE_FAILED;
2247 : }
2248 :
2249 0 : if (mPingTimeout) {
2250 0 : mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
2251 0 : if (NS_FAILED(rv)) {
2252 0 : NS_WARNING("unable to create ping timer. Carrying on.");
2253 : } else {
2254 0 : LOG(("WebSocketChannel will generate ping after %d ms of receive silence\n",
2255 : mPingTimeout));
2256 0 : mPingTimer->SetTarget(mSocketThread);
2257 0 : mPingTimer->InitWithCallback(this, mPingTimeout, nsITimer::TYPE_ONE_SHOT);
2258 : }
2259 : }
2260 :
2261 0 : mOriginalURI = aURI;
2262 0 : mURI = mOriginalURI;
2263 0 : mListener = aListener;
2264 0 : mContext = aContext;
2265 0 : mOrigin = aOrigin;
2266 :
2267 0 : nsCOMPtr<nsIURI> localURI;
2268 0 : nsCOMPtr<nsIChannel> localChannel;
2269 :
2270 0 : mURI->Clone(getter_AddRefs(localURI));
2271 0 : if (mEncrypted)
2272 0 : rv = localURI->SetScheme(NS_LITERAL_CSTRING("https"));
2273 : else
2274 0 : rv = localURI->SetScheme(NS_LITERAL_CSTRING("http"));
2275 0 : NS_ENSURE_SUCCESS(rv, rv);
2276 :
2277 0 : nsCOMPtr<nsIIOService> ioService;
2278 0 : ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
2279 0 : if (NS_FAILED(rv)) {
2280 0 : NS_WARNING("unable to continue without io service");
2281 0 : return rv;
2282 : }
2283 :
2284 0 : nsCOMPtr<nsIIOService2> io2 = do_QueryInterface(ioService, &rv);
2285 0 : if (NS_FAILED(rv)) {
2286 0 : NS_WARNING("WebSocketChannel: unable to continue without ioservice2");
2287 0 : return rv;
2288 : }
2289 :
2290 0 : rv = io2->NewChannelFromURIWithProxyFlags(
2291 : localURI,
2292 : mURI,
2293 : nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY |
2294 : nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
2295 : nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
2296 0 : getter_AddRefs(localChannel));
2297 0 : NS_ENSURE_SUCCESS(rv, rv);
2298 :
2299 : // Pass most GetInterface() requests through to our instantiator, but handle
2300 : // nsIChannelEventSink in this object in order to deal with redirects
2301 0 : localChannel->SetNotificationCallbacks(this);
2302 :
2303 0 : mChannel = do_QueryInterface(localChannel, &rv);
2304 0 : NS_ENSURE_SUCCESS(rv, rv);
2305 :
2306 0 : mHttpChannel = do_QueryInterface(localChannel, &rv);
2307 0 : NS_ENSURE_SUCCESS(rv, rv);
2308 :
2309 0 : rv = SetupRequest();
2310 0 : if (NS_FAILED(rv))
2311 0 : return rv;
2312 :
2313 0 : return ApplyForAdmission();
2314 : }
2315 :
2316 : NS_IMETHODIMP
2317 0 : WebSocketChannel::Close(PRUint16 code, const nsACString & reason)
2318 : {
2319 0 : LOG(("WebSocketChannel::Close() %p\n", this));
2320 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2321 :
2322 0 : if (mRequestedClose) {
2323 0 : return NS_OK;
2324 : }
2325 :
2326 0 : if (!mTransport) {
2327 0 : LOG(("WebSocketChannel::Close() without transport - aborting."));
2328 0 : AbortSession(NS_ERROR_NOT_CONNECTED);
2329 0 : return NS_ERROR_NOT_CONNECTED;
2330 : }
2331 :
2332 : // The API requires the UTF-8 string to be 123 or less bytes
2333 0 : if (reason.Length() > 123)
2334 0 : return NS_ERROR_ILLEGAL_VALUE;
2335 :
2336 0 : mRequestedClose = 1;
2337 0 : mScriptCloseReason = reason;
2338 0 : mScriptCloseCode = code;
2339 :
2340 0 : return mSocketThread->Dispatch(
2341 0 : new OutboundEnqueuer(this, new OutboundMessage(kMsgTypeFin, nsnull)),
2342 0 : nsIEventTarget::DISPATCH_NORMAL);
2343 : }
2344 :
2345 : NS_IMETHODIMP
2346 0 : WebSocketChannel::SendMsg(const nsACString &aMsg)
2347 : {
2348 0 : LOG(("WebSocketChannel::SendMsg() %p\n", this));
2349 :
2350 0 : return SendMsgCommon(&aMsg, false, aMsg.Length());
2351 : }
2352 :
2353 : NS_IMETHODIMP
2354 0 : WebSocketChannel::SendBinaryMsg(const nsACString &aMsg)
2355 : {
2356 0 : LOG(("WebSocketChannel::SendBinaryMsg() %p len=%d\n", this, aMsg.Length()));
2357 0 : return SendMsgCommon(&aMsg, true, aMsg.Length());
2358 : }
2359 :
2360 : NS_IMETHODIMP
2361 0 : WebSocketChannel::SendBinaryStream(nsIInputStream *aStream, PRUint32 aLength)
2362 : {
2363 0 : LOG(("WebSocketChannel::SendBinaryStream() %p\n", this));
2364 :
2365 0 : return SendMsgCommon(nsnull, true, aLength, aStream);
2366 : }
2367 :
2368 : nsresult
2369 0 : WebSocketChannel::SendMsgCommon(const nsACString *aMsg, bool aIsBinary,
2370 : PRUint32 aLength, nsIInputStream *aStream)
2371 : {
2372 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2373 :
2374 0 : if (mRequestedClose) {
2375 0 : LOG(("WebSocketChannel:: Error: send when closed\n"));
2376 0 : return NS_ERROR_UNEXPECTED;
2377 : }
2378 :
2379 0 : if (mStopped) {
2380 0 : LOG(("WebSocketChannel:: Error: send when stopped\n"));
2381 0 : return NS_ERROR_NOT_CONNECTED;
2382 : }
2383 :
2384 0 : NS_ABORT_IF_FALSE(mMaxMessageSize >= 0, "max message size negative");
2385 0 : if (aLength > static_cast<PRUint32>(mMaxMessageSize)) {
2386 0 : LOG(("WebSocketChannel:: Error: message too big\n"));
2387 0 : return NS_ERROR_FILE_TOO_BIG;
2388 : }
2389 :
2390 0 : return mSocketThread->Dispatch(
2391 0 : aStream ? new OutboundEnqueuer(this, new OutboundMessage(aStream, aLength))
2392 : : new OutboundEnqueuer(this,
2393 : new OutboundMessage(aIsBinary ? kMsgTypeBinaryString
2394 : : kMsgTypeString,
2395 0 : new nsCString(*aMsg))),
2396 0 : nsIEventTarget::DISPATCH_NORMAL);
2397 : }
2398 :
2399 : NS_IMETHODIMP
2400 0 : WebSocketChannel::OnTransportAvailable(nsISocketTransport *aTransport,
2401 : nsIAsyncInputStream *aSocketIn,
2402 : nsIAsyncOutputStream *aSocketOut)
2403 : {
2404 0 : LOG(("WebSocketChannel::OnTransportAvailable %p [%p %p %p] rcvdonstart=%d\n",
2405 : this, aTransport, aSocketIn, aSocketOut, mRecvdHttpOnStartRequest));
2406 :
2407 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2408 0 : NS_ABORT_IF_FALSE(!mRecvdHttpUpgradeTransport, "OTA duplicated");
2409 0 : NS_ABORT_IF_FALSE(aSocketIn, "OTA with invalid socketIn");
2410 :
2411 0 : mTransport = aTransport;
2412 0 : mSocketIn = aSocketIn;
2413 0 : mSocketOut = aSocketOut;
2414 :
2415 : nsresult rv;
2416 0 : rv = mTransport->SetEventSink(nsnull, nsnull);
2417 0 : if (NS_FAILED(rv)) return rv;
2418 0 : rv = mTransport->SetSecurityCallbacks(this);
2419 0 : if (NS_FAILED(rv)) return rv;
2420 :
2421 0 : mRecvdHttpUpgradeTransport = 1;
2422 0 : if (mRecvdHttpOnStartRequest)
2423 0 : return StartWebsocketData();
2424 0 : return NS_OK;
2425 : }
2426 :
2427 : // nsIRequestObserver (from nsIStreamListener)
2428 :
2429 : NS_IMETHODIMP
2430 0 : WebSocketChannel::OnStartRequest(nsIRequest *aRequest,
2431 : nsISupports *aContext)
2432 : {
2433 0 : LOG(("WebSocketChannel::OnStartRequest(): %p [%p %p] recvdhttpupgrade=%d\n",
2434 : this, aRequest, aContext, mRecvdHttpUpgradeTransport));
2435 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2436 0 : NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest, "OTA duplicated");
2437 :
2438 : // Generating the onStart event will take us out of the
2439 : // CONNECTING state which means we can now open another,
2440 : // perhaps parallel, connection to the same host if one
2441 : // is pending
2442 :
2443 0 : if (sWebSocketAdmissions->Complete(this))
2444 0 : LOG(("WebSocketChannel::OnStartRequest: Starting Pending Open\n"));
2445 : else
2446 0 : LOG(("WebSocketChannel::OnStartRequest: No More Pending Opens\n"));
2447 :
2448 0 : if (mOpenTimer) {
2449 0 : mOpenTimer->Cancel();
2450 0 : mOpenTimer = nsnull;
2451 : }
2452 :
2453 0 : if (mStopped) {
2454 0 : LOG(("WebSocketChannel::OnStartRequest: Channel Already Done\n"));
2455 0 : AbortSession(NS_ERROR_CONNECTION_REFUSED);
2456 0 : return NS_ERROR_CONNECTION_REFUSED;
2457 : }
2458 :
2459 : nsresult rv;
2460 : PRUint32 status;
2461 : char *val, *token;
2462 :
2463 0 : rv = mHttpChannel->GetResponseStatus(&status);
2464 0 : if (NS_FAILED(rv)) {
2465 0 : LOG(("WebSocketChannel::OnStartRequest: No HTTP Response\n"));
2466 0 : AbortSession(NS_ERROR_CONNECTION_REFUSED);
2467 0 : return NS_ERROR_CONNECTION_REFUSED;
2468 : }
2469 :
2470 0 : LOG(("WebSocketChannel::OnStartRequest: HTTP status %d\n", status));
2471 0 : if (status != 101) {
2472 0 : AbortSession(NS_ERROR_CONNECTION_REFUSED);
2473 0 : return NS_ERROR_CONNECTION_REFUSED;
2474 : }
2475 :
2476 0 : nsCAutoString respUpgrade;
2477 0 : rv = mHttpChannel->GetResponseHeader(
2478 0 : NS_LITERAL_CSTRING("Upgrade"), respUpgrade);
2479 :
2480 0 : if (NS_SUCCEEDED(rv)) {
2481 0 : rv = NS_ERROR_ILLEGAL_VALUE;
2482 0 : if (!respUpgrade.IsEmpty()) {
2483 0 : val = respUpgrade.BeginWriting();
2484 0 : while ((token = nsCRT::strtok(val, ", \t", &val))) {
2485 0 : if (PL_strcasecmp(token, "Websocket") == 0) {
2486 0 : rv = NS_OK;
2487 0 : break;
2488 : }
2489 : }
2490 : }
2491 : }
2492 :
2493 0 : if (NS_FAILED(rv)) {
2494 0 : LOG(("WebSocketChannel::OnStartRequest: "
2495 : "HTTP response header Upgrade: websocket not found\n"));
2496 0 : AbortSession(rv);
2497 0 : return rv;
2498 : }
2499 :
2500 0 : nsCAutoString respConnection;
2501 0 : rv = mHttpChannel->GetResponseHeader(
2502 0 : NS_LITERAL_CSTRING("Connection"), respConnection);
2503 :
2504 0 : if (NS_SUCCEEDED(rv)) {
2505 0 : rv = NS_ERROR_ILLEGAL_VALUE;
2506 0 : if (!respConnection.IsEmpty()) {
2507 0 : val = respConnection.BeginWriting();
2508 0 : while ((token = nsCRT::strtok(val, ", \t", &val))) {
2509 0 : if (PL_strcasecmp(token, "Upgrade") == 0) {
2510 0 : rv = NS_OK;
2511 0 : break;
2512 : }
2513 : }
2514 : }
2515 : }
2516 :
2517 0 : if (NS_FAILED(rv)) {
2518 0 : LOG(("WebSocketChannel::OnStartRequest: "
2519 : "HTTP response header 'Connection: Upgrade' not found\n"));
2520 0 : AbortSession(rv);
2521 0 : return rv;
2522 : }
2523 :
2524 0 : nsCAutoString respAccept;
2525 0 : rv = mHttpChannel->GetResponseHeader(
2526 0 : NS_LITERAL_CSTRING("Sec-WebSocket-Accept"),
2527 0 : respAccept);
2528 :
2529 0 : if (NS_FAILED(rv) ||
2530 0 : respAccept.IsEmpty() || !respAccept.Equals(mHashedSecret)) {
2531 0 : LOG(("WebSocketChannel::OnStartRequest: "
2532 : "HTTP response header Sec-WebSocket-Accept check failed\n"));
2533 0 : LOG(("WebSocketChannel::OnStartRequest: Expected %s recevied %s\n",
2534 : mHashedSecret.get(), respAccept.get()));
2535 0 : AbortSession(NS_ERROR_ILLEGAL_VALUE);
2536 0 : return NS_ERROR_ILLEGAL_VALUE;
2537 : }
2538 :
2539 : // If we sent a sub protocol header, verify the response matches
2540 : // If it does not, set mProtocol to "" so the protocol attribute
2541 : // of the WebSocket JS object reflects that
2542 0 : if (!mProtocol.IsEmpty()) {
2543 0 : nsCAutoString respProtocol;
2544 0 : rv = mHttpChannel->GetResponseHeader(
2545 0 : NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
2546 0 : respProtocol);
2547 0 : if (NS_SUCCEEDED(rv)) {
2548 0 : rv = NS_ERROR_ILLEGAL_VALUE;
2549 0 : val = mProtocol.BeginWriting();
2550 0 : while ((token = nsCRT::strtok(val, ", \t", &val))) {
2551 0 : if (PL_strcasecmp(token, respProtocol.get()) == 0) {
2552 0 : rv = NS_OK;
2553 0 : break;
2554 : }
2555 : }
2556 :
2557 0 : if (NS_SUCCEEDED(rv)) {
2558 0 : LOG(("WebsocketChannel::OnStartRequest: subprotocol %s confirmed",
2559 : respProtocol.get()));
2560 0 : mProtocol = respProtocol;
2561 : } else {
2562 0 : LOG(("WebsocketChannel::OnStartRequest: "
2563 : "subprotocol [%s] not found - %s returned",
2564 : mProtocol.get(), respProtocol.get()));
2565 0 : mProtocol.Truncate();
2566 : }
2567 : } else {
2568 0 : LOG(("WebsocketChannel::OnStartRequest "
2569 : "subprotocol [%s] not found - none returned",
2570 : mProtocol.get()));
2571 0 : mProtocol.Truncate();
2572 : }
2573 : }
2574 :
2575 0 : rv = HandleExtensions();
2576 0 : if (NS_FAILED(rv))
2577 0 : return rv;
2578 :
2579 0 : LOG(("WebSocketChannel::OnStartRequest: Notifying Listener %p\n",
2580 : mListener.get()));
2581 :
2582 0 : if (mListener)
2583 0 : mListener->OnStart(mContext);
2584 :
2585 0 : mRecvdHttpOnStartRequest = 1;
2586 0 : if (mRecvdHttpUpgradeTransport)
2587 0 : return StartWebsocketData();
2588 :
2589 0 : return NS_OK;
2590 : }
2591 :
2592 : NS_IMETHODIMP
2593 0 : WebSocketChannel::OnStopRequest(nsIRequest *aRequest,
2594 : nsISupports *aContext,
2595 : nsresult aStatusCode)
2596 : {
2597 0 : LOG(("WebSocketChannel::OnStopRequest() %p [%p %p %x]\n",
2598 : this, aRequest, aContext, aStatusCode));
2599 0 : NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
2600 :
2601 : // This is the end of the HTTP upgrade transaction, the
2602 : // upgraded streams live on
2603 :
2604 0 : mChannel = nsnull;
2605 0 : mHttpChannel = nsnull;
2606 0 : mLoadGroup = nsnull;
2607 0 : mCallbacks = nsnull;
2608 :
2609 0 : return NS_OK;
2610 : }
2611 :
2612 : // nsIInputStreamCallback
2613 :
2614 : NS_IMETHODIMP
2615 0 : WebSocketChannel::OnInputStreamReady(nsIAsyncInputStream *aStream)
2616 : {
2617 0 : LOG(("WebSocketChannel::OnInputStreamReady() %p\n", this));
2618 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
2619 :
2620 0 : if (!mSocketIn) // did we we clean up the socket after scheduling InputReady?
2621 0 : return NS_OK;
2622 :
2623 0 : nsRefPtr<nsIStreamListener> deleteProtector1(mInflateReader);
2624 0 : nsRefPtr<nsIStringInputStream> deleteProtector2(mInflateStream);
2625 :
2626 : // this is after the http upgrade - so we are speaking websockets
2627 : char buffer[2048];
2628 : PRUint32 count;
2629 : nsresult rv;
2630 :
2631 0 : do {
2632 0 : rv = mSocketIn->Read((char *)buffer, 2048, &count);
2633 0 : LOG(("WebSocketChannel::OnInputStreamReady: read %u rv %x\n", count, rv));
2634 :
2635 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2636 0 : mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
2637 0 : return NS_OK;
2638 : }
2639 :
2640 0 : if (NS_FAILED(rv)) {
2641 0 : mTCPClosed = true;
2642 0 : AbortSession(rv);
2643 0 : return rv;
2644 : }
2645 :
2646 0 : if (count == 0) {
2647 0 : mTCPClosed = true;
2648 0 : AbortSession(NS_BASE_STREAM_CLOSED);
2649 0 : return NS_OK;
2650 : }
2651 :
2652 0 : if (mStopped) {
2653 0 : NS_ABORT_IF_FALSE(mLingeringCloseTimer,
2654 : "OnInputReady after stop without linger");
2655 0 : continue;
2656 : }
2657 :
2658 0 : if (mInflateReader) {
2659 0 : mInflateStream->ShareData(buffer, count);
2660 0 : rv = mInflateReader->OnDataAvailable(nsnull, mSocketIn, mInflateStream,
2661 0 : 0, count);
2662 : } else {
2663 0 : rv = ProcessInput((PRUint8 *)buffer, count);
2664 : }
2665 :
2666 0 : if (NS_FAILED(rv)) {
2667 0 : AbortSession(rv);
2668 0 : return rv;
2669 : }
2670 0 : } while (NS_SUCCEEDED(rv) && mSocketIn);
2671 :
2672 0 : return NS_OK;
2673 : }
2674 :
2675 :
2676 : // nsIOutputStreamCallback
2677 :
2678 : NS_IMETHODIMP
2679 0 : WebSocketChannel::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
2680 : {
2681 0 : LOG(("WebSocketChannel::OnOutputStreamReady() %p\n", this));
2682 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "not socket thread");
2683 : nsresult rv;
2684 :
2685 0 : if (!mCurrentOut)
2686 0 : PrimeNewOutgoingMessage();
2687 :
2688 0 : while (mCurrentOut && mSocketOut) {
2689 : const char *sndBuf;
2690 : PRUint32 toSend;
2691 : PRUint32 amtSent;
2692 :
2693 0 : if (mHdrOut) {
2694 0 : sndBuf = (const char *)mHdrOut;
2695 0 : toSend = mHdrOutToSend;
2696 0 : LOG(("WebSocketChannel::OnOutputStreamReady: "
2697 : "Try to send %u of hdr/copybreak\n", toSend));
2698 : } else {
2699 0 : sndBuf = (char *) mCurrentOut->BeginReading() + mCurrentOutSent;
2700 0 : toSend = mCurrentOut->Length() - mCurrentOutSent;
2701 0 : if (toSend > 0) {
2702 0 : LOG(("WebSocketChannel::OnOutputStreamReady: "
2703 : "Try to send %u of data\n", toSend));
2704 : }
2705 : }
2706 :
2707 0 : if (toSend == 0) {
2708 0 : amtSent = 0;
2709 : } else {
2710 0 : rv = mSocketOut->Write(sndBuf, toSend, &amtSent);
2711 0 : LOG(("WebSocketChannel::OnOutputStreamReady: write %u rv %x\n",
2712 : amtSent, rv));
2713 :
2714 0 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2715 0 : mSocketOut->AsyncWait(this, 0, 0, nsnull);
2716 0 : return NS_OK;
2717 : }
2718 :
2719 0 : if (NS_FAILED(rv)) {
2720 0 : AbortSession(rv);
2721 0 : return NS_OK;
2722 : }
2723 : }
2724 :
2725 0 : if (mHdrOut) {
2726 0 : if (amtSent == toSend) {
2727 0 : mHdrOut = nsnull;
2728 0 : mHdrOutToSend = 0;
2729 : } else {
2730 0 : mHdrOut += amtSent;
2731 0 : mHdrOutToSend -= amtSent;
2732 : }
2733 : } else {
2734 0 : if (amtSent == toSend) {
2735 0 : if (!mStopped) {
2736 : NS_DispatchToMainThread(new CallAcknowledge(this,
2737 0 : mCurrentOut->Length()));
2738 : }
2739 0 : DeleteCurrentOutGoingMessage();
2740 0 : PrimeNewOutgoingMessage();
2741 : } else {
2742 0 : mCurrentOutSent += amtSent;
2743 : }
2744 : }
2745 : }
2746 :
2747 0 : if (mReleaseOnTransmit)
2748 0 : ReleaseSession();
2749 0 : return NS_OK;
2750 : }
2751 :
2752 : // nsIStreamListener
2753 :
2754 : NS_IMETHODIMP
2755 0 : WebSocketChannel::OnDataAvailable(nsIRequest *aRequest,
2756 : nsISupports *aContext,
2757 : nsIInputStream *aInputStream,
2758 : PRUint32 aOffset,
2759 : PRUint32 aCount)
2760 : {
2761 0 : LOG(("WebSocketChannel::OnDataAvailable() %p [%p %p %p %u %u]\n",
2762 : this, aRequest, aContext, aInputStream, aOffset, aCount));
2763 :
2764 0 : if (aContext == mSocketIn) {
2765 : // This is the deflate decoder
2766 :
2767 0 : LOG(("WebSocketChannel::OnDataAvailable: Deflate Data %u\n",
2768 : aCount));
2769 :
2770 : PRUint8 buffer[2048];
2771 : PRUint32 maxRead;
2772 : PRUint32 count;
2773 : nsresult rv;
2774 :
2775 0 : while (aCount > 0) {
2776 0 : if (mStopped)
2777 0 : return NS_BASE_STREAM_CLOSED;
2778 :
2779 0 : maxRead = NS_MIN(2048U, aCount);
2780 0 : rv = aInputStream->Read((char *)buffer, maxRead, &count);
2781 0 : LOG(("WebSocketChannel::OnDataAvailable: InflateRead read %u rv %x\n",
2782 : count, rv));
2783 0 : if (NS_FAILED(rv) || count == 0) {
2784 0 : AbortSession(rv);
2785 0 : break;
2786 : }
2787 :
2788 0 : aCount -= count;
2789 0 : rv = ProcessInput(buffer, count);
2790 : }
2791 0 : return NS_OK;
2792 : }
2793 :
2794 0 : if (aContext == mSocketOut) {
2795 : // This is the deflate encoder
2796 :
2797 : PRUint32 maxRead;
2798 : PRUint32 count;
2799 : nsresult rv;
2800 :
2801 0 : while (aCount > 0) {
2802 0 : if (mStopped)
2803 0 : return NS_BASE_STREAM_CLOSED;
2804 :
2805 0 : maxRead = NS_MIN(2048U, aCount);
2806 0 : EnsureHdrOut(mHdrOutToSend + aCount);
2807 0 : rv = aInputStream->Read((char *)mHdrOut + mHdrOutToSend, maxRead, &count);
2808 0 : LOG(("WebSocketChannel::OnDataAvailable: DeflateWrite read %u rv %x\n",
2809 : count, rv));
2810 0 : if (NS_FAILED(rv) || count == 0) {
2811 0 : AbortSession(rv);
2812 0 : break;
2813 : }
2814 :
2815 0 : mHdrOutToSend += count;
2816 0 : aCount -= count;
2817 : }
2818 0 : return NS_OK;
2819 : }
2820 :
2821 :
2822 : // Otherwise, this is the HTTP OnDataAvailable Method, which means
2823 : // this is http data in response to the upgrade request and
2824 : // there should be no http response body if the upgrade succeeded
2825 :
2826 : // This generally should be caught by a non 101 response code in
2827 : // OnStartRequest().. so we can ignore the data here
2828 :
2829 0 : LOG(("WebSocketChannel::OnDataAvailable: HTTP data unexpected len>=%u\n",
2830 : aCount));
2831 :
2832 0 : return NS_OK;
2833 : }
2834 :
2835 : } // namespace mozilla::net
2836 : } // namespace mozilla
|