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