75
75
using namespace mozilla ;
76
76
using namespace mozilla ::net;
77
77
78
- namespace mozilla {
79
- namespace net {
78
+ namespace mozilla ::net {
80
79
81
80
NS_IMPL_ISUPPORTS (WebSocketChannel, nsIWebSocketChannel, nsIHttpUpgradeListener,
82
81
nsIRequestObserver, nsIStreamListener, nsIProtocolHandler,
@@ -121,11 +120,11 @@ const uint32_t kWSReconnectBaseLifeTime = 60 * 1000;
121
120
const uint32_t kWSReconnectMaxDelay = 60 * 1000 ;
122
121
123
122
// hold record of failed connections, and calculates needed delay for reconnects
124
- // to same host/port.
123
+ // to same host/path/ port.
125
124
class FailDelay {
126
125
public:
127
- FailDelay (nsCString address, int32_t port)
128
- : mAddress (std::move(address)), mPort (port) {
126
+ FailDelay (nsCString address, nsCString path, int32_t port)
127
+ : mAddress (std::move(address)), mPath (std::move(path)), mPort (port) {
129
128
mLastFailure = TimeStamp::Now ();
130
129
mNextDelay = kWSReconnectInitialBaseDelay +
131
130
(rand () % kWSReconnectInitialRandomDelay );
@@ -139,9 +138,10 @@ class FailDelay {
139
138
mNextDelay = static_cast <uint32_t >(
140
139
std::min<double >(kWSReconnectMaxDelay , mNextDelay * 1.5 ));
141
140
LOG (
142
- (" WebSocket: FailedAgain: host=%s, port=%d: incremented delay to "
141
+ (" WebSocket: FailedAgain: host=%s, path=%s, port=%d: incremented delay "
142
+ " to "
143
143
" %" PRIu32,
144
- mAddress .get (), mPort , mNextDelay ));
144
+ mAddress .get (), mPath . get (), mPort , mNextDelay ));
145
145
}
146
146
147
147
// returns 0 if there is no need to delay (i.e. delay interval is over)
@@ -160,6 +160,7 @@ class FailDelay {
160
160
}
161
161
162
162
nsCString mAddress ; // IP address (or hostname if using proxy)
163
+ nsCString mPath ;
163
164
int32_t mPort ;
164
165
165
166
private:
@@ -191,16 +192,16 @@ class FailDelayManager {
191
192
192
193
~FailDelayManager () { MOZ_COUNT_DTOR (FailDelayManager); }
193
194
194
- void Add (nsCString& address, int32_t port) {
195
+ void Add (nsCString& address, nsCString& path, int32_t port) {
195
196
if (mDelaysDisabled ) return ;
196
197
197
- UniquePtr<FailDelay> record (new FailDelay (address, port));
198
+ UniquePtr<FailDelay> record (new FailDelay (address, path, port));
198
199
mEntries .AppendElement (std::move (record));
199
200
}
200
201
201
202
// Element returned may not be valid after next main thread event: don't keep
202
203
// pointer to it around
203
- FailDelay* Lookup (nsCString& address, int32_t port,
204
+ FailDelay* Lookup (nsCString& address, nsCString& path, int32_t port,
204
205
uint32_t * outIndex = nullptr ) {
205
206
if (mDelaysDisabled ) return nullptr ;
206
207
@@ -211,7 +212,8 @@ class FailDelayManager {
211
212
// indexing simpler
212
213
for (int32_t i = mEntries .Length () - 1 ; i >= 0 ; --i) {
213
214
FailDelay* fail = mEntries [i].get ();
214
- if (fail->mAddress .Equals (address) && fail->mPort == port) {
215
+ if (fail->mAddress .Equals (address) && fail->mPath .Equals (path) &&
216
+ fail->mPort == port) {
215
217
if (outIndex) *outIndex = i;
216
218
result = fail;
217
219
// break here: removing more entries would mess up *outIndex.
@@ -230,7 +232,7 @@ class FailDelayManager {
230
232
void DelayOrBegin (WebSocketChannel* ws) {
231
233
if (!mDelaysDisabled ) {
232
234
uint32_t failIndex = 0 ;
233
- FailDelay* fail = Lookup (ws->mAddress , ws->mPort , &failIndex);
235
+ FailDelay* fail = Lookup (ws->mAddress , ws->mPath , ws-> mPort , &failIndex);
234
236
235
237
if (fail) {
236
238
TimeStamp rightNow = TimeStamp::Now ();
@@ -266,13 +268,14 @@ class FailDelayManager {
266
268
267
269
// Remove() also deletes all expired entries as it iterates: better for
268
270
// battery life than using a periodic timer.
269
- void Remove (nsCString& address, int32_t port) {
271
+ void Remove (nsCString& address, nsCString& path, int32_t port) {
270
272
TimeStamp rightNow = TimeStamp::Now ();
271
273
272
274
// iterate from end, to make deletion indexing easier
273
275
for (int32_t i = mEntries .Length () - 1 ; i >= 0 ; --i) {
274
276
FailDelay* entry = mEntries [i].get ();
275
- if ((entry->mAddress .Equals (address) && entry->mPort == port) ||
277
+ if ((entry->mAddress .Equals (address) && entry->mPath .Equals (path) &&
278
+ entry->mPort == port) ||
276
279
entry->IsExpired (rightNow)) {
277
280
mEntries .RemoveElementAt (i);
278
281
}
@@ -321,14 +324,29 @@ class nsWSAdmissionManager {
321
324
322
325
// If there is already another WS channel connecting to this IP address,
323
326
// defer BeginOpen and mark as waiting in queue.
324
- bool found = (sManager ->IndexOf (ws->mAddress , ws->mOriginSuffix ) >= 0 );
327
+ bool hostFound = (sManager ->IndexOf (ws->mAddress , ws->mOriginSuffix ) >= 0 );
328
+
329
+ uint32_t failIndex = 0 ;
330
+ FailDelay* fail = sManager ->mFailures .Lookup (ws->mAddress , ws->mPath ,
331
+ ws->mPort , &failIndex);
332
+ bool existingFail = fail != nullptr ;
325
333
326
334
// Always add ourselves to queue, even if we'll connect immediately
327
335
UniquePtr<nsOpenConn> newdata (
328
- new nsOpenConn (ws->mAddress , ws->mOriginSuffix , ws));
329
- sManager ->mQueue .AppendElement (std::move (newdata));
336
+ new nsOpenConn (ws->mAddress , ws->mOriginSuffix , existingFail, ws));
330
337
331
- if (found) {
338
+ // If a connection has not previously failed then prioritize it over
339
+ // connections that have
340
+ if (existingFail) {
341
+ sManager ->mQueue .AppendElement (std::move (newdata));
342
+ } else {
343
+ uint32_t insertionIndex = sManager ->IndexOfFirstFailure ();
344
+ MOZ_ASSERT (insertionIndex <= sManager ->mQueue .Length (),
345
+ " Insertion index outside bounds" );
346
+ sManager ->mQueue .InsertElementAt (insertionIndex, std::move (newdata));
347
+ }
348
+
349
+ if (hostFound) {
332
350
LOG (
333
351
(" Websocket: some other channel is connecting, changing state to "
334
352
" CONNECTING_QUEUED" ));
@@ -357,7 +375,8 @@ class nsWSAdmissionManager {
357
375
sManager ->RemoveFromQueue (aChannel);
358
376
359
377
// Connection succeeded, so stop keeping track of any previous failures
360
- sManager ->mFailures .Remove (aChannel->mAddress , aChannel->mPort );
378
+ sManager ->mFailures .Remove (aChannel->mAddress , aChannel->mPath ,
379
+ aChannel->mPort );
361
380
362
381
// Check for queued connections to same host.
363
382
// Note: still need to check for failures, since next websocket with same
@@ -378,24 +397,27 @@ class nsWSAdmissionManager {
378
397
379
398
if (NS_FAILED(aReason)) {
380
399
// Have we seen this failure before?
381
- FailDelay* knownFailure =
382
- sManager -> mFailures . Lookup ( aChannel->mAddress , aChannel->mPort );
400
+ FailDelay* knownFailure = sManager -> mFailures . Lookup (
401
+ aChannel-> mAddress , aChannel->mPath , aChannel->mPort );
383
402
if (knownFailure) {
384
403
if (aReason == NS_ERROR_NOT_CONNECTED) {
385
404
// Don't count close() before connection as a network error
386
405
LOG (
387
- (" Websocket close() before connection to %s, %d completed"
406
+ (" Websocket close() before connection to %s, %s, % d completed"
388
407
" [this=%p]" ,
389
- aChannel->mAddress .get (), (int )aChannel->mPort , aChannel));
408
+ aChannel->mAddress .get (), aChannel->mPath .get (),
409
+ (int )aChannel->mPort , aChannel));
390
410
} else {
391
411
// repeated failure to connect: increase delay for next connection
392
412
knownFailure->FailedAgain ();
393
413
}
394
414
} else {
395
415
// new connection failure: record it.
396
- LOG ((" WebSocket: connection to %s, %d failed: [this=%p]" ,
397
- aChannel->mAddress .get (), (int )aChannel->mPort , aChannel));
398
- sManager ->mFailures .Add (aChannel->mAddress , aChannel->mPort );
416
+ LOG ((" WebSocket: connection to %s, %s, %d failed: [this=%p]" ,
417
+ aChannel->mAddress .get (), aChannel->mPath .get (),
418
+ (int )aChannel->mPort , aChannel));
419
+ sManager ->mFailures .Add (aChannel->mAddress , aChannel->mPath ,
420
+ aChannel->mPort );
399
421
}
400
422
}
401
423
@@ -480,15 +502,19 @@ class nsWSAdmissionManager {
480
502
481
503
class nsOpenConn {
482
504
public:
483
- nsOpenConn (nsCString& addr, nsCString& originSuffix,
505
+ nsOpenConn (nsCString& addr, nsCString& originSuffix, bool failed,
484
506
WebSocketChannel* channel)
485
- : mAddress (addr), mOriginSuffix (originSuffix), mChannel (channel) {
507
+ : mAddress (addr),
508
+ mOriginSuffix (originSuffix),
509
+ mFailed(failed),
510
+ mChannel(channel) {
486
511
MOZ_COUNT_CTOR (nsOpenConn);
487
512
}
488
513
MOZ_COUNTED_DTOR (nsOpenConn)
489
514
490
515
nsCString mAddress;
491
516
nsCString mOriginSuffix ;
517
+ bool mFailed = false ;
492
518
RefPtr<WebSocketChannel> mChannel ;
493
519
};
494
520
@@ -535,6 +561,15 @@ class nsWSAdmissionManager {
535
561
return -1 ;
536
562
}
537
563
564
+ // Returns the index of the first entry that failed, or else the last entry if
565
+ // none found
566
+ uint32_t IndexOfFirstFailure () {
567
+ for (uint32_t i = 0 ; i < mQueue .Length (); i++) {
568
+ if (mQueue [i]->mFailed ) return i;
569
+ }
570
+ return mQueue .Length ();
571
+ }
572
+
538
573
// SessionCount might be decremented from the main or the socket
539
574
// thread, so manage it with atomic counters
540
575
Atomic<int32_t > mSessionCount ;
@@ -2853,6 +2888,10 @@ nsresult WebSocketChannel::DoAdmissionDNS() {
2853
2888
rv = mURI ->GetHost (hostName);
2854
2889
NS_ENSURE_SUCCESS (rv, rv);
2855
2890
mAddress = hostName;
2891
+ nsCString path;
2892
+ rv = mURI ->GetFilePath (path);
2893
+ NS_ENSURE_SUCCESS (rv, rv);
2894
+ mPath = path;
2856
2895
rv = mURI ->GetPort (&mPort );
2857
2896
NS_ENSURE_SUCCESS (rv, rv);
2858
2897
if (mPort == -1 ) mPort = (mEncrypted ? kDefaultWSSPort : kDefaultWSPort );
@@ -4292,7 +4331,6 @@ nsresult WebSocketChannel::OnDataReceived(uint8_t* aData, uint32_t aCount) {
4292
4331
return ProcessInput (aData, aCount);
4293
4332
}
4294
4333
4295
- } // namespace net
4296
- } // namespace mozilla
4334
+ } // namespace mozilla::net
4297
4335
4298
4336
#undef CLOSE_GOING_AWAY
0 commit comments