1 : /* vim:set ts=4 sw=4 sts=4 et cin: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsHttpConnectionMgr.h"
40 : #include "nsHttpConnection.h"
41 : #include "nsHttpPipeline.h"
42 : #include "nsHttpHandler.h"
43 : #include "nsNetCID.h"
44 : #include "nsCOMPtr.h"
45 : #include "nsNetUtil.h"
46 :
47 : #include "nsIServiceManager.h"
48 :
49 : #include "nsIObserverService.h"
50 :
51 : #include "nsISSLSocketControl.h"
52 : #include "prnetdb.h"
53 : #include "mozilla/Telemetry.h"
54 :
55 : using namespace mozilla;
56 :
57 : // defined by the socket transport service while active
58 : extern PRThread *gSocketThread;
59 :
60 : static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
61 :
62 : //-----------------------------------------------------------------------------
63 :
64 :
65 20954 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
66 :
67 : static void
68 2990 : InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
69 : {
70 : // insert into queue with smallest valued number first. search in reverse
71 : // order under the assumption that many of the existing transactions will
72 : // have the same priority (usually 0).
73 :
74 2990 : for (PRInt32 i=pendingQ.Length()-1; i>=0; --i) {
75 9 : nsHttpTransaction *t = pendingQ[i];
76 9 : if (trans->Priority() >= t->Priority()) {
77 9 : pendingQ.InsertElementAt(i+1, trans);
78 9 : return;
79 : }
80 : }
81 2981 : pendingQ.InsertElementAt(0, trans);
82 : }
83 :
84 : //-----------------------------------------------------------------------------
85 :
86 673 : nsHttpConnectionMgr::nsHttpConnectionMgr()
87 : : mRef(0)
88 : , mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
89 : , mMaxConns(0)
90 : , mMaxConnsPerHost(0)
91 : , mMaxConnsPerProxy(0)
92 : , mMaxPersistConnsPerHost(0)
93 : , mMaxPersistConnsPerProxy(0)
94 : , mIsShuttingDown(false)
95 : , mNumActiveConns(0)
96 : , mNumIdleConns(0)
97 : , mTimeOfNextWakeUp(LL_MAXUINT)
98 673 : , mReadTimeoutTickArmed(false)
99 : {
100 673 : LOG(("Creating nsHttpConnectionMgr @%x\n", this));
101 673 : mCT.Init();
102 673 : mAlternateProtocolHash.Init(16);
103 673 : mSpdyPreferredHash.Init();
104 673 : }
105 :
106 2013 : nsHttpConnectionMgr::~nsHttpConnectionMgr()
107 : {
108 671 : LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
109 671 : if (mReadTimeoutTick)
110 0 : mReadTimeoutTick->Cancel();
111 2684 : }
112 :
113 : nsresult
114 7745 : nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
115 : {
116 : nsresult rv;
117 15490 : nsCOMPtr<nsIEventTarget> sts;
118 15490 : nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
119 7745 : if (NS_SUCCEEDED(rv)) {
120 7745 : bool offline = true;
121 7745 : ioService->GetOffline(&offline);
122 :
123 7745 : if (!offline) {
124 7731 : sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
125 : }
126 : }
127 :
128 15490 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
129 :
130 : // do nothing if already initialized or if we've shut down
131 7745 : if (mSocketThreadTarget || mIsShuttingDown)
132 7071 : return NS_OK;
133 :
134 674 : mSocketThreadTarget = sts;
135 :
136 674 : return rv;
137 : }
138 :
139 : nsresult
140 673 : nsHttpConnectionMgr::Init(PRUint16 maxConns,
141 : PRUint16 maxConnsPerHost,
142 : PRUint16 maxConnsPerProxy,
143 : PRUint16 maxPersistConnsPerHost,
144 : PRUint16 maxPersistConnsPerProxy,
145 : PRUint16 maxRequestDelay,
146 : PRUint16 maxPipelinedRequests)
147 : {
148 673 : LOG(("nsHttpConnectionMgr::Init\n"));
149 :
150 : {
151 1346 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
152 :
153 673 : mMaxConns = maxConns;
154 673 : mMaxConnsPerHost = maxConnsPerHost;
155 673 : mMaxConnsPerProxy = maxConnsPerProxy;
156 673 : mMaxPersistConnsPerHost = maxPersistConnsPerHost;
157 673 : mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
158 673 : mMaxRequestDelay = maxRequestDelay;
159 673 : mMaxPipelinedRequests = maxPipelinedRequests;
160 :
161 673 : mIsShuttingDown = false;
162 : }
163 :
164 673 : return EnsureSocketThreadTargetIfOnline();
165 : }
166 :
167 : nsresult
168 1873 : nsHttpConnectionMgr::Shutdown()
169 : {
170 1873 : LOG(("nsHttpConnectionMgr::Shutdown\n"));
171 :
172 3746 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
173 :
174 : // do nothing if already shutdown
175 1873 : if (!mSocketThreadTarget)
176 1201 : return NS_OK;
177 :
178 672 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown);
179 :
180 : // release our reference to the STS to prevent further events
181 : // from being posted. this is how we indicate that we are
182 : // shutting down.
183 672 : mIsShuttingDown = true;
184 672 : mSocketThreadTarget = 0;
185 :
186 672 : if (NS_FAILED(rv)) {
187 0 : NS_WARNING("unable to post SHUTDOWN message");
188 0 : return rv;
189 : }
190 :
191 : // wait for shutdown event to complete
192 672 : mon.Wait();
193 672 : return NS_OK;
194 : }
195 :
196 : nsresult
197 7072 : nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
198 : {
199 : // This object doesn't get reinitialized if the offline state changes, so our
200 : // socket thread target might be uninitialized if we were offline when this
201 : // object was being initialized, and we go online later on. This call takes
202 : // care of initializing the socket thread target if that's the case.
203 7072 : EnsureSocketThreadTargetIfOnline();
204 :
205 14144 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
206 :
207 : nsresult rv;
208 7072 : if (!mSocketThreadTarget) {
209 12 : NS_WARNING("cannot post event if not initialized");
210 12 : rv = NS_ERROR_NOT_INITIALIZED;
211 : }
212 : else {
213 14120 : nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
214 7060 : if (!event)
215 0 : rv = NS_ERROR_OUT_OF_MEMORY;
216 : else
217 7060 : rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
218 : }
219 7072 : return rv;
220 : }
221 :
222 : void
223 4 : nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
224 : {
225 4 : LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
226 :
227 4 : if(!mTimer)
228 4 : mTimer = do_CreateInstance("@mozilla.org/timer;1");
229 :
230 : // failure to create a timer is not a fatal error, but idle connections
231 : // will not be cleaned up until we try to use them.
232 4 : if (mTimer) {
233 4 : mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
234 4 : mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
235 : } else {
236 0 : NS_WARNING("failed to create: timer for pruning the dead connections!");
237 : }
238 4 : }
239 :
240 : void
241 292 : nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
242 : {
243 : // Leave the timer in place if there are connections that potentially
244 : // need management
245 292 : if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
246 7 : return;
247 :
248 285 : LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
249 :
250 : // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
251 285 : mTimeOfNextWakeUp = LL_MAXUINT;
252 285 : if (mTimer) {
253 4 : mTimer->Cancel();
254 4 : mTimer = NULL;
255 : }
256 : }
257 :
258 : void
259 5977 : nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick()
260 : {
261 5977 : LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick "
262 : "armed=%d active=%d\n", mReadTimeoutTickArmed, mNumActiveConns));
263 :
264 5977 : if (!mReadTimeoutTickArmed)
265 2741 : return;
266 :
267 3236 : if (mNumActiveConns)
268 496 : return;
269 :
270 2740 : LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick stop==true\n"));
271 :
272 2740 : mReadTimeoutTick->Cancel();
273 2740 : mReadTimeoutTickArmed = false;
274 : }
275 :
276 : //-----------------------------------------------------------------------------
277 : // nsHttpConnectionMgr::nsIObserver
278 : //-----------------------------------------------------------------------------
279 :
280 : NS_IMETHODIMP
281 1 : nsHttpConnectionMgr::Observe(nsISupports *subject,
282 : const char *topic,
283 : const PRUnichar *data)
284 : {
285 1 : LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
286 :
287 1 : if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
288 2 : nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
289 1 : if (timer == mTimer) {
290 0 : PruneDeadConnections();
291 : }
292 1 : else if (timer == mReadTimeoutTick) {
293 1 : ReadTimeoutTick();
294 : }
295 : else {
296 0 : NS_ABORT_IF_FALSE(false, "unexpected timer-callback");
297 0 : LOG(("Unexpected timer object\n"));
298 0 : return NS_ERROR_UNEXPECTED;
299 : }
300 : }
301 :
302 1 : return NS_OK;
303 : }
304 :
305 :
306 : //-----------------------------------------------------------------------------
307 :
308 : nsresult
309 3002 : nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
310 : {
311 3002 : LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
312 :
313 3002 : NS_ADDREF(trans);
314 3002 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
315 3002 : if (NS_FAILED(rv))
316 12 : NS_RELEASE(trans);
317 3002 : return rv;
318 : }
319 :
320 : nsresult
321 0 : nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, PRInt32 priority)
322 : {
323 0 : LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
324 :
325 0 : NS_ADDREF(trans);
326 0 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
327 0 : if (NS_FAILED(rv))
328 0 : NS_RELEASE(trans);
329 0 : return rv;
330 : }
331 :
332 : nsresult
333 163 : nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
334 : {
335 163 : LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
336 :
337 163 : NS_ADDREF(trans);
338 163 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
339 163 : if (NS_FAILED(rv))
340 0 : NS_RELEASE(trans);
341 163 : return rv;
342 : }
343 :
344 : nsresult
345 124 : nsHttpConnectionMgr::PruneDeadConnections()
346 : {
347 124 : return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
348 : }
349 :
350 : nsresult
351 123 : nsHttpConnectionMgr::ClosePersistentConnections()
352 : {
353 123 : return PostEvent(&nsHttpConnectionMgr::OnMsgClosePersistentConnections);
354 : }
355 :
356 : nsresult
357 0 : nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
358 : {
359 : // This object doesn't get reinitialized if the offline state changes, so our
360 : // socket thread target might be uninitialized if we were offline when this
361 : // object was being initialized, and we go online later on. This call takes
362 : // care of initializing the socket thread target if that's the case.
363 0 : EnsureSocketThreadTargetIfOnline();
364 :
365 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
366 0 : NS_IF_ADDREF(*target = mSocketThreadTarget);
367 0 : return NS_OK;
368 : }
369 :
370 : void
371 0 : nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
372 : {
373 0 : LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
374 :
375 0 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
376 :
377 0 : nsRefPtr<nsHttpConnectionInfo> ci;
378 0 : pipeline->GetConnectionInfo(getter_AddRefs(ci));
379 0 : if (ci) {
380 0 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
381 0 : if (ent) {
382 : // search for another request to pipeline...
383 0 : PRInt32 i, count = ent->mPendingQ.Length();
384 0 : for (i=0; i<count; ++i) {
385 0 : nsHttpTransaction *trans = ent->mPendingQ[i];
386 0 : if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
387 0 : pipeline->AddTransaction(trans);
388 :
389 : // remove transaction from pending queue
390 0 : ent->mPendingQ.RemoveElementAt(i);
391 0 : NS_RELEASE(trans);
392 0 : break;
393 : }
394 : }
395 : }
396 : }
397 0 : }
398 :
399 : nsresult
400 2988 : nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
401 : {
402 2988 : LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
403 :
404 2988 : NS_ADDREF(conn);
405 2988 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
406 2988 : if (NS_FAILED(rv))
407 0 : NS_RELEASE(conn);
408 2988 : return rv;
409 : }
410 :
411 : nsresult
412 0 : nsHttpConnectionMgr::UpdateParam(nsParamName name, PRUint16 value)
413 : {
414 0 : PRUint32 param = (PRUint32(name) << 16) | PRUint32(value);
415 0 : return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0, (void *) param);
416 : }
417 :
418 : nsresult
419 0 : nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
420 : {
421 0 : LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
422 :
423 0 : NS_ADDREF(ci);
424 0 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
425 0 : if (NS_FAILED(rv))
426 0 : NS_RELEASE(ci);
427 0 : return rv;
428 : }
429 :
430 : // Given a nsHttpConnectionInfo find the connection entry object that
431 : // contains either the nshttpconnection or nshttptransaction parameter.
432 : // Normally this is done by the hashkey lookup of connectioninfo,
433 : // but if spdy coalescing is in play it might be found in a redirected
434 : // entry
435 : nsHttpConnectionMgr::nsConnectionEntry *
436 6130 : nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
437 : nsHttpConnection *conn,
438 : nsHttpTransaction *trans)
439 : {
440 6130 : if (!ci)
441 0 : return nsnull;
442 :
443 6130 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
444 :
445 : // If there is no sign of coalescing (or it is disabled) then just
446 : // return the primary hash lookup
447 6130 : if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
448 6130 : return ent;
449 :
450 : // If there is no preferred coalescing entry for this host (or the
451 : // preferred entry is the one that matched the mCT hash lookup) then
452 : // there is only option
453 0 : nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
454 0 : if (!preferred || (preferred == ent))
455 0 : return ent;
456 :
457 0 : if (conn) {
458 : // The connection could be either in preferred or ent. It is most
459 : // likely the only active connection in preferred - so start with that.
460 0 : if (preferred->mActiveConns.Contains(conn))
461 0 : return preferred;
462 0 : if (preferred->mIdleConns.Contains(conn))
463 0 : return preferred;
464 : }
465 :
466 0 : if (trans && preferred->mPendingQ.Contains(trans))
467 0 : return preferred;
468 :
469 : // Neither conn nor trans found in preferred, use the default entry
470 0 : return ent;
471 : }
472 :
473 : nsresult
474 0 : nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
475 : {
476 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
477 0 : LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
478 : this, conn));
479 :
480 0 : if (!conn->ConnectionInfo())
481 0 : return NS_ERROR_UNEXPECTED;
482 :
483 : nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
484 0 : conn, nsnull);
485 :
486 0 : if (!ent || !ent->mIdleConns.RemoveElement(conn))
487 0 : return NS_ERROR_UNEXPECTED;
488 :
489 0 : conn->Close(NS_ERROR_ABORT);
490 0 : NS_RELEASE(conn);
491 0 : mNumIdleConns--;
492 0 : ConditionallyStopPruneDeadConnectionsTimer();
493 0 : return NS_OK;
494 : }
495 :
496 : // This function lets a connection, after completing the NPN phase,
497 : // report whether or not it is using spdy through the usingSpdy
498 : // argument. It would not be necessary if NPN were driven out of
499 : // the connection manager. The connection entry associated with the
500 : // connection is then updated to indicate whether or not we want to use
501 : // spdy with that host and update the preliminary preferred host
502 : // entries used for de-sharding hostsnames.
503 : void
504 2988 : nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
505 : bool usingSpdy)
506 : {
507 2988 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
508 :
509 : nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
510 2988 : conn, nsnull);
511 :
512 2988 : NS_ABORT_IF_FALSE(ent, "no connection entry");
513 2988 : if (!ent)
514 0 : return;
515 :
516 2988 : ent->mTestedSpdy = true;
517 :
518 2988 : if (!usingSpdy)
519 2988 : return;
520 :
521 0 : ent->mUsingSpdy = true;
522 :
523 0 : PRUint32 ttl = conn->TimeToLive();
524 0 : PRUint64 timeOfExpire = NowInSeconds() + ttl;
525 0 : if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
526 0 : PruneDeadConnectionsAfter(ttl);
527 :
528 : // Lookup preferred directly from the hash instead of using
529 : // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
530 : // check at this point because the cert is never part of the hash
531 : // lookup. Filtering on that has to be done at the time of use
532 : // rather than the time of registration (i.e. now).
533 : nsConnectionEntry *preferred =
534 0 : mSpdyPreferredHash.Get(ent->mCoalescingKey);
535 :
536 0 : LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
537 : ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
538 : ent, preferred));
539 :
540 0 : if (!preferred) {
541 0 : if (!ent->mCoalescingKey.IsEmpty()) {
542 0 : mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
543 0 : ent->mSpdyPreferred = true;
544 0 : preferred = ent;
545 : }
546 : }
547 0 : else if (preferred != ent) {
548 : // A different hostname is the preferred spdy host for this
549 : // IP address. That preferred mapping must have been setup while
550 : // this connection was negotiating NPN.
551 :
552 : // Call don't reuse on the current connection to shut it down as soon
553 : // as possible without causing any errors.
554 : // i.e. the current transaction(s) on this connection will be processed
555 : // normally, but then it will go away and future connections will be
556 : // coalesced through the preferred entry.
557 :
558 0 : conn->DontReuse();
559 : }
560 :
561 0 : ProcessAllSpdyPendingQ();
562 : }
563 :
564 : bool
565 5060 : nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
566 : {
567 5060 : if (!gHttpHandler->UseAlternateProtocol())
568 0 : return false;
569 :
570 : // The Alternate Protocol hash is protected under the monitor because
571 : // it is read from both the main and the network thread.
572 10120 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
573 :
574 5060 : return mAlternateProtocolHash.Contains(hostPortKey);
575 : }
576 :
577 : void
578 0 : nsHttpConnectionMgr::ReportSpdyAlternateProtocol(nsHttpConnection *conn)
579 : {
580 : // Check network.http.spdy.use-alternate-protocol pref
581 0 : if (!gHttpHandler->UseAlternateProtocol())
582 0 : return;
583 :
584 : // For now lets not bypass proxies due to the alternate-protocol header
585 0 : if (conn->ConnectionInfo()->UsingHttpProxy())
586 0 : return;
587 :
588 0 : nsCString hostPortKey(conn->ConnectionInfo()->Host());
589 0 : if (conn->ConnectionInfo()->Port() != 80) {
590 0 : hostPortKey.Append(NS_LITERAL_CSTRING(":"));
591 0 : hostPortKey.AppendInt(conn->ConnectionInfo()->Port());
592 : }
593 :
594 : // The Alternate Protocol hash is protected under the monitor because
595 : // it is read from both the main and the network thread.
596 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
597 :
598 : // Check to see if this is already present
599 0 : if (mAlternateProtocolHash.Contains(hostPortKey))
600 : return;
601 :
602 0 : if (mAlternateProtocolHash.Count() > 2000)
603 : mAlternateProtocolHash.EnumerateEntries(&TrimAlternateProtocolHash,
604 0 : this);
605 :
606 0 : mAlternateProtocolHash.PutEntry(hostPortKey);
607 : }
608 :
609 : void
610 2 : nsHttpConnectionMgr::RemoveSpdyAlternateProtocol(nsACString &hostPortKey)
611 : {
612 : // The Alternate Protocol hash is protected under the monitor because
613 : // it is read from both the main and the network thread.
614 4 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
615 :
616 2 : return mAlternateProtocolHash.RemoveEntry(hostPortKey);
617 : }
618 :
619 : PLDHashOperator
620 0 : nsHttpConnectionMgr::TrimAlternateProtocolHash(nsCStringHashKey *entry,
621 : void *closure)
622 : {
623 0 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
624 :
625 0 : if (self->mAlternateProtocolHash.Count() > 2000)
626 0 : return PL_DHASH_REMOVE;
627 0 : return PL_DHASH_STOP;
628 : }
629 :
630 : nsHttpConnectionMgr::nsConnectionEntry *
631 12219 : nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
632 : {
633 36657 : if (!gHttpHandler->IsSpdyEnabled() ||
634 12219 : !gHttpHandler->CoalesceSpdy() ||
635 12219 : aOriginalEntry->mCoalescingKey.IsEmpty())
636 12183 : return nsnull;
637 :
638 : nsConnectionEntry *preferred =
639 36 : mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
640 :
641 : // if there is no redirection no cert validation is required
642 36 : if (preferred == aOriginalEntry)
643 0 : return aOriginalEntry;
644 :
645 : // if there is no preferred host or it is no longer using spdy
646 : // then skip pooling
647 36 : if (!preferred || !preferred->mUsingSpdy)
648 36 : return nsnull;
649 :
650 : // if there is not an active spdy session in this entry then
651 : // we cannot pool because the cert upon activation may not
652 : // be the same as the old one. Active sessions are prohibited
653 : // from changing certs.
654 :
655 0 : nsHttpConnection *activeSpdy = nsnull;
656 :
657 0 : for (PRUint32 index = 0; index < preferred->mActiveConns.Length(); ++index) {
658 0 : if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
659 0 : activeSpdy = preferred->mActiveConns[index];
660 0 : break;
661 : }
662 : }
663 :
664 0 : if (!activeSpdy) {
665 : // remove the preferred status of this entry if it cannot be
666 : // used for pooling.
667 0 : preferred->mSpdyPreferred = false;
668 0 : RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
669 0 : LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
670 : "preferred host mapping %s to %s removed due to inactivity.\n",
671 : aOriginalEntry->mConnInfo->Host(),
672 : preferred->mConnInfo->Host()));
673 :
674 0 : return nsnull;
675 : }
676 :
677 : // Check that the server cert supports redirection
678 : nsresult rv;
679 0 : bool isJoined = false;
680 :
681 0 : nsCOMPtr<nsISupports> securityInfo;
682 0 : nsCOMPtr<nsISSLSocketControl> sslSocketControl;
683 0 : nsCAutoString negotiatedNPN;
684 :
685 0 : activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
686 0 : if (!securityInfo) {
687 0 : NS_WARNING("cannot obtain spdy security info");
688 0 : return nsnull;
689 : }
690 :
691 0 : sslSocketControl = do_QueryInterface(securityInfo, &rv);
692 0 : if (NS_FAILED(rv)) {
693 0 : NS_WARNING("sslSocketControl QI Failed");
694 0 : return nsnull;
695 : }
696 :
697 0 : rv = sslSocketControl->JoinConnection(NS_LITERAL_CSTRING("spdy/2"),
698 0 : aOriginalEntry->mConnInfo->GetHost(),
699 : aOriginalEntry->mConnInfo->Port(),
700 0 : &isJoined);
701 :
702 0 : if (NS_FAILED(rv) || !isJoined) {
703 0 : LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
704 : "Host %s cannot be confirmed to be joined "
705 : "with %s connections. rv=%x isJoined=%d",
706 : preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
707 : rv, isJoined));
708 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN,
709 0 : false);
710 0 : return nsnull;
711 : }
712 :
713 : // IP pooling confirmed
714 0 : LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
715 : "Host %s has cert valid for %s connections, "
716 : "so %s will be coalesced with %s",
717 : preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
718 : aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
719 0 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN, true);
720 0 : return preferred;
721 : }
722 :
723 : void
724 0 : nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
725 : {
726 0 : if (aHashKey.IsEmpty())
727 0 : return;
728 :
729 0 : mSpdyPreferredHash.Remove(aHashKey);
730 : }
731 :
732 : //-----------------------------------------------------------------------------
733 : // enumeration callbacks
734 :
735 : PLDHashOperator
736 3246 : nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
737 : nsAutoPtr<nsConnectionEntry> &ent,
738 : void *closure)
739 : {
740 3246 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
741 :
742 3246 : if (self->ProcessPendingQForEntry(ent))
743 0 : return PL_DHASH_STOP;
744 :
745 3246 : return PL_DHASH_NEXT;
746 : }
747 :
748 : // If the global number of idle connections is preventing the opening of
749 : // new connections to a host without idle connections, then
750 : // close them regardless of their TTL
751 : PLDHashOperator
752 0 : nsHttpConnectionMgr::PurgeExcessIdleConnectionsCB(const nsACString &key,
753 : nsAutoPtr<nsConnectionEntry> &ent,
754 : void *closure)
755 : {
756 0 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
757 :
758 0 : while (self->mNumIdleConns + self->mNumActiveConns + 1 >= self->mMaxConns) {
759 0 : if (!ent->mIdleConns.Length()) {
760 : // There are no idle conns left in this connection entry
761 0 : return PL_DHASH_NEXT;
762 : }
763 0 : nsHttpConnection *conn = ent->mIdleConns[0];
764 0 : ent->mIdleConns.RemoveElementAt(0);
765 0 : conn->Close(NS_ERROR_ABORT);
766 0 : NS_RELEASE(conn);
767 0 : self->mNumIdleConns--;
768 0 : self->ConditionallyStopPruneDeadConnectionsTimer();
769 : }
770 0 : return PL_DHASH_STOP;
771 : }
772 :
773 : PLDHashOperator
774 1 : nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
775 : nsAutoPtr<nsConnectionEntry> &ent,
776 : void *closure)
777 : {
778 1 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
779 :
780 1 : LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
781 :
782 : // Find out how long it will take for next idle connection to not be reusable
783 : // anymore.
784 1 : PRUint32 timeToNextExpire = PR_UINT32_MAX;
785 1 : PRInt32 count = ent->mIdleConns.Length();
786 1 : if (count > 0) {
787 0 : for (PRInt32 i=count-1; i>=0; --i) {
788 0 : nsHttpConnection *conn = ent->mIdleConns[i];
789 0 : if (!conn->CanReuse()) {
790 0 : ent->mIdleConns.RemoveElementAt(i);
791 0 : conn->Close(NS_ERROR_ABORT);
792 0 : NS_RELEASE(conn);
793 0 : self->mNumIdleConns--;
794 : } else {
795 0 : timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
796 : }
797 : }
798 : }
799 :
800 1 : if (ent->mUsingSpdy) {
801 0 : for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
802 0 : nsHttpConnection *conn = ent->mActiveConns[index];
803 0 : if (conn->UsingSpdy()) {
804 0 : if (!conn->CanReuse()) {
805 : // marking it dont reuse will create an active tear down if
806 : // the spdy session is idle.
807 0 : conn->DontReuse();
808 : }
809 : else {
810 : timeToNextExpire = NS_MIN(timeToNextExpire,
811 0 : conn->TimeToLive());
812 : }
813 : }
814 : }
815 : }
816 :
817 : // If time to next expire found is shorter than time to next wake-up, we need to
818 : // change the time for next wake-up.
819 1 : if (timeToNextExpire != PR_UINT32_MAX) {
820 0 : PRUint32 now = NowInSeconds();
821 0 : PRUint64 timeOfNextExpire = now + timeToNextExpire;
822 : // If pruning of dead connections is not already scheduled to happen
823 : // or time found for next connection to expire is is before
824 : // mTimeOfNextWakeUp, we need to schedule the pruning to happen
825 : // after timeToNextExpire.
826 0 : if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
827 0 : self->PruneDeadConnectionsAfter(timeToNextExpire);
828 : }
829 : } else {
830 1 : self->ConditionallyStopPruneDeadConnectionsTimer();
831 : }
832 : #ifdef DEBUG
833 1 : count = ent->mActiveConns.Length();
834 1 : if (count > 0) {
835 2 : for (PRInt32 i=count-1; i>=0; --i) {
836 1 : nsHttpConnection *conn = ent->mActiveConns[i];
837 1 : LOG((" active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
838 : }
839 : }
840 : #endif
841 :
842 : // if this entry is empty, then we can remove it.
843 2 : if (ent->mIdleConns.Length() == 0 &&
844 1 : ent->mActiveConns.Length() == 0 &&
845 0 : ent->mHalfOpens.Length() == 0 &&
846 0 : ent->mPendingQ.Length() == 0 &&
847 0 : ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
848 0 : !gHttpHandler->IsSpdyEnabled() ||
849 0 : self->mCT.Count() > 300)) {
850 0 : LOG((" removing empty connection entry\n"));
851 0 : return PL_DHASH_REMOVE;
852 : }
853 :
854 : // else, use this opportunity to compact our arrays...
855 1 : ent->mIdleConns.Compact();
856 1 : ent->mActiveConns.Compact();
857 1 : ent->mPendingQ.Compact();
858 :
859 1 : return PL_DHASH_NEXT;
860 : }
861 :
862 : PLDHashOperator
863 291 : nsHttpConnectionMgr::ShutdownPassCB(const nsACString &key,
864 : nsAutoPtr<nsConnectionEntry> &ent,
865 : void *closure)
866 : {
867 291 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
868 :
869 : nsHttpTransaction *trans;
870 : nsHttpConnection *conn;
871 :
872 : // close all active connections
873 582 : while (ent->mActiveConns.Length()) {
874 0 : conn = ent->mActiveConns[0];
875 :
876 0 : ent->mActiveConns.RemoveElementAt(0);
877 0 : self->mNumActiveConns--;
878 :
879 0 : conn->Close(NS_ERROR_ABORT);
880 0 : NS_RELEASE(conn);
881 : }
882 :
883 : // close all idle connections
884 586 : while (ent->mIdleConns.Length()) {
885 4 : conn = ent->mIdleConns[0];
886 :
887 4 : ent->mIdleConns.RemoveElementAt(0);
888 4 : self->mNumIdleConns--;
889 :
890 4 : conn->Close(NS_ERROR_ABORT);
891 4 : NS_RELEASE(conn);
892 : }
893 : // If all idle connections are removed,
894 : // we can stop pruning dead connections.
895 291 : self->ConditionallyStopPruneDeadConnectionsTimer();
896 :
897 : // close all pending transactions
898 583 : while (ent->mPendingQ.Length()) {
899 1 : trans = ent->mPendingQ[0];
900 :
901 1 : ent->mPendingQ.RemoveElementAt(0);
902 :
903 1 : trans->Close(NS_ERROR_ABORT);
904 1 : NS_RELEASE(trans);
905 : }
906 :
907 : // close all half open tcp connections
908 291 : for (PRInt32 i = ((PRInt32) ent->mHalfOpens.Length()) - 1; i >= 0; i--)
909 0 : ent->mHalfOpens[i]->Abandon();
910 :
911 291 : return PL_DHASH_REMOVE;
912 : }
913 :
914 : //-----------------------------------------------------------------------------
915 :
916 : bool
917 6235 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
918 : {
919 6235 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
920 6235 : LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
921 : ent->mConnInfo->HashKey().get()));
922 :
923 6235 : ProcessSpdyPendingQ(ent);
924 :
925 6235 : PRUint32 i, count = ent->mPendingQ.Length();
926 6235 : if (count > 0) {
927 0 : LOG((" pending-count=%u\n", count));
928 0 : nsHttpTransaction *trans = nsnull;
929 0 : nsHttpConnection *conn = nsnull;
930 0 : for (i = 0; i < count; ++i) {
931 0 : trans = ent->mPendingQ[i];
932 :
933 : // When this transaction has already established a half-open
934 : // connection, we want to prevent any duplicate half-open
935 : // connections from being established and bound to this
936 : // transaction. Allow only use of an idle persistent connection
937 : // (if found) for transactions referred by a half-open connection.
938 0 : bool alreadyHalfOpen = false;
939 0 : for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
940 0 : if (ent->mHalfOpens[j]->Transaction() == trans) {
941 0 : alreadyHalfOpen = true;
942 0 : break;
943 : }
944 : }
945 :
946 0 : GetConnection(ent, trans, alreadyHalfOpen, &conn);
947 0 : if (conn)
948 0 : break;
949 :
950 0 : NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
951 : "something mutated pending queue from "
952 : "GetConnection()");
953 : }
954 0 : if (conn) {
955 0 : LOG((" dispatching pending transaction...\n"));
956 :
957 : // remove pending transaction
958 0 : ent->mPendingQ.RemoveElementAt(i);
959 :
960 0 : nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
961 0 : if (NS_SUCCEEDED(rv))
962 0 : NS_RELEASE(trans);
963 : else {
964 0 : LOG((" DispatchTransaction failed [rv=%x]\n", rv));
965 : // on failure, just put the transaction back
966 0 : ent->mPendingQ.InsertElementAt(i, trans);
967 : // might be something wrong with the connection... close it.
968 0 : conn->Close(rv);
969 : }
970 :
971 0 : NS_RELEASE(conn);
972 0 : return true;
973 : }
974 : }
975 6235 : return false;
976 : }
977 :
978 : // we're at the active connection limit if any one of the following conditions is true:
979 : // (1) at max-connections
980 : // (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
981 : // (3) keep-alive disabled and at max-connections-per-server
982 : bool
983 2990 : nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
984 : {
985 2990 : nsHttpConnectionInfo *ci = ent->mConnInfo;
986 :
987 2990 : LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
988 : ci->HashKey().get(), caps));
989 :
990 : // update maxconns if potentially limited by the max socket count
991 : // this requires a dynamic reduction in the max socket count to a point
992 : // lower than the max-connections pref.
993 2990 : PRUint32 maxSocketCount = gHttpHandler->MaxSocketCount();
994 2990 : if (mMaxConns > maxSocketCount) {
995 0 : mMaxConns = maxSocketCount;
996 0 : LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u",
997 : this, mMaxConns));
998 : }
999 :
1000 : // If there are more active connections than the global limit, then we're
1001 : // done. Purging idle connections won't get us below it.
1002 2990 : if (mNumActiveConns >= mMaxConns) {
1003 0 : LOG((" num active conns == max conns\n"));
1004 0 : return true;
1005 : }
1006 :
1007 : nsHttpConnection *conn;
1008 2990 : PRInt32 i, totalCount, persistCount = 0;
1009 :
1010 2990 : totalCount = ent->mActiveConns.Length();
1011 :
1012 : // count the number of persistent connections
1013 3420 : for (i=0; i<totalCount; ++i) {
1014 430 : conn = ent->mActiveConns[i];
1015 430 : if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
1016 407 : persistCount++;
1017 : }
1018 :
1019 : // Add in the in-progress tcp connections, we will assume they are
1020 : // keepalive enabled.
1021 2990 : totalCount += ent->mHalfOpens.Length();
1022 2990 : persistCount += ent->mHalfOpens.Length();
1023 :
1024 2990 : LOG((" total=%d, persist=%d\n", totalCount, persistCount));
1025 :
1026 : PRUint16 maxConns;
1027 : PRUint16 maxPersistConns;
1028 :
1029 2990 : if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
1030 27 : maxConns = mMaxConnsPerProxy;
1031 27 : maxPersistConns = mMaxPersistConnsPerProxy;
1032 : }
1033 : else {
1034 2963 : maxConns = mMaxConnsPerHost;
1035 2963 : maxPersistConns = mMaxPersistConnsPerHost;
1036 : }
1037 :
1038 : // use >= just to be safe
1039 : return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
1040 2990 : (persistCount >= maxPersistConns) );
1041 : }
1042 :
1043 : void
1044 4 : nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
1045 : {
1046 4 : LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
1047 : ent->mConnInfo->HashKey().get()));
1048 8 : while (ent->mIdleConns.Length()) {
1049 0 : nsHttpConnection *conn = ent->mIdleConns[0];
1050 0 : ent->mIdleConns.RemoveElementAt(0);
1051 0 : mNumIdleConns--;
1052 0 : conn->Close(NS_ERROR_ABORT);
1053 0 : NS_RELEASE(conn);
1054 : }
1055 :
1056 4 : PRInt32 activeCount = ent->mActiveConns.Length();
1057 5 : for (PRInt32 i=0; i < activeCount; i++)
1058 1 : ent->mActiveConns[i]->DontReuse();
1059 4 : }
1060 :
1061 : PLDHashOperator
1062 4 : nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
1063 : nsAutoPtr<nsConnectionEntry> &ent,
1064 : void *closure)
1065 : {
1066 4 : nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
1067 4 : self->ClosePersistentConnections(ent);
1068 4 : return PL_DHASH_NEXT;
1069 : }
1070 :
1071 : void
1072 2990 : nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
1073 : nsHttpTransaction *trans,
1074 : bool onlyReusedConnection,
1075 : nsHttpConnection **result)
1076 : {
1077 2990 : LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
1078 : ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
1079 :
1080 : // First, see if an existing connection can be used - either an idle
1081 : // persistent connection or an active spdy session may be reused instead of
1082 : // establishing a new socket. We do not need to check the connection limits
1083 : // yet as they govern the maximum number of open connections and reusing
1084 : // an old connection never increases that.
1085 :
1086 2990 : *result = nsnull;
1087 :
1088 2990 : nsHttpConnection *conn = nsnull;
1089 2990 : bool addConnToActiveList = true;
1090 :
1091 2990 : if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
1092 :
1093 : // if willing to use spdy look for an active spdy connections
1094 : // before considering idle http ones.
1095 2990 : if (gHttpHandler->IsSpdyEnabled()) {
1096 2990 : conn = GetSpdyPreferredConn(ent);
1097 2990 : if (conn)
1098 0 : addConnToActiveList = false;
1099 : }
1100 :
1101 : // search the idle connection list. Each element in the list
1102 : // has a reference, so if we remove it from the list into a local
1103 : // ptr, that ptr now owns the reference
1104 5980 : while (!conn && (ent->mIdleConns.Length() > 0)) {
1105 0 : conn = ent->mIdleConns[0];
1106 : // we check if the connection can be reused before even checking if
1107 : // it is a "matching" connection.
1108 0 : if (!conn->CanReuse()) {
1109 0 : LOG((" dropping stale connection: [conn=%x]\n", conn));
1110 0 : conn->Close(NS_ERROR_ABORT);
1111 0 : NS_RELEASE(conn);
1112 : }
1113 : else {
1114 0 : LOG((" reusing connection [conn=%x]\n", conn));
1115 0 : conn->EndIdleMonitoring();
1116 : }
1117 :
1118 0 : ent->mIdleConns.RemoveElementAt(0);
1119 0 : mNumIdleConns--;
1120 :
1121 : // If there are no idle connections left at all, we need to make
1122 : // sure that we are not pruning dead connections anymore.
1123 0 : ConditionallyStopPruneDeadConnectionsTimer();
1124 : }
1125 : }
1126 :
1127 2990 : if (!conn) {
1128 :
1129 : // If the onlyReusedConnection parameter is TRUE, then GetConnection()
1130 : // does not create new transports under any circumstances.
1131 2990 : if (onlyReusedConnection)
1132 0 : return;
1133 :
1134 5984 : if (gHttpHandler->IsSpdyEnabled() &&
1135 2990 : ent->mConnInfo->UsingSSL() &&
1136 4 : !ent->mConnInfo->UsingHttpProxy())
1137 : {
1138 : // If this host is trying to negotiate a SPDY session right now,
1139 : // don't create any new connections until the result of the
1140 : // negotiation is known.
1141 :
1142 12 : if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
1143 8 : (ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
1144 0 : return;
1145 : }
1146 :
1147 : // Check if we need to purge an idle connection. Note that we may have
1148 : // removed one above; if so, this will be a no-op. We do this before
1149 : // checking the active connection limit to catch the case where we do
1150 : // have an idle connection, but the purge timer hasn't fired yet.
1151 : // XXX this just purges a random idle connection. we should instead
1152 : // enumerate the entire hash table to find the eldest idle connection.
1153 2990 : if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
1154 0 : mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
1155 :
1156 : // Need to make a new TCP connection. First, we check if we've hit
1157 : // either the maximum connection limit globally or for this particular
1158 : // host or proxy. If we have, we're done.
1159 2990 : if (AtActiveConnectionLimit(ent, trans->Caps())) {
1160 0 : LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
1161 : "at active connection limit - will queue\n",
1162 : ent->mConnInfo->HashKey().get()));
1163 0 : return;
1164 : }
1165 :
1166 2990 : LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
1167 : "%s %s ent=%p spdy=%d",
1168 : ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
1169 : ent, ent->mUsingSpdy));
1170 :
1171 2990 : nsresult rv = CreateTransport(ent, trans);
1172 2990 : if (NS_FAILED(rv))
1173 1 : trans->Close(rv);
1174 2990 : return;
1175 : }
1176 :
1177 0 : if (addConnToActiveList) {
1178 : // hold an owning ref to this connection
1179 0 : ent->mActiveConns.AppendElement(conn);
1180 0 : mNumActiveConns++;
1181 : }
1182 :
1183 0 : NS_ADDREF(conn);
1184 0 : *result = conn;
1185 : }
1186 :
1187 : void
1188 2988 : nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
1189 : nsConnectionEntry *ent)
1190 : {
1191 2988 : NS_ADDREF(conn);
1192 2988 : ent->mActiveConns.AppendElement(conn);
1193 2988 : mNumActiveConns++;
1194 2988 : ActivateTimeoutTick();
1195 2988 : }
1196 :
1197 : void
1198 2989 : nsHttpConnectionMgr::StartedConnect()
1199 : {
1200 2989 : mNumActiveConns++;
1201 2989 : }
1202 :
1203 : void
1204 2989 : nsHttpConnectionMgr::RecvdConnect()
1205 : {
1206 2989 : mNumActiveConns--;
1207 2989 : ConditionallyStopReadTimeoutTick();
1208 2989 : }
1209 :
1210 : nsresult
1211 2990 : nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
1212 : nsHttpTransaction *trans)
1213 : {
1214 2990 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1215 :
1216 5980 : nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
1217 2990 : nsresult rv = sock->SetupPrimaryStreams();
1218 2990 : NS_ENSURE_SUCCESS(rv, rv);
1219 :
1220 2989 : ent->mHalfOpens.AppendElement(sock);
1221 2989 : return NS_OK;
1222 : }
1223 :
1224 : nsresult
1225 2988 : nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
1226 : nsHttpTransaction *aTrans,
1227 : PRUint8 caps,
1228 : nsHttpConnection *conn)
1229 : {
1230 2988 : LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
1231 : ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
1232 : nsresult rv;
1233 :
1234 2988 : PRInt32 priority = aTrans->Priority();
1235 :
1236 2988 : if (conn->UsingSpdy()) {
1237 0 : LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
1238 : "Connection host = %s\n",
1239 : aTrans->ConnectionInfo()->Host(),
1240 : conn->ConnectionInfo()->Host()));
1241 0 : rv = conn->Activate(aTrans, caps, priority);
1242 0 : NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
1243 0 : return rv;
1244 : }
1245 :
1246 2988 : nsConnectionHandle *handle = new nsConnectionHandle(conn);
1247 2988 : if (!handle)
1248 0 : return NS_ERROR_OUT_OF_MEMORY;
1249 2988 : NS_ADDREF(handle);
1250 :
1251 2988 : nsHttpPipeline *pipeline = nsnull;
1252 2988 : nsAHttpTransaction *trans = aTrans;
1253 :
1254 2988 : if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
1255 0 : LOG((" looking to build pipeline...\n"));
1256 0 : if (BuildPipeline(ent, trans, &pipeline))
1257 0 : trans = pipeline;
1258 : }
1259 :
1260 : // give the transaction the indirect reference to the connection.
1261 2988 : trans->SetConnection(handle);
1262 :
1263 2988 : rv = conn->Activate(trans, caps, priority);
1264 :
1265 2988 : if (NS_FAILED(rv)) {
1266 0 : LOG((" conn->Activate failed [rv=%x]\n", rv));
1267 0 : ent->mActiveConns.RemoveElement(conn);
1268 0 : mNumActiveConns--;
1269 0 : ConditionallyStopReadTimeoutTick();
1270 :
1271 : // sever back references to connection, and do so without triggering
1272 : // a call to ReclaimConnection ;-)
1273 0 : trans->SetConnection(nsnull);
1274 0 : NS_RELEASE(handle->mConn);
1275 : // destroy the connection
1276 0 : NS_RELEASE(conn);
1277 : }
1278 :
1279 : // if we were unable to activate the pipeline, then this will destroy
1280 : // the pipeline, which will cause each the transactions owned by the
1281 : // pipeline to be restarted.
1282 2988 : NS_IF_RELEASE(pipeline);
1283 :
1284 2988 : NS_RELEASE(handle);
1285 2988 : return rv;
1286 : }
1287 :
1288 : bool
1289 0 : nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
1290 : nsAHttpTransaction *firstTrans,
1291 : nsHttpPipeline **result)
1292 : {
1293 0 : if (mMaxPipelinedRequests < 2)
1294 0 : return false;
1295 :
1296 0 : nsHttpPipeline *pipeline = nsnull;
1297 : nsHttpTransaction *trans;
1298 :
1299 0 : PRUint32 i = 0, numAdded = 0;
1300 0 : while (i < ent->mPendingQ.Length()) {
1301 0 : trans = ent->mPendingQ[i];
1302 0 : if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
1303 0 : if (numAdded == 0) {
1304 0 : pipeline = new nsHttpPipeline;
1305 0 : if (!pipeline)
1306 0 : return false;
1307 0 : pipeline->AddTransaction(firstTrans);
1308 0 : numAdded = 1;
1309 : }
1310 0 : pipeline->AddTransaction(trans);
1311 :
1312 : // remove transaction from pending queue
1313 0 : ent->mPendingQ.RemoveElementAt(i);
1314 0 : NS_RELEASE(trans);
1315 :
1316 0 : if (++numAdded == mMaxPipelinedRequests)
1317 0 : break;
1318 : }
1319 : else
1320 0 : ++i; // skip to next pending transaction
1321 : }
1322 :
1323 0 : if (numAdded == 0)
1324 0 : return false;
1325 :
1326 0 : LOG((" pipelined %u transactions\n", numAdded));
1327 0 : NS_ADDREF(*result = pipeline);
1328 0 : return true;
1329 : }
1330 :
1331 : nsresult
1332 2990 : nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
1333 : {
1334 2990 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1335 :
1336 : // since "adds" and "cancels" are processed asynchronously and because
1337 : // various events might trigger an "add" directly on the socket thread,
1338 : // we must take care to avoid dispatching a transaction that has already
1339 : // been canceled (see bug 190001).
1340 2990 : if (NS_FAILED(trans->Status())) {
1341 0 : LOG((" transaction was canceled... dropping event!\n"));
1342 0 : return NS_OK;
1343 : }
1344 :
1345 2990 : PRUint8 caps = trans->Caps();
1346 2990 : nsHttpConnectionInfo *ci = trans->ConnectionInfo();
1347 2990 : NS_ASSERTION(ci, "no connection info");
1348 :
1349 2990 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
1350 2990 : if (!ent) {
1351 291 : nsHttpConnectionInfo *clone = ci->Clone();
1352 291 : if (!clone)
1353 0 : return NS_ERROR_OUT_OF_MEMORY;
1354 291 : ent = new nsConnectionEntry(clone);
1355 291 : if (!ent)
1356 0 : return NS_ERROR_OUT_OF_MEMORY;
1357 291 : mCT.Put(ci->HashKey(), ent);
1358 : }
1359 :
1360 : // SPDY coalescing of hostnames means we might redirect from this
1361 : // connection entry onto the preferred one.
1362 2990 : nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
1363 2990 : if (preferredEntry && (preferredEntry != ent)) {
1364 0 : LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
1365 : "redirected via coalescing from %s to %s\n", trans,
1366 : ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
1367 :
1368 0 : ent = preferredEntry;
1369 : }
1370 :
1371 : // If we are doing a force reload then close out any existing conns
1372 : // to this host so that changes in DNS, LBs, etc.. are reflected
1373 2990 : if (caps & NS_HTTP_CLEAR_KEEPALIVES)
1374 0 : ClosePersistentConnections(ent);
1375 :
1376 : // Check if the transaction already has a sticky reference to a connection.
1377 : // If so, then we can just use it directly by transferring its reference
1378 : // to the new connection var instead of calling GetConnection() to search
1379 : // for an available one.
1380 :
1381 2990 : nsAHttpConnection *wrappedConnection = trans->Connection();
1382 : nsHttpConnection *conn;
1383 2990 : conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
1384 :
1385 2990 : if (conn) {
1386 0 : NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
1387 :
1388 0 : trans->SetConnection(nsnull);
1389 : }
1390 : else
1391 2990 : GetConnection(ent, trans, false, &conn);
1392 :
1393 : nsresult rv;
1394 2990 : if (!conn) {
1395 2990 : LOG((" adding transaction to pending queue [trans=%x pending-count=%u]\n",
1396 : trans, ent->mPendingQ.Length()+1));
1397 : // put this transaction on the pending queue...
1398 2990 : InsertTransactionSorted(ent->mPendingQ, trans);
1399 2990 : NS_ADDREF(trans);
1400 2990 : rv = NS_OK;
1401 : }
1402 : else {
1403 0 : rv = DispatchTransaction(ent, trans, caps, conn);
1404 0 : NS_RELEASE(conn);
1405 : }
1406 :
1407 2990 : return rv;
1408 : }
1409 :
1410 : // This function tries to dispatch the pending spdy transactions on
1411 : // the connection entry sent in as an argument. It will do so on the
1412 : // active spdy connection either in that same entry or in the
1413 : // redirected 'preferred' entry for the same coalescing hash key if
1414 : // coalescing is enabled.
1415 :
1416 : void
1417 6239 : nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
1418 : {
1419 6239 : nsHttpConnection *conn = GetSpdyPreferredConn(ent);
1420 6239 : if (!conn)
1421 6239 : return;
1422 :
1423 0 : for (PRInt32 index = ent->mPendingQ.Length() - 1;
1424 0 : index >= 0 && conn->CanDirectlyActivate();
1425 : --index) {
1426 0 : nsHttpTransaction *trans = ent->mPendingQ[index];
1427 :
1428 0 : if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
1429 0 : trans->Caps() & NS_HTTP_DISALLOW_SPDY)
1430 0 : continue;
1431 :
1432 0 : ent->mPendingQ.RemoveElementAt(index);
1433 :
1434 0 : nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
1435 0 : if (NS_FAILED(rv)) {
1436 : // this cannot happen, but if due to some bug it does then
1437 : // close the transaction
1438 0 : NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
1439 0 : LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
1440 : trans));
1441 0 : trans->Close(rv);
1442 : }
1443 0 : NS_RELEASE(trans);
1444 : }
1445 : }
1446 :
1447 : PLDHashOperator
1448 0 : nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
1449 : nsAutoPtr<nsConnectionEntry> &ent,
1450 : void *closure)
1451 : {
1452 0 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
1453 0 : self->ProcessSpdyPendingQ(ent);
1454 0 : return PL_DHASH_NEXT;
1455 : }
1456 :
1457 : void
1458 0 : nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
1459 : {
1460 0 : mCT.Enumerate(ProcessSpdyPendingQCB, this);
1461 0 : }
1462 :
1463 : nsHttpConnection *
1464 9229 : nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
1465 : {
1466 9229 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1467 9229 : NS_ABORT_IF_FALSE(ent, "no connection entry");
1468 :
1469 9229 : nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
1470 :
1471 : // this entry is spdy-enabled if it is involved in a redirect
1472 9229 : if (preferred)
1473 : // all new connections for this entry will use spdy too
1474 0 : ent->mUsingSpdy = true;
1475 : else
1476 9229 : preferred = ent;
1477 :
1478 9229 : nsHttpConnection *conn = nsnull;
1479 :
1480 9229 : if (preferred->mUsingSpdy) {
1481 0 : for (PRUint32 index = 0;
1482 0 : index < preferred->mActiveConns.Length();
1483 : ++index) {
1484 0 : if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
1485 0 : conn = preferred->mActiveConns[index];
1486 0 : break;
1487 : }
1488 : }
1489 : }
1490 :
1491 9229 : return conn;
1492 : }
1493 :
1494 : //-----------------------------------------------------------------------------
1495 :
1496 : void
1497 672 : nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
1498 : {
1499 672 : LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
1500 :
1501 672 : mCT.Enumerate(ShutdownPassCB, this);
1502 :
1503 672 : if (mReadTimeoutTick) {
1504 259 : mReadTimeoutTick->Cancel();
1505 259 : mReadTimeoutTick = nsnull;
1506 259 : mReadTimeoutTickArmed = false;
1507 : }
1508 :
1509 : // signal shutdown complete
1510 1344 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
1511 672 : mon.Notify();
1512 672 : }
1513 :
1514 : void
1515 2990 : nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
1516 : {
1517 2990 : LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
1518 :
1519 2990 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1520 2990 : trans->SetPriority(priority);
1521 2990 : nsresult rv = ProcessNewTransaction(trans);
1522 2990 : if (NS_FAILED(rv))
1523 0 : trans->Close(rv); // for whatever its worth
1524 2990 : NS_RELEASE(trans);
1525 2990 : }
1526 :
1527 : void
1528 0 : nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
1529 : {
1530 0 : LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
1531 :
1532 0 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1533 0 : trans->SetPriority(priority);
1534 :
1535 : nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
1536 0 : nsnull, trans);
1537 :
1538 0 : if (ent) {
1539 0 : PRInt32 index = ent->mPendingQ.IndexOf(trans);
1540 0 : if (index >= 0) {
1541 0 : ent->mPendingQ.RemoveElementAt(index);
1542 0 : InsertTransactionSorted(ent->mPendingQ, trans);
1543 : }
1544 : }
1545 :
1546 0 : NS_RELEASE(trans);
1547 0 : }
1548 :
1549 : void
1550 163 : nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
1551 : {
1552 163 : LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
1553 :
1554 163 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1555 : //
1556 : // if the transaction owns a connection and the transaction is not done,
1557 : // then ask the connection to close the transaction. otherwise, close the
1558 : // transaction directly (removing it from the pending queue first).
1559 : //
1560 163 : nsAHttpConnection *conn = trans->Connection();
1561 163 : if (conn && !trans->IsDone())
1562 10 : conn->CloseTransaction(trans, reason);
1563 : else {
1564 : nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
1565 153 : nsnull, trans);
1566 :
1567 153 : if (ent) {
1568 153 : PRInt32 index = ent->mPendingQ.IndexOf(trans);
1569 153 : if (index >= 0) {
1570 1 : ent->mPendingQ.RemoveElementAt(index);
1571 1 : nsHttpTransaction *temp = trans;
1572 1 : NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
1573 : }
1574 : }
1575 153 : trans->Close(reason);
1576 : }
1577 163 : NS_RELEASE(trans);
1578 163 : }
1579 :
1580 : void
1581 2989 : nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
1582 : {
1583 2989 : nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
1584 :
1585 2989 : LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
1586 :
1587 : // start by processing the queue identified by the given connection info.
1588 2989 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
1589 2989 : if (!(ent && ProcessPendingQForEntry(ent))) {
1590 : // if we reach here, it means that we couldn't dispatch a transaction
1591 : // for the specified connection info. walk the connection table...
1592 2989 : mCT.Enumerate(ProcessOneTransactionCB, this);
1593 : }
1594 :
1595 2989 : NS_RELEASE(ci);
1596 2989 : }
1597 :
1598 : void
1599 124 : nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
1600 : {
1601 124 : LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
1602 :
1603 : // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
1604 124 : mTimeOfNextWakeUp = LL_MAXUINT;
1605 :
1606 : // check canreuse() for all idle connections plus any active connections on
1607 : // connection entries that are using spdy.
1608 124 : if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
1609 1 : mCT.Enumerate(PruneDeadConnectionsCB, this);
1610 124 : }
1611 :
1612 : void
1613 123 : nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
1614 : {
1615 123 : LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
1616 :
1617 123 : mCT.Enumerate(ClosePersistentConnectionsCB, this);
1618 123 : }
1619 :
1620 : void
1621 2989 : nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
1622 : {
1623 2989 : LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
1624 :
1625 2989 : nsHttpConnection *conn = (nsHttpConnection *) param;
1626 :
1627 : //
1628 : // 1) remove the connection from the active list
1629 : // 2) if keep-alive, add connection to idle list
1630 : // 3) post event to process the pending transaction queue
1631 : //
1632 :
1633 : nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
1634 2989 : conn, nsnull);
1635 2989 : nsHttpConnectionInfo *ci = nsnull;
1636 :
1637 2989 : if (!ent) {
1638 : // this should never happen
1639 0 : LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
1640 0 : NS_ABORT_IF_FALSE(false, "no connection entry");
1641 0 : NS_ADDREF(ci = conn->ConnectionInfo());
1642 : }
1643 : else {
1644 2989 : NS_ADDREF(ci = ent->mConnInfo);
1645 :
1646 : // If the connection is in the active list, remove that entry
1647 : // and the reference held by the mActiveConns list.
1648 : // This is never the final reference on conn as the event context
1649 : // is also holding one that is released at the end of this function.
1650 :
1651 2989 : if (ent->mUsingSpdy) {
1652 : // Spdy connections aren't reused in the traditional HTTP way in
1653 : // the idleconns list, they are actively multplexed as active
1654 : // conns. Even when they have 0 transactions on them they are
1655 : // considered active connections. So when one is reclaimed it
1656 : // is really complete and is meant to be shut down and not
1657 : // reused.
1658 0 : conn->DontReuse();
1659 : }
1660 :
1661 2989 : if (ent->mActiveConns.RemoveElement(conn)) {
1662 2988 : nsHttpConnection *temp = conn;
1663 2988 : NS_RELEASE(temp);
1664 2988 : mNumActiveConns--;
1665 2988 : ConditionallyStopReadTimeoutTick();
1666 : }
1667 :
1668 2989 : if (conn->CanReuse()) {
1669 4 : LOG((" adding connection to idle list\n"));
1670 : // Keep The idle connection list sorted with the connections that
1671 : // have moved the largest data pipelines at the front because these
1672 : // connections have the largest cwnds on the server.
1673 :
1674 : // The linear search is ok here because the number of idleconns
1675 : // in a single entry is generally limited to a small number (i.e. 6)
1676 :
1677 : PRUint32 idx;
1678 4 : for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
1679 0 : nsHttpConnection *idleConn = ent->mIdleConns[idx];
1680 0 : if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
1681 0 : break;
1682 : }
1683 :
1684 4 : NS_ADDREF(conn);
1685 4 : ent->mIdleConns.InsertElementAt(idx, conn);
1686 4 : mNumIdleConns++;
1687 4 : conn->BeginIdleMonitoring();
1688 :
1689 : // If the added connection was first idle connection or has shortest
1690 : // time to live among the watched connections, pruning dead
1691 : // connections needs to be done when it can't be reused anymore.
1692 4 : PRUint32 timeToLive = conn->TimeToLive();
1693 4 : if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
1694 4 : PruneDeadConnectionsAfter(timeToLive);
1695 : }
1696 : else {
1697 2985 : LOG((" connection cannot be reused; closing connection\n"));
1698 : // make sure the connection is closed and release our reference.
1699 2985 : conn->Close(NS_ERROR_ABORT);
1700 : }
1701 : }
1702 :
1703 2989 : OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
1704 2989 : NS_RELEASE(conn);
1705 2989 : }
1706 :
1707 : void
1708 0 : nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
1709 : {
1710 0 : PRUint16 name = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
1711 0 : PRUint16 value = NS_PTR_TO_INT32(param) & 0x0000FFFF;
1712 :
1713 0 : switch (name) {
1714 : case MAX_CONNECTIONS:
1715 0 : mMaxConns = value;
1716 0 : break;
1717 : case MAX_CONNECTIONS_PER_HOST:
1718 0 : mMaxConnsPerHost = value;
1719 0 : break;
1720 : case MAX_CONNECTIONS_PER_PROXY:
1721 0 : mMaxConnsPerProxy = value;
1722 0 : break;
1723 : case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
1724 0 : mMaxPersistConnsPerHost = value;
1725 0 : break;
1726 : case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
1727 0 : mMaxPersistConnsPerProxy = value;
1728 0 : break;
1729 : case MAX_REQUEST_DELAY:
1730 0 : mMaxRequestDelay = value;
1731 0 : break;
1732 : case MAX_PIPELINED_REQUESTS:
1733 0 : mMaxPipelinedRequests = value;
1734 0 : break;
1735 : default:
1736 0 : NS_NOTREACHED("unexpected parameter name");
1737 : }
1738 0 : }
1739 :
1740 : // nsHttpConnectionMgr::nsConnectionEntry
1741 582 : nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
1742 : {
1743 291 : if (mSpdyPreferred)
1744 0 : gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
1745 :
1746 291 : NS_RELEASE(mConnInfo);
1747 291 : }
1748 :
1749 : // Read Timeout Tick handlers
1750 :
1751 : void
1752 2988 : nsHttpConnectionMgr::ActivateTimeoutTick()
1753 : {
1754 2988 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1755 2988 : LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
1756 : "this=%p mReadTimeoutTick=%p\n"));
1757 :
1758 : // right now the spdy timeout code is the only thing hooked to the timeout
1759 : // tick, so disable it if spdy is not being used. However pipelining code
1760 : // will also want this functionality soon.
1761 2988 : if (!gHttpHandler->IsSpdyEnabled())
1762 0 : return;
1763 :
1764 : // The timer tick should be enabled if it is not already pending.
1765 : // Upon running the tick will rearm itself if there are active
1766 : // connections available.
1767 :
1768 2988 : if (mReadTimeoutTick && mReadTimeoutTickArmed)
1769 248 : return;
1770 :
1771 2740 : if (!mReadTimeoutTick) {
1772 259 : mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
1773 259 : if (!mReadTimeoutTick) {
1774 0 : NS_WARNING("failed to create timer for http timeout management");
1775 0 : return;
1776 : }
1777 259 : mReadTimeoutTick->SetTarget(mSocketThreadTarget);
1778 : }
1779 :
1780 2740 : NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
1781 2740 : mReadTimeoutTickArmed = true;
1782 : // pipeline will expect a 1000ms granuality
1783 2740 : mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
1784 : }
1785 :
1786 : void
1787 1 : nsHttpConnectionMgr::ReadTimeoutTick()
1788 : {
1789 1 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1790 1 : NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
1791 :
1792 1 : LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
1793 : mNumActiveConns));
1794 :
1795 1 : mCT.Enumerate(ReadTimeoutTickCB, this);
1796 1 : }
1797 :
1798 : PLDHashOperator
1799 1 : nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
1800 : nsAutoPtr<nsConnectionEntry> &ent,
1801 : void *closure)
1802 : {
1803 1 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
1804 :
1805 1 : LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
1806 : self, ent->mConnInfo->Host()));
1807 :
1808 1 : PRIntervalTime now = PR_IntervalNow();
1809 2 : for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
1810 1 : ent->mActiveConns[index]->ReadTimeoutTick(now);
1811 :
1812 1 : return PL_DHASH_NEXT;
1813 : }
1814 :
1815 : //-----------------------------------------------------------------------------
1816 : // nsHttpConnectionMgr::nsConnectionHandle
1817 :
1818 5976 : nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
1819 : {
1820 2988 : if (mConn) {
1821 2988 : gHttpHandler->ReclaimConnection(mConn);
1822 2988 : NS_RELEASE(mConn);
1823 : }
1824 11952 : }
1825 :
1826 12020 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
1827 :
1828 : nsresult
1829 2832 : nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
1830 : nsHttpRequestHead *req,
1831 : nsHttpResponseHead *resp,
1832 : bool *reset)
1833 : {
1834 2832 : return mConn->OnHeadersAvailable(trans, req, resp, reset);
1835 : }
1836 :
1837 : nsresult
1838 0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
1839 : {
1840 0 : return mConn->ResumeSend();
1841 : }
1842 :
1843 : nsresult
1844 0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
1845 : {
1846 0 : return mConn->ResumeRecv();
1847 : }
1848 :
1849 : void
1850 10 : nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
1851 : {
1852 10 : mConn->CloseTransaction(trans, reason);
1853 10 : }
1854 :
1855 : void
1856 0 : nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
1857 : {
1858 0 : mConn->GetConnectionInfo(result);
1859 0 : }
1860 :
1861 : nsresult
1862 0 : nsHttpConnectionMgr::
1863 : nsConnectionHandle::TakeTransport(nsISocketTransport **aTransport,
1864 : nsIAsyncInputStream **aInputStream,
1865 : nsIAsyncOutputStream **aOutputStream)
1866 : {
1867 0 : return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
1868 : }
1869 :
1870 : void
1871 2988 : nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
1872 : {
1873 2988 : mConn->GetSecurityInfo(result);
1874 2988 : }
1875 :
1876 : bool
1877 5558 : nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
1878 : {
1879 5558 : return mConn->IsPersistent();
1880 : }
1881 :
1882 : bool
1883 2988 : nsHttpConnectionMgr::nsConnectionHandle::IsReused()
1884 : {
1885 2988 : return mConn->IsReused();
1886 : }
1887 :
1888 : nsresult
1889 0 : nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
1890 : {
1891 0 : return mConn->PushBack(buf, bufLen);
1892 : }
1893 :
1894 :
1895 : //////////////////////// nsHalfOpenSocket
1896 :
1897 :
1898 89391 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
1899 : nsIOutputStreamCallback,
1900 : nsITransportEventSink,
1901 : nsIInterfaceRequestor,
1902 : nsITimerCallback)
1903 :
1904 2990 : nsHttpConnectionMgr::
1905 : nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
1906 : nsHttpTransaction *trans)
1907 : : mEnt(ent),
1908 2990 : mTransaction(trans)
1909 : {
1910 2990 : NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
1911 2990 : LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
1912 : this, trans, ent->mConnInfo->Host()));
1913 2990 : }
1914 :
1915 5980 : nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
1916 : {
1917 2990 : NS_ABORT_IF_FALSE(!mStreamOut, "streamout not null");
1918 2990 : NS_ABORT_IF_FALSE(!mBackupStreamOut, "backupstreamout not null");
1919 2990 : NS_ABORT_IF_FALSE(!mSynTimer, "syntimer not null");
1920 2990 : LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
1921 :
1922 2990 : if (mEnt) {
1923 : // A failure to create the transport object at all
1924 : // will result in this not being present in the halfopen table
1925 : // so ignore failures of RemoveElement()
1926 2990 : mEnt->mHalfOpens.RemoveElement(this);
1927 : }
1928 2990 : }
1929 :
1930 : nsresult
1931 2990 : nsHttpConnectionMgr::
1932 : nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
1933 : nsIAsyncInputStream **instream,
1934 : nsIAsyncOutputStream **outstream,
1935 : bool isBackup)
1936 : {
1937 : nsresult rv;
1938 :
1939 : const char* types[1];
1940 2990 : types[0] = (mEnt->mConnInfo->UsingSSL()) ?
1941 2990 : "ssl" : gHttpHandler->DefaultSocketType();
1942 2990 : PRUint32 typeCount = (types[0] != nsnull);
1943 :
1944 5980 : nsCOMPtr<nsISocketTransport> socketTransport;
1945 5980 : nsCOMPtr<nsISocketTransportService> sts;
1946 :
1947 2990 : sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1948 2990 : NS_ENSURE_SUCCESS(rv, rv);
1949 :
1950 2990 : rv = sts->CreateTransport(types, typeCount,
1951 2990 : nsDependentCString(mEnt->mConnInfo->Host()),
1952 : mEnt->mConnInfo->Port(),
1953 2990 : mEnt->mConnInfo->ProxyInfo(),
1954 8970 : getter_AddRefs(socketTransport));
1955 2990 : NS_ENSURE_SUCCESS(rv, rv);
1956 :
1957 2989 : PRUint32 tmpFlags = 0;
1958 2989 : if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
1959 1936 : tmpFlags = nsISocketTransport::BYPASS_CACHE;
1960 :
1961 2989 : if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
1962 1 : tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
1963 :
1964 : // For backup connections, we disable IPv6. That's because some users have
1965 : // broken IPv6 connectivity (leading to very long timeouts), and disabling
1966 : // IPv6 on the backup connection gives them a much better user experience
1967 : // with dual-stack hosts, though they still pay the 250ms delay for each new
1968 : // connection. This strategy is also known as "happy eyeballs".
1969 2989 : if (isBackup && gHttpHandler->FastFallbackToIPv4())
1970 0 : tmpFlags |= nsISocketTransport::DISABLE_IPV6;
1971 :
1972 2989 : socketTransport->SetConnectionFlags(tmpFlags);
1973 :
1974 2989 : socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
1975 :
1976 2989 : rv = socketTransport->SetEventSink(this, nsnull);
1977 2989 : NS_ENSURE_SUCCESS(rv, rv);
1978 :
1979 2989 : rv = socketTransport->SetSecurityCallbacks(this);
1980 2989 : NS_ENSURE_SUCCESS(rv, rv);
1981 :
1982 5978 : nsCOMPtr<nsIOutputStream> sout;
1983 2989 : rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
1984 : 0, 0,
1985 2989 : getter_AddRefs(sout));
1986 2989 : NS_ENSURE_SUCCESS(rv, rv);
1987 :
1988 5978 : nsCOMPtr<nsIInputStream> sin;
1989 2989 : rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
1990 : 0, 0,
1991 2989 : getter_AddRefs(sin));
1992 2989 : NS_ENSURE_SUCCESS(rv, rv);
1993 :
1994 2989 : socketTransport.forget(transport);
1995 2989 : CallQueryInterface(sin, instream);
1996 2989 : CallQueryInterface(sout, outstream);
1997 :
1998 2989 : rv = (*outstream)->AsyncWait(this, 0, 0, nsnull);
1999 2989 : if (NS_SUCCEEDED(rv))
2000 2989 : gHttpHandler->ConnMgr()->StartedConnect();
2001 :
2002 2989 : return rv;
2003 : }
2004 :
2005 : nsresult
2006 2990 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
2007 : {
2008 2990 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2009 :
2010 : nsresult rv;
2011 :
2012 2990 : rv = SetupStreams(getter_AddRefs(mSocketTransport),
2013 2990 : getter_AddRefs(mStreamIn),
2014 2990 : getter_AddRefs(mStreamOut),
2015 2990 : false);
2016 2990 : LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
2017 : this, mEnt->mConnInfo->Host(), rv));
2018 2990 : if (NS_FAILED(rv)) {
2019 1 : if (mStreamOut)
2020 0 : mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2021 1 : mStreamOut = nsnull;
2022 1 : mStreamIn = nsnull;
2023 1 : mSocketTransport = nsnull;
2024 : }
2025 2990 : return rv;
2026 : }
2027 :
2028 : nsresult
2029 0 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
2030 : {
2031 0 : nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
2032 0 : getter_AddRefs(mBackupStreamIn),
2033 0 : getter_AddRefs(mBackupStreamOut),
2034 0 : true);
2035 0 : LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
2036 : this, mEnt->mConnInfo->Host(), rv));
2037 0 : if (NS_FAILED(rv)) {
2038 0 : if (mBackupStreamOut)
2039 0 : mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2040 0 : mBackupStreamOut = nsnull;
2041 0 : mBackupStreamIn = nsnull;
2042 0 : mBackupTransport = nsnull;
2043 : }
2044 0 : return rv;
2045 : }
2046 :
2047 : void
2048 2982 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
2049 : {
2050 2982 : PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
2051 2982 : NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
2052 :
2053 2982 : if (timeout) {
2054 : // Setup the timer that will establish a backup socket
2055 : // if we do not get a writable event on the main one.
2056 : // We do this because a lost SYN takes a very long time
2057 : // to repair at the TCP level.
2058 : //
2059 : // Failure to setup the timer is something we can live with,
2060 : // so don't return an error in that case.
2061 : nsresult rv;
2062 2982 : mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
2063 2982 : if (NS_SUCCEEDED(rv)) {
2064 2982 : mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
2065 2982 : LOG(("nsHalfOpenSocket::SetupBackupTimer()"));
2066 : }
2067 : }
2068 2982 : }
2069 :
2070 : void
2071 5837 : nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
2072 : {
2073 : // If the syntimer is still armed, we can cancel it because no backup
2074 : // socket should be formed at this point
2075 5837 : if (!mSynTimer)
2076 2855 : return;
2077 :
2078 2982 : LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
2079 2982 : mSynTimer->Cancel();
2080 2982 : mSynTimer = nsnull;
2081 : }
2082 :
2083 : void
2084 0 : nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
2085 : {
2086 0 : LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
2087 : this, mEnt->mConnInfo->Host()));
2088 :
2089 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2090 :
2091 0 : nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
2092 :
2093 0 : if (mStreamOut) {
2094 0 : gHttpHandler->ConnMgr()->RecvdConnect();
2095 0 : mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2096 0 : mStreamOut = nsnull;
2097 : }
2098 0 : if (mBackupStreamOut) {
2099 0 : gHttpHandler->ConnMgr()->RecvdConnect();
2100 0 : mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2101 0 : mBackupStreamOut = nsnull;
2102 : }
2103 :
2104 0 : CancelBackupTimer();
2105 :
2106 0 : mEnt = nsnull;
2107 0 : }
2108 :
2109 : NS_IMETHODIMP // method for nsITimerCallback
2110 0 : nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
2111 : {
2112 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2113 0 : NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
2114 :
2115 0 : if (!gHttpHandler->ConnMgr()->
2116 0 : AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
2117 0 : SetupBackupStreams();
2118 : }
2119 :
2120 0 : mSynTimer = nsnull;
2121 0 : return NS_OK;
2122 : }
2123 :
2124 : // method for nsIAsyncOutputStreamCallback
2125 : NS_IMETHODIMP
2126 2989 : nsHttpConnectionMgr::
2127 : nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
2128 : {
2129 2989 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2130 2989 : NS_ABORT_IF_FALSE(out == mStreamOut ||
2131 : out == mBackupStreamOut, "stream mismatch");
2132 2989 : LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
2133 : this, mEnt->mConnInfo->Host(),
2134 : out == mStreamOut ? "primary" : "backup"));
2135 : PRInt32 index;
2136 : nsresult rv;
2137 :
2138 2989 : gHttpHandler->ConnMgr()->RecvdConnect();
2139 :
2140 2989 : CancelBackupTimer();
2141 :
2142 : // assign the new socket to the http connection
2143 5978 : nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
2144 2989 : LOG(("nsHalfOpenSocket::OnOutputStreamReady "
2145 : "Created new nshttpconnection %p\n", conn.get()));
2146 :
2147 5978 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2148 5978 : nsCOMPtr<nsIEventTarget> callbackTarget;
2149 5978 : mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
2150 5978 : getter_AddRefs(callbackTarget));
2151 2989 : if (out == mStreamOut) {
2152 : rv = conn->Init(mEnt->mConnInfo,
2153 2989 : gHttpHandler->ConnMgr()->mMaxRequestDelay,
2154 : mSocketTransport, mStreamIn, mStreamOut,
2155 5978 : callbacks, callbackTarget);
2156 :
2157 : // The nsHttpConnection object now owns these streams and sockets
2158 2989 : mStreamOut = nsnull;
2159 2989 : mStreamIn = nsnull;
2160 2989 : mSocketTransport = nsnull;
2161 : }
2162 : else {
2163 : rv = conn->Init(mEnt->mConnInfo,
2164 0 : gHttpHandler->ConnMgr()->mMaxRequestDelay,
2165 : mBackupTransport, mBackupStreamIn, mBackupStreamOut,
2166 0 : callbacks, callbackTarget);
2167 :
2168 : // The nsHttpConnection object now owns these streams and sockets
2169 0 : mBackupStreamOut = nsnull;
2170 0 : mBackupStreamIn = nsnull;
2171 0 : mBackupTransport = nsnull;
2172 : }
2173 :
2174 2989 : if (NS_FAILED(rv)) {
2175 0 : LOG(("nsHalfOpenSocket::OnOutputStreamReady "
2176 : "conn->init (%p) failed %x\n", conn.get(), rv));
2177 0 : return rv;
2178 : }
2179 :
2180 : // if this is still in the pending list, remove it and dispatch it
2181 2989 : index = mEnt->mPendingQ.IndexOf(mTransaction);
2182 2989 : if (index != -1) {
2183 2988 : mEnt->mPendingQ.RemoveElementAt(index);
2184 2988 : nsHttpTransaction *temp = mTransaction;
2185 2988 : NS_RELEASE(temp);
2186 2988 : gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
2187 : rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
2188 2988 : mTransaction->Caps(),
2189 5976 : conn);
2190 : }
2191 : else {
2192 : // this transaction was dispatched off the pending q before all the
2193 : // sockets established themselves.
2194 :
2195 : // We need to establish a small non-zero idle timeout so the connection
2196 : // mgr perceives this socket as suitable for persistent connection reuse
2197 1 : const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
2198 1 : if (k5Sec < gHttpHandler->IdleTimeout())
2199 1 : conn->SetIdleTimeout(k5Sec);
2200 : else
2201 0 : conn->SetIdleTimeout(gHttpHandler->IdleTimeout());
2202 :
2203 : // After about 1 second allow for the possibility of restarting a
2204 : // transaction due to server close. Keep at sub 1 second as that is the
2205 : // minimum granularity we can expect a server to be timing out with.
2206 1 : conn->SetIsReusedAfter(950);
2207 :
2208 2 : nsRefPtr<nsHttpConnection> copy(conn); // because onmsg*() expects to drop a reference
2209 1 : gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn.forget().get());
2210 : }
2211 :
2212 2989 : return rv;
2213 : }
2214 :
2215 : // method for nsITransportEventSink
2216 : NS_IMETHODIMP
2217 11808 : nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
2218 : nsresult status,
2219 : PRUint64 progress,
2220 : PRUint64 progressMax)
2221 : {
2222 11808 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2223 :
2224 11808 : if (mTransaction)
2225 11808 : mTransaction->OnTransportStatus(trans, status, progress);
2226 :
2227 11808 : if (trans != mSocketTransport)
2228 0 : return NS_OK;
2229 :
2230 : // if we are doing spdy coalescing and haven't recorded the ip address
2231 : // for this entry before then make the hash key if our dns lookup
2232 : // just completed
2233 :
2234 20360 : if (status == nsISocketTransport::STATUS_CONNECTED_TO &&
2235 2848 : gHttpHandler->IsSpdyEnabled() &&
2236 2848 : gHttpHandler->CoalesceSpdy() &&
2237 2848 : mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
2238 4 : !mEnt->mConnInfo->UsingHttpProxy() &&
2239 4 : mEnt->mCoalescingKey.IsEmpty()) {
2240 :
2241 : PRNetAddr addr;
2242 4 : nsresult rv = mSocketTransport->GetPeerAddr(&addr);
2243 4 : if (NS_SUCCEEDED(rv)) {
2244 4 : mEnt->mCoalescingKey.SetCapacity(72);
2245 4 : PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64);
2246 : mEnt->mCoalescingKey.SetLength(
2247 4 : strlen(mEnt->mCoalescingKey.BeginReading()));
2248 :
2249 4 : if (mEnt->mConnInfo->GetAnonymous())
2250 0 : mEnt->mCoalescingKey.AppendLiteral("~A:");
2251 : else
2252 4 : mEnt->mCoalescingKey.AppendLiteral("~.:");
2253 4 : mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
2254 :
2255 4 : LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
2256 : "STATUS_CONNECTED_TO Established New Coalescing Key for host "
2257 : "%s [%s]", mEnt->mConnInfo->Host(),
2258 : mEnt->mCoalescingKey.get()));
2259 :
2260 4 : gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
2261 : }
2262 : }
2263 :
2264 11808 : switch (status) {
2265 : case nsISocketTransport::STATUS_CONNECTING_TO:
2266 : // Passed DNS resolution, now trying to connect, start the backup timer
2267 : // only prevent creating another backup transport.
2268 : // We also check for mEnt presence to not instantiate the timer after
2269 : // this half open socket has already been abandoned. It may happen
2270 : // when we get this notification right between main-thread calls to
2271 : // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
2272 : // where the first abandones all half open socket instances and only
2273 : // after that the second stops the socket thread.
2274 2982 : if (mEnt && !mBackupTransport && !mSynTimer)
2275 2982 : SetupBackupTimer();
2276 2982 : break;
2277 :
2278 : case nsISocketTransport::STATUS_CONNECTED_TO:
2279 : // TCP connection's up, now transfer or SSL negotiantion starts,
2280 : // no need for backup socket
2281 2848 : CancelBackupTimer();
2282 2848 : break;
2283 :
2284 : default:
2285 5978 : break;
2286 : }
2287 :
2288 11808 : return NS_OK;
2289 : }
2290 :
2291 : // method for nsIInterfaceRequestor
2292 : NS_IMETHODIMP
2293 0 : nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
2294 : void **result)
2295 : {
2296 0 : if (mTransaction) {
2297 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2298 0 : mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull);
2299 0 : if (callbacks)
2300 0 : return callbacks->GetInterface(iid, result);
2301 : }
2302 0 : return NS_ERROR_NO_INTERFACE;
2303 : }
2304 :
2305 :
2306 : nsHttpConnection *
2307 0 : nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
2308 : {
2309 : // return our connection object to the caller and clear it internally
2310 : // do not drop our reference - the caller now owns it.
2311 :
2312 0 : NS_ASSERTION(mConn, "no connection");
2313 0 : nsHttpConnection *conn = mConn;
2314 0 : mConn = nsnull;
2315 0 : return conn;
2316 : }
2317 :
2318 : bool
2319 2843 : nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
2320 : {
2321 2843 : return mConn->LastTransactionExpectedNoContent();
2322 : }
2323 :
2324 : void
2325 2832 : nsHttpConnectionMgr::
2326 : nsConnectionHandle::SetLastTransactionExpectedNoContent(bool val)
2327 : {
2328 2832 : mConn->SetLastTransactionExpectedNoContent(val);
2329 2832 : }
2330 :
2331 : nsISocketTransport *
2332 0 : nsHttpConnectionMgr::nsConnectionHandle::Transport()
2333 : {
2334 0 : if (!mConn)
2335 0 : return nsnull;
2336 0 : return mConn->Transport();
2337 : }
|