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 20940 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
66 :
67 : static void
68 2977 : 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 2977 : for (PRInt32 i=pendingQ.Length()-1; i>=0; --i) {
75 108 : nsHttpTransaction *t = pendingQ[i];
76 108 : if (trans->Priority() >= t->Priority()) {
77 108 : pendingQ.InsertElementAt(i+1, trans);
78 108 : return;
79 : }
80 : }
81 2869 : pendingQ.InsertElementAt(0, trans);
82 : }
83 :
84 : //-----------------------------------------------------------------------------
85 :
86 679 : 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 679 : , mReadTimeoutTickArmed(false)
99 : {
100 679 : LOG(("Creating nsHttpConnectionMgr @%x\n", this));
101 679 : mCT.Init();
102 679 : mAlternateProtocolHash.Init(16);
103 679 : mSpdyPreferredHash.Init();
104 679 : }
105 :
106 2031 : nsHttpConnectionMgr::~nsHttpConnectionMgr()
107 : {
108 677 : LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
109 677 : if (mReadTimeoutTick)
110 0 : mReadTimeoutTick->Cancel();
111 2708 : }
112 :
113 : nsresult
114 7732 : nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
115 : {
116 : nsresult rv;
117 15464 : nsCOMPtr<nsIEventTarget> sts;
118 15464 : nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
119 7732 : if (NS_SUCCEEDED(rv)) {
120 7732 : bool offline = true;
121 7732 : ioService->GetOffline(&offline);
122 :
123 7732 : if (!offline) {
124 7718 : sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
125 : }
126 : }
127 :
128 15464 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
129 :
130 : // do nothing if already initialized or if we've shut down
131 7732 : if (mSocketThreadTarget || mIsShuttingDown)
132 7052 : return NS_OK;
133 :
134 680 : mSocketThreadTarget = sts;
135 :
136 680 : return rv;
137 : }
138 :
139 : nsresult
140 679 : nsHttpConnectionMgr::Init(PRUint16 maxConns,
141 : PRUint16 maxConnsPerHost,
142 : PRUint16 maxConnsPerProxy,
143 : PRUint16 maxPersistConnsPerHost,
144 : PRUint16 maxPersistConnsPerProxy,
145 : PRUint16 maxRequestDelay,
146 : PRUint16 maxPipelinedRequests)
147 : {
148 679 : LOG(("nsHttpConnectionMgr::Init\n"));
149 :
150 : {
151 1358 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
152 :
153 679 : mMaxConns = maxConns;
154 679 : mMaxConnsPerHost = maxConnsPerHost;
155 679 : mMaxConnsPerProxy = maxConnsPerProxy;
156 679 : mMaxPersistConnsPerHost = maxPersistConnsPerHost;
157 679 : mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
158 679 : mMaxRequestDelay = maxRequestDelay;
159 679 : mMaxPipelinedRequests = maxPipelinedRequests;
160 :
161 679 : mIsShuttingDown = false;
162 : }
163 :
164 679 : return EnsureSocketThreadTargetIfOnline();
165 : }
166 :
167 : nsresult
168 1888 : nsHttpConnectionMgr::Shutdown()
169 : {
170 1888 : LOG(("nsHttpConnectionMgr::Shutdown\n"));
171 :
172 3776 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
173 :
174 : // do nothing if already shutdown
175 1888 : if (!mSocketThreadTarget)
176 1210 : return NS_OK;
177 :
178 678 : 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 678 : mIsShuttingDown = true;
184 678 : mSocketThreadTarget = 0;
185 :
186 678 : 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 678 : mon.Wait();
193 678 : return NS_OK;
194 : }
195 :
196 : nsresult
197 7053 : 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 7053 : EnsureSocketThreadTargetIfOnline();
204 :
205 14106 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
206 :
207 : nsresult rv;
208 7053 : if (!mSocketThreadTarget) {
209 12 : NS_WARNING("cannot post event if not initialized");
210 12 : rv = NS_ERROR_NOT_INITIALIZED;
211 : }
212 : else {
213 14082 : nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
214 7041 : if (!event)
215 0 : rv = NS_ERROR_OUT_OF_MEMORY;
216 : else
217 7041 : rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
218 : }
219 7053 : return rv;
220 : }
221 :
222 : void
223 6 : nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
224 : {
225 6 : LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
226 :
227 6 : if(!mTimer)
228 5 : 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 6 : if (mTimer) {
233 6 : mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
234 6 : 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 6 : }
239 :
240 : void
241 293 : nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
242 : {
243 : // Leave the timer in place if there are connections that potentially
244 : // need management
245 293 : if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
246 7 : return;
247 :
248 286 : LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
249 :
250 : // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
251 286 : mTimeOfNextWakeUp = LL_MAXUINT;
252 286 : if (mTimer) {
253 5 : mTimer->Cancel();
254 5 : mTimer = NULL;
255 : }
256 : }
257 :
258 : void
259 5951 : nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick()
260 : {
261 5951 : LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick "
262 : "armed=%d active=%d\n", mReadTimeoutTickArmed, mNumActiveConns));
263 :
264 5951 : if (!mReadTimeoutTickArmed)
265 2725 : return;
266 :
267 3226 : if (mNumActiveConns)
268 483 : return;
269 :
270 2743 : LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick stop==true\n"));
271 :
272 2743 : mReadTimeoutTick->Cancel();
273 2743 : mReadTimeoutTickArmed = false;
274 : }
275 :
276 : //-----------------------------------------------------------------------------
277 : // nsHttpConnectionMgr::nsIObserver
278 : //-----------------------------------------------------------------------------
279 :
280 : NS_IMETHODIMP
281 2 : nsHttpConnectionMgr::Observe(nsISupports *subject,
282 : const char *topic,
283 : const PRUnichar *data)
284 : {
285 2 : LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
286 :
287 2 : if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
288 4 : nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
289 2 : if (timer == mTimer) {
290 2 : PruneDeadConnections();
291 : }
292 0 : else if (timer == mReadTimeoutTick) {
293 0 : 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 2 : return NS_OK;
303 : }
304 :
305 :
306 : //-----------------------------------------------------------------------------
307 :
308 : nsresult
309 2989 : nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
310 : {
311 2989 : LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
312 :
313 2989 : NS_ADDREF(trans);
314 2989 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
315 2989 : if (NS_FAILED(rv))
316 12 : NS_RELEASE(trans);
317 2989 : 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 162 : nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
334 : {
335 162 : LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
336 :
337 162 : NS_ADDREF(trans);
338 162 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
339 162 : if (NS_FAILED(rv))
340 0 : NS_RELEASE(trans);
341 162 : return rv;
342 : }
343 :
344 : nsresult
345 126 : nsHttpConnectionMgr::PruneDeadConnections()
346 : {
347 126 : 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 2975 : nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
401 : {
402 2975 : LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
403 :
404 2975 : NS_ADDREF(conn);
405 2975 : nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
406 2975 : if (NS_FAILED(rv))
407 0 : NS_RELEASE(conn);
408 2975 : 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 6104 : nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
437 : nsHttpConnection *conn,
438 : nsHttpTransaction *trans)
439 : {
440 6104 : if (!ci)
441 0 : return nsnull;
442 :
443 6104 : 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 6104 : if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
448 6104 : 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 2975 : nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
505 : bool usingSpdy)
506 : {
507 2975 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
508 :
509 : nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
510 2975 : conn, nsnull);
511 :
512 2975 : NS_ABORT_IF_FALSE(ent, "no connection entry");
513 2975 : if (!ent)
514 0 : return;
515 :
516 2975 : ent->mTestedSpdy = true;
517 :
518 2975 : if (!usingSpdy)
519 2975 : 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 5047 : nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
566 : {
567 5047 : 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 10094 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
573 :
574 5047 : 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 12249 : nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
632 : {
633 36747 : if (!gHttpHandler->IsSpdyEnabled() ||
634 12249 : !gHttpHandler->CoalesceSpdy() ||
635 12249 : aOriginalEntry->mCoalescingKey.IsEmpty())
636 12213 : 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 3233 : nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
737 : nsAutoPtr<nsConnectionEntry> &ent,
738 : void *closure)
739 : {
740 3233 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
741 :
742 3233 : if (self->ProcessPendingQForEntry(ent))
743 0 : return PL_DHASH_STOP;
744 :
745 3233 : 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 3 : nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
775 : nsAutoPtr<nsConnectionEntry> &ent,
776 : void *closure)
777 : {
778 3 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
779 :
780 3 : 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 3 : PRUint32 timeToNextExpire = PR_UINT32_MAX;
785 3 : PRInt32 count = ent->mIdleConns.Length();
786 3 : if (count > 0) {
787 4 : for (PRInt32 i=count-1; i>=0; --i) {
788 2 : nsHttpConnection *conn = ent->mIdleConns[i];
789 2 : if (!conn->CanReuse()) {
790 1 : ent->mIdleConns.RemoveElementAt(i);
791 1 : conn->Close(NS_ERROR_ABORT);
792 1 : NS_RELEASE(conn);
793 1 : self->mNumIdleConns--;
794 : } else {
795 1 : timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
796 : }
797 : }
798 : }
799 :
800 3 : 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 3 : if (timeToNextExpire != PR_UINT32_MAX) {
820 1 : PRUint32 now = NowInSeconds();
821 1 : 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 1 : if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
827 1 : self->PruneDeadConnectionsAfter(timeToNextExpire);
828 : }
829 : } else {
830 2 : self->ConditionallyStopPruneDeadConnectionsTimer();
831 : }
832 : #ifdef DEBUG
833 3 : count = ent->mActiveConns.Length();
834 3 : 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 10 : if (ent->mIdleConns.Length() == 0 &&
844 2 : ent->mActiveConns.Length() == 0 &&
845 1 : ent->mHalfOpens.Length() == 0 &&
846 1 : ent->mPendingQ.Length() == 0 &&
847 1 : ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
848 1 : !gHttpHandler->IsSpdyEnabled() ||
849 1 : 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 3 : ent->mIdleConns.Compact();
856 3 : ent->mActiveConns.Compact();
857 3 : ent->mPendingQ.Compact();
858 :
859 3 : 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 6209 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
918 : {
919 6209 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
920 6209 : LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
921 : ent->mConnInfo->HashKey().get()));
922 :
923 6209 : ProcessSpdyPendingQ(ent);
924 :
925 6209 : PRUint32 i, count = ent->mPendingQ.Length();
926 6209 : if (count > 0) {
927 62 : LOG((" pending-count=%u\n", count));
928 62 : nsHttpTransaction *trans = nsnull;
929 62 : nsHttpConnection *conn = nsnull;
930 144 : for (i = 0; i < count; ++i) {
931 82 : 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 82 : bool alreadyHalfOpen = false;
939 110 : for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
940 110 : if (ent->mHalfOpens[j]->Transaction() == trans) {
941 82 : alreadyHalfOpen = true;
942 82 : break;
943 : }
944 : }
945 :
946 82 : GetConnection(ent, trans, alreadyHalfOpen, &conn);
947 82 : if (conn)
948 0 : break;
949 :
950 82 : NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
951 : "something mutated pending queue from "
952 : "GetConnection()");
953 : }
954 62 : 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 6209 : 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 2977 : nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
984 : {
985 2977 : nsHttpConnectionInfo *ci = ent->mConnInfo;
986 :
987 2977 : 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 2977 : PRUint32 maxSocketCount = gHttpHandler->MaxSocketCount();
994 2977 : 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 2977 : if (mNumActiveConns >= mMaxConns) {
1003 0 : LOG((" num active conns == max conns\n"));
1004 0 : return true;
1005 : }
1006 :
1007 : nsHttpConnection *conn;
1008 2977 : PRInt32 i, totalCount, persistCount = 0;
1009 :
1010 2977 : totalCount = ent->mActiveConns.Length();
1011 :
1012 : // count the number of persistent connections
1013 3271 : for (i=0; i<totalCount; ++i) {
1014 294 : conn = ent->mActiveConns[i];
1015 294 : if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
1016 257 : persistCount++;
1017 : }
1018 :
1019 : // Add in the in-progress tcp connections, we will assume they are
1020 : // keepalive enabled.
1021 2977 : totalCount += ent->mHalfOpens.Length();
1022 2977 : persistCount += ent->mHalfOpens.Length();
1023 :
1024 2977 : LOG((" total=%d, persist=%d\n", totalCount, persistCount));
1025 :
1026 : PRUint16 maxConns;
1027 : PRUint16 maxPersistConns;
1028 :
1029 2977 : if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
1030 27 : maxConns = mMaxConnsPerProxy;
1031 27 : maxPersistConns = mMaxPersistConnsPerProxy;
1032 : }
1033 : else {
1034 2950 : maxConns = mMaxConnsPerHost;
1035 2950 : maxPersistConns = mMaxPersistConnsPerHost;
1036 : }
1037 :
1038 : // use >= just to be safe
1039 : return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
1040 2977 : (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 3059 : nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
1073 : nsHttpTransaction *trans,
1074 : bool onlyReusedConnection,
1075 : nsHttpConnection **result)
1076 : {
1077 3059 : 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 3059 : *result = nsnull;
1087 :
1088 3059 : nsHttpConnection *conn = nsnull;
1089 3059 : bool addConnToActiveList = true;
1090 :
1091 3059 : 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 3059 : if (gHttpHandler->IsSpdyEnabled()) {
1096 3059 : conn = GetSpdyPreferredConn(ent);
1097 3059 : 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 6118 : 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 3059 : if (!conn) {
1128 :
1129 : // If the onlyReusedConnection parameter is TRUE, then GetConnection()
1130 : // does not create new transports under any circumstances.
1131 3059 : if (onlyReusedConnection)
1132 82 : return;
1133 :
1134 5974 : if (gHttpHandler->IsSpdyEnabled() &&
1135 2977 : ent->mConnInfo->UsingSSL() &&
1136 4 : !ent->mConnInfo->UsingHttpProxy() &&
1137 4 : !(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
1138 4 : (!ent->mTestedSpdy || ent->mUsingSpdy) &&
1139 8 : (ent->mHalfOpens.Length() || ent->mActiveConns.Length())) {
1140 0 : bool restrictConnection = true;
1141 :
1142 : // There is a concern that a host is using a mix of HTTP/1 and SPDY.
1143 : // In that case we don't want to restrict connections just because
1144 : // there is a single active HTTP/1 session in use. Confirm that the
1145 : // restriction is due to a handshake in progress or a live spdy
1146 : // session.
1147 0 : if (!ent->mHalfOpens.Length() &&
1148 0 : ent->mUsingSpdy && ent->mActiveConns.Length()) {
1149 0 : bool confirmedRestrict = false;
1150 :
1151 0 : for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
1152 0 : nsHttpConnection *conn = ent->mActiveConns[index];
1153 0 : if (!conn->ReportedNPN() || conn->CanDirectlyActivate()) {
1154 0 : confirmedRestrict = true;
1155 0 : break; // confirmed;
1156 : }
1157 : }
1158 0 : if (!confirmedRestrict) {
1159 0 : LOG(("nsHttpConnectionMgr spdy connection restriction to "
1160 : "%s bypassed.\n", ent->mConnInfo->Host()));
1161 0 : restrictConnection = false;
1162 : }
1163 : }
1164 0 : if (restrictConnection)
1165 0 : return;
1166 : }
1167 :
1168 : // Check if we need to purge an idle connection. Note that we may have
1169 : // removed one above; if so, this will be a no-op. We do this before
1170 : // checking the active connection limit to catch the case where we do
1171 : // have an idle connection, but the purge timer hasn't fired yet.
1172 : // XXX this just purges a random idle connection. we should instead
1173 : // enumerate the entire hash table to find the eldest idle connection.
1174 2977 : if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
1175 0 : mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
1176 :
1177 : // Need to make a new TCP connection. First, we check if we've hit
1178 : // either the maximum connection limit globally or for this particular
1179 : // host or proxy. If we have, we're done.
1180 2977 : if (AtActiveConnectionLimit(ent, trans->Caps())) {
1181 0 : LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
1182 : "at active connection limit - will queue\n",
1183 : ent->mConnInfo->HashKey().get()));
1184 0 : return;
1185 : }
1186 :
1187 2977 : LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
1188 : "%s %s ent=%p spdy=%d",
1189 : ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
1190 : ent, ent->mUsingSpdy));
1191 :
1192 2977 : nsresult rv = CreateTransport(ent, trans);
1193 2977 : if (NS_FAILED(rv))
1194 1 : trans->Close(rv);
1195 2977 : return;
1196 : }
1197 :
1198 0 : if (addConnToActiveList) {
1199 : // hold an owning ref to this connection
1200 0 : ent->mActiveConns.AppendElement(conn);
1201 0 : mNumActiveConns++;
1202 : }
1203 :
1204 0 : NS_ADDREF(conn);
1205 0 : *result = conn;
1206 : }
1207 :
1208 : void
1209 2975 : nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
1210 : nsConnectionEntry *ent)
1211 : {
1212 2975 : NS_ADDREF(conn);
1213 2975 : ent->mActiveConns.AppendElement(conn);
1214 2975 : mNumActiveConns++;
1215 2975 : ActivateTimeoutTick();
1216 2975 : }
1217 :
1218 : void
1219 2976 : nsHttpConnectionMgr::StartedConnect()
1220 : {
1221 2976 : mNumActiveConns++;
1222 2976 : }
1223 :
1224 : void
1225 2976 : nsHttpConnectionMgr::RecvdConnect()
1226 : {
1227 2976 : mNumActiveConns--;
1228 2976 : ConditionallyStopReadTimeoutTick();
1229 2976 : }
1230 :
1231 : nsresult
1232 2977 : nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
1233 : nsHttpTransaction *trans)
1234 : {
1235 2977 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1236 :
1237 5954 : nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
1238 2977 : nsresult rv = sock->SetupPrimaryStreams();
1239 2977 : NS_ENSURE_SUCCESS(rv, rv);
1240 :
1241 2976 : ent->mHalfOpens.AppendElement(sock);
1242 2976 : return NS_OK;
1243 : }
1244 :
1245 : nsresult
1246 2975 : nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
1247 : nsHttpTransaction *aTrans,
1248 : PRUint8 caps,
1249 : nsHttpConnection *conn)
1250 : {
1251 2975 : LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
1252 : ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
1253 : nsresult rv;
1254 :
1255 2975 : PRInt32 priority = aTrans->Priority();
1256 :
1257 2975 : if (conn->UsingSpdy()) {
1258 0 : LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
1259 : "Connection host = %s\n",
1260 : aTrans->ConnectionInfo()->Host(),
1261 : conn->ConnectionInfo()->Host()));
1262 0 : rv = conn->Activate(aTrans, caps, priority);
1263 0 : NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
1264 0 : return rv;
1265 : }
1266 :
1267 2975 : nsConnectionHandle *handle = new nsConnectionHandle(conn);
1268 2975 : if (!handle)
1269 0 : return NS_ERROR_OUT_OF_MEMORY;
1270 2975 : NS_ADDREF(handle);
1271 :
1272 2975 : nsHttpPipeline *pipeline = nsnull;
1273 2975 : nsAHttpTransaction *trans = aTrans;
1274 :
1275 2975 : if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
1276 0 : LOG((" looking to build pipeline...\n"));
1277 0 : if (BuildPipeline(ent, trans, &pipeline))
1278 0 : trans = pipeline;
1279 : }
1280 :
1281 : // give the transaction the indirect reference to the connection.
1282 2975 : trans->SetConnection(handle);
1283 :
1284 2975 : rv = conn->Activate(trans, caps, priority);
1285 :
1286 2975 : if (NS_FAILED(rv)) {
1287 0 : LOG((" conn->Activate failed [rv=%x]\n", rv));
1288 0 : ent->mActiveConns.RemoveElement(conn);
1289 0 : mNumActiveConns--;
1290 0 : ConditionallyStopReadTimeoutTick();
1291 :
1292 : // sever back references to connection, and do so without triggering
1293 : // a call to ReclaimConnection ;-)
1294 0 : trans->SetConnection(nsnull);
1295 0 : NS_RELEASE(handle->mConn);
1296 : // destroy the connection
1297 0 : NS_RELEASE(conn);
1298 : }
1299 :
1300 : // if we were unable to activate the pipeline, then this will destroy
1301 : // the pipeline, which will cause each the transactions owned by the
1302 : // pipeline to be restarted.
1303 2975 : NS_IF_RELEASE(pipeline);
1304 :
1305 2975 : NS_RELEASE(handle);
1306 2975 : return rv;
1307 : }
1308 :
1309 : bool
1310 0 : nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
1311 : nsAHttpTransaction *firstTrans,
1312 : nsHttpPipeline **result)
1313 : {
1314 0 : if (mMaxPipelinedRequests < 2)
1315 0 : return false;
1316 :
1317 0 : nsHttpPipeline *pipeline = nsnull;
1318 : nsHttpTransaction *trans;
1319 :
1320 0 : PRUint32 i = 0, numAdded = 0;
1321 0 : while (i < ent->mPendingQ.Length()) {
1322 0 : trans = ent->mPendingQ[i];
1323 0 : if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
1324 0 : if (numAdded == 0) {
1325 0 : pipeline = new nsHttpPipeline;
1326 0 : if (!pipeline)
1327 0 : return false;
1328 0 : pipeline->AddTransaction(firstTrans);
1329 0 : numAdded = 1;
1330 : }
1331 0 : pipeline->AddTransaction(trans);
1332 :
1333 : // remove transaction from pending queue
1334 0 : ent->mPendingQ.RemoveElementAt(i);
1335 0 : NS_RELEASE(trans);
1336 :
1337 0 : if (++numAdded == mMaxPipelinedRequests)
1338 0 : break;
1339 : }
1340 : else
1341 0 : ++i; // skip to next pending transaction
1342 : }
1343 :
1344 0 : if (numAdded == 0)
1345 0 : return false;
1346 :
1347 0 : LOG((" pipelined %u transactions\n", numAdded));
1348 0 : NS_ADDREF(*result = pipeline);
1349 0 : return true;
1350 : }
1351 :
1352 : nsresult
1353 2977 : nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
1354 : {
1355 2977 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1356 :
1357 : // since "adds" and "cancels" are processed asynchronously and because
1358 : // various events might trigger an "add" directly on the socket thread,
1359 : // we must take care to avoid dispatching a transaction that has already
1360 : // been canceled (see bug 190001).
1361 2977 : if (NS_FAILED(trans->Status())) {
1362 0 : LOG((" transaction was canceled... dropping event!\n"));
1363 0 : return NS_OK;
1364 : }
1365 :
1366 2977 : PRUint8 caps = trans->Caps();
1367 2977 : nsHttpConnectionInfo *ci = trans->ConnectionInfo();
1368 2977 : NS_ASSERTION(ci, "no connection info");
1369 :
1370 2977 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
1371 2977 : if (!ent) {
1372 291 : nsHttpConnectionInfo *clone = ci->Clone();
1373 291 : if (!clone)
1374 0 : return NS_ERROR_OUT_OF_MEMORY;
1375 291 : ent = new nsConnectionEntry(clone);
1376 291 : if (!ent)
1377 0 : return NS_ERROR_OUT_OF_MEMORY;
1378 291 : mCT.Put(ci->HashKey(), ent);
1379 : }
1380 :
1381 : // SPDY coalescing of hostnames means we might redirect from this
1382 : // connection entry onto the preferred one.
1383 2977 : nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
1384 2977 : if (preferredEntry && (preferredEntry != ent)) {
1385 0 : LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
1386 : "redirected via coalescing from %s to %s\n", trans,
1387 : ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
1388 :
1389 0 : ent = preferredEntry;
1390 : }
1391 :
1392 : // If we are doing a force reload then close out any existing conns
1393 : // to this host so that changes in DNS, LBs, etc.. are reflected
1394 2977 : if (caps & NS_HTTP_CLEAR_KEEPALIVES)
1395 0 : ClosePersistentConnections(ent);
1396 :
1397 : // Check if the transaction already has a sticky reference to a connection.
1398 : // If so, then we can just use it directly by transferring its reference
1399 : // to the new connection var instead of calling GetConnection() to search
1400 : // for an available one.
1401 :
1402 2977 : nsAHttpConnection *wrappedConnection = trans->Connection();
1403 : nsHttpConnection *conn;
1404 2977 : conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
1405 :
1406 2977 : if (conn) {
1407 0 : NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
1408 :
1409 0 : trans->SetConnection(nsnull);
1410 : }
1411 : else
1412 2977 : GetConnection(ent, trans, false, &conn);
1413 :
1414 : nsresult rv;
1415 2977 : if (!conn) {
1416 2977 : LOG((" adding transaction to pending queue [trans=%x pending-count=%u]\n",
1417 : trans, ent->mPendingQ.Length()+1));
1418 : // put this transaction on the pending queue...
1419 2977 : InsertTransactionSorted(ent->mPendingQ, trans);
1420 2977 : NS_ADDREF(trans);
1421 2977 : rv = NS_OK;
1422 : }
1423 : else {
1424 0 : rv = DispatchTransaction(ent, trans, caps, conn);
1425 0 : NS_RELEASE(conn);
1426 : }
1427 :
1428 2977 : return rv;
1429 : }
1430 :
1431 : // This function tries to dispatch the pending spdy transactions on
1432 : // the connection entry sent in as an argument. It will do so on the
1433 : // active spdy connection either in that same entry or in the
1434 : // redirected 'preferred' entry for the same coalescing hash key if
1435 : // coalescing is enabled.
1436 :
1437 : void
1438 6213 : nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
1439 : {
1440 6213 : nsHttpConnection *conn = GetSpdyPreferredConn(ent);
1441 6213 : if (!conn)
1442 6213 : return;
1443 :
1444 0 : for (PRInt32 index = ent->mPendingQ.Length() - 1;
1445 0 : index >= 0 && conn->CanDirectlyActivate();
1446 : --index) {
1447 0 : nsHttpTransaction *trans = ent->mPendingQ[index];
1448 :
1449 0 : if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
1450 0 : trans->Caps() & NS_HTTP_DISALLOW_SPDY)
1451 0 : continue;
1452 :
1453 0 : ent->mPendingQ.RemoveElementAt(index);
1454 :
1455 0 : nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
1456 0 : if (NS_FAILED(rv)) {
1457 : // this cannot happen, but if due to some bug it does then
1458 : // close the transaction
1459 0 : NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
1460 0 : LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
1461 : trans));
1462 0 : trans->Close(rv);
1463 : }
1464 0 : NS_RELEASE(trans);
1465 : }
1466 : }
1467 :
1468 : PLDHashOperator
1469 0 : nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
1470 : nsAutoPtr<nsConnectionEntry> &ent,
1471 : void *closure)
1472 : {
1473 0 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
1474 0 : self->ProcessSpdyPendingQ(ent);
1475 0 : return PL_DHASH_NEXT;
1476 : }
1477 :
1478 : void
1479 0 : nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
1480 : {
1481 0 : mCT.Enumerate(ProcessSpdyPendingQCB, this);
1482 0 : }
1483 :
1484 : nsHttpConnection *
1485 9272 : nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
1486 : {
1487 9272 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1488 9272 : NS_ABORT_IF_FALSE(ent, "no connection entry");
1489 :
1490 9272 : nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
1491 :
1492 : // this entry is spdy-enabled if it is involved in a redirect
1493 9272 : if (preferred)
1494 : // all new connections for this entry will use spdy too
1495 0 : ent->mUsingSpdy = true;
1496 : else
1497 9272 : preferred = ent;
1498 :
1499 9272 : nsHttpConnection *conn = nsnull;
1500 :
1501 9272 : if (preferred->mUsingSpdy) {
1502 0 : for (PRUint32 index = 0;
1503 0 : index < preferred->mActiveConns.Length();
1504 : ++index) {
1505 0 : if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
1506 0 : conn = preferred->mActiveConns[index];
1507 0 : break;
1508 : }
1509 : }
1510 : }
1511 :
1512 9272 : return conn;
1513 : }
1514 :
1515 : //-----------------------------------------------------------------------------
1516 :
1517 : void
1518 678 : nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
1519 : {
1520 678 : LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
1521 :
1522 678 : mCT.Enumerate(ShutdownPassCB, this);
1523 :
1524 678 : if (mReadTimeoutTick) {
1525 260 : mReadTimeoutTick->Cancel();
1526 260 : mReadTimeoutTick = nsnull;
1527 260 : mReadTimeoutTickArmed = false;
1528 : }
1529 :
1530 : // signal shutdown complete
1531 1356 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
1532 678 : mon.Notify();
1533 678 : }
1534 :
1535 : void
1536 2977 : nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
1537 : {
1538 2977 : LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
1539 :
1540 2977 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1541 2977 : trans->SetPriority(priority);
1542 2977 : nsresult rv = ProcessNewTransaction(trans);
1543 2977 : if (NS_FAILED(rv))
1544 0 : trans->Close(rv); // for whatever its worth
1545 2977 : NS_RELEASE(trans);
1546 2977 : }
1547 :
1548 : void
1549 0 : nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
1550 : {
1551 0 : LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
1552 :
1553 0 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1554 0 : trans->SetPriority(priority);
1555 :
1556 : nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
1557 0 : nsnull, trans);
1558 :
1559 0 : if (ent) {
1560 0 : PRInt32 index = ent->mPendingQ.IndexOf(trans);
1561 0 : if (index >= 0) {
1562 0 : ent->mPendingQ.RemoveElementAt(index);
1563 0 : InsertTransactionSorted(ent->mPendingQ, trans);
1564 : }
1565 : }
1566 :
1567 0 : NS_RELEASE(trans);
1568 0 : }
1569 :
1570 : void
1571 162 : nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
1572 : {
1573 162 : LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
1574 :
1575 162 : nsHttpTransaction *trans = (nsHttpTransaction *) param;
1576 : //
1577 : // if the transaction owns a connection and the transaction is not done,
1578 : // then ask the connection to close the transaction. otherwise, close the
1579 : // transaction directly (removing it from the pending queue first).
1580 : //
1581 162 : nsAHttpConnection *conn = trans->Connection();
1582 162 : if (conn && !trans->IsDone())
1583 9 : conn->CloseTransaction(trans, reason);
1584 : else {
1585 : nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
1586 153 : nsnull, trans);
1587 :
1588 153 : if (ent) {
1589 153 : PRInt32 index = ent->mPendingQ.IndexOf(trans);
1590 153 : if (index >= 0) {
1591 1 : ent->mPendingQ.RemoveElementAt(index);
1592 1 : nsHttpTransaction *temp = trans;
1593 1 : NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
1594 : }
1595 : }
1596 153 : trans->Close(reason);
1597 : }
1598 162 : NS_RELEASE(trans);
1599 162 : }
1600 :
1601 : void
1602 2976 : nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
1603 : {
1604 2976 : nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
1605 :
1606 2976 : LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
1607 :
1608 : // start by processing the queue identified by the given connection info.
1609 2976 : nsConnectionEntry *ent = mCT.Get(ci->HashKey());
1610 2976 : if (!(ent && ProcessPendingQForEntry(ent))) {
1611 : // if we reach here, it means that we couldn't dispatch a transaction
1612 : // for the specified connection info. walk the connection table...
1613 2976 : mCT.Enumerate(ProcessOneTransactionCB, this);
1614 : }
1615 :
1616 2976 : NS_RELEASE(ci);
1617 2976 : }
1618 :
1619 : void
1620 126 : nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
1621 : {
1622 126 : LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
1623 :
1624 : // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
1625 126 : mTimeOfNextWakeUp = LL_MAXUINT;
1626 :
1627 : // check canreuse() for all idle connections plus any active connections on
1628 : // connection entries that are using spdy.
1629 126 : if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
1630 3 : mCT.Enumerate(PruneDeadConnectionsCB, this);
1631 126 : }
1632 :
1633 : void
1634 123 : nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
1635 : {
1636 123 : LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
1637 :
1638 123 : mCT.Enumerate(ClosePersistentConnectionsCB, this);
1639 123 : }
1640 :
1641 : void
1642 2976 : nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
1643 : {
1644 2976 : LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
1645 :
1646 2976 : nsHttpConnection *conn = (nsHttpConnection *) param;
1647 :
1648 : //
1649 : // 1) remove the connection from the active list
1650 : // 2) if keep-alive, add connection to idle list
1651 : // 3) post event to process the pending transaction queue
1652 : //
1653 :
1654 : nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
1655 2976 : conn, nsnull);
1656 2976 : nsHttpConnectionInfo *ci = nsnull;
1657 :
1658 2976 : if (!ent) {
1659 : // this should never happen
1660 0 : LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
1661 0 : NS_ABORT_IF_FALSE(false, "no connection entry");
1662 0 : NS_ADDREF(ci = conn->ConnectionInfo());
1663 : }
1664 : else {
1665 2976 : NS_ADDREF(ci = ent->mConnInfo);
1666 :
1667 : // If the connection is in the active list, remove that entry
1668 : // and the reference held by the mActiveConns list.
1669 : // This is never the final reference on conn as the event context
1670 : // is also holding one that is released at the end of this function.
1671 :
1672 2976 : if (ent->mUsingSpdy) {
1673 : // Spdy connections aren't reused in the traditional HTTP way in
1674 : // the idleconns list, they are actively multplexed as active
1675 : // conns. Even when they have 0 transactions on them they are
1676 : // considered active connections. So when one is reclaimed it
1677 : // is really complete and is meant to be shut down and not
1678 : // reused.
1679 0 : conn->DontReuse();
1680 : }
1681 :
1682 2976 : if (ent->mActiveConns.RemoveElement(conn)) {
1683 2975 : nsHttpConnection *temp = conn;
1684 2975 : NS_RELEASE(temp);
1685 2975 : mNumActiveConns--;
1686 2975 : ConditionallyStopReadTimeoutTick();
1687 : }
1688 :
1689 2976 : if (conn->CanReuse()) {
1690 5 : LOG((" adding connection to idle list\n"));
1691 : // Keep The idle connection list sorted with the connections that
1692 : // have moved the largest data pipelines at the front because these
1693 : // connections have the largest cwnds on the server.
1694 :
1695 : // The linear search is ok here because the number of idleconns
1696 : // in a single entry is generally limited to a small number (i.e. 6)
1697 :
1698 : PRUint32 idx;
1699 5 : for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
1700 0 : nsHttpConnection *idleConn = ent->mIdleConns[idx];
1701 0 : if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
1702 0 : break;
1703 : }
1704 :
1705 5 : NS_ADDREF(conn);
1706 5 : ent->mIdleConns.InsertElementAt(idx, conn);
1707 5 : mNumIdleConns++;
1708 5 : conn->BeginIdleMonitoring();
1709 :
1710 : // If the added connection was first idle connection or has shortest
1711 : // time to live among the watched connections, pruning dead
1712 : // connections needs to be done when it can't be reused anymore.
1713 5 : PRUint32 timeToLive = conn->TimeToLive();
1714 5 : if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
1715 5 : PruneDeadConnectionsAfter(timeToLive);
1716 : }
1717 : else {
1718 2971 : LOG((" connection cannot be reused; closing connection\n"));
1719 : // make sure the connection is closed and release our reference.
1720 2971 : conn->Close(NS_ERROR_ABORT);
1721 : }
1722 : }
1723 :
1724 2976 : OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
1725 2976 : NS_RELEASE(conn);
1726 2976 : }
1727 :
1728 : void
1729 0 : nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
1730 : {
1731 0 : PRUint16 name = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
1732 0 : PRUint16 value = NS_PTR_TO_INT32(param) & 0x0000FFFF;
1733 :
1734 0 : switch (name) {
1735 : case MAX_CONNECTIONS:
1736 0 : mMaxConns = value;
1737 0 : break;
1738 : case MAX_CONNECTIONS_PER_HOST:
1739 0 : mMaxConnsPerHost = value;
1740 0 : break;
1741 : case MAX_CONNECTIONS_PER_PROXY:
1742 0 : mMaxConnsPerProxy = value;
1743 0 : break;
1744 : case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
1745 0 : mMaxPersistConnsPerHost = value;
1746 0 : break;
1747 : case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
1748 0 : mMaxPersistConnsPerProxy = value;
1749 0 : break;
1750 : case MAX_REQUEST_DELAY:
1751 0 : mMaxRequestDelay = value;
1752 0 : break;
1753 : case MAX_PIPELINED_REQUESTS:
1754 0 : mMaxPipelinedRequests = value;
1755 0 : break;
1756 : default:
1757 0 : NS_NOTREACHED("unexpected parameter name");
1758 : }
1759 0 : }
1760 :
1761 : // nsHttpConnectionMgr::nsConnectionEntry
1762 582 : nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
1763 : {
1764 291 : if (mSpdyPreferred)
1765 0 : gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
1766 :
1767 291 : NS_RELEASE(mConnInfo);
1768 291 : }
1769 :
1770 : // Read Timeout Tick handlers
1771 :
1772 : void
1773 2975 : nsHttpConnectionMgr::ActivateTimeoutTick()
1774 : {
1775 2975 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1776 2975 : LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
1777 : "this=%p mReadTimeoutTick=%p\n"));
1778 :
1779 : // right now the spdy timeout code is the only thing hooked to the timeout
1780 : // tick, so disable it if spdy is not being used. However pipelining code
1781 : // will also want this functionality soon.
1782 2975 : if (!gHttpHandler->IsSpdyEnabled())
1783 0 : return;
1784 :
1785 : // The timer tick should be enabled if it is not already pending.
1786 : // Upon running the tick will rearm itself if there are active
1787 : // connections available.
1788 :
1789 2975 : if (mReadTimeoutTick && mReadTimeoutTickArmed)
1790 232 : return;
1791 :
1792 2743 : if (!mReadTimeoutTick) {
1793 260 : mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
1794 260 : if (!mReadTimeoutTick) {
1795 0 : NS_WARNING("failed to create timer for http timeout management");
1796 0 : return;
1797 : }
1798 260 : mReadTimeoutTick->SetTarget(mSocketThreadTarget);
1799 : }
1800 :
1801 2743 : NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
1802 2743 : mReadTimeoutTickArmed = true;
1803 : // pipeline will expect a 1000ms granuality
1804 2743 : mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
1805 : }
1806 :
1807 : void
1808 0 : nsHttpConnectionMgr::ReadTimeoutTick()
1809 : {
1810 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1811 0 : NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
1812 :
1813 0 : LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
1814 : mNumActiveConns));
1815 :
1816 0 : mCT.Enumerate(ReadTimeoutTickCB, this);
1817 0 : }
1818 :
1819 : PLDHashOperator
1820 0 : nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
1821 : nsAutoPtr<nsConnectionEntry> &ent,
1822 : void *closure)
1823 : {
1824 0 : nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
1825 :
1826 0 : LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
1827 : self, ent->mConnInfo->Host()));
1828 :
1829 0 : PRIntervalTime now = PR_IntervalNow();
1830 0 : for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
1831 0 : ent->mActiveConns[index]->ReadTimeoutTick(now);
1832 :
1833 0 : return PL_DHASH_NEXT;
1834 : }
1835 :
1836 : //-----------------------------------------------------------------------------
1837 : // nsHttpConnectionMgr::nsConnectionHandle
1838 :
1839 5950 : nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
1840 : {
1841 2975 : if (mConn) {
1842 2975 : gHttpHandler->ReclaimConnection(mConn);
1843 2975 : NS_RELEASE(mConn);
1844 : }
1845 11900 : }
1846 :
1847 11968 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
1848 :
1849 : nsresult
1850 2819 : nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
1851 : nsHttpRequestHead *req,
1852 : nsHttpResponseHead *resp,
1853 : bool *reset)
1854 : {
1855 2819 : return mConn->OnHeadersAvailable(trans, req, resp, reset);
1856 : }
1857 :
1858 : nsresult
1859 0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
1860 : {
1861 0 : return mConn->ResumeSend();
1862 : }
1863 :
1864 : nsresult
1865 0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
1866 : {
1867 0 : return mConn->ResumeRecv();
1868 : }
1869 :
1870 : void
1871 9 : nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
1872 : {
1873 9 : mConn->CloseTransaction(trans, reason);
1874 9 : }
1875 :
1876 : void
1877 0 : nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
1878 : {
1879 0 : mConn->GetConnectionInfo(result);
1880 0 : }
1881 :
1882 : nsresult
1883 0 : nsHttpConnectionMgr::
1884 : nsConnectionHandle::TakeTransport(nsISocketTransport **aTransport,
1885 : nsIAsyncInputStream **aInputStream,
1886 : nsIAsyncOutputStream **aOutputStream)
1887 : {
1888 0 : return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
1889 : }
1890 :
1891 : void
1892 2975 : nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
1893 : {
1894 2975 : mConn->GetSecurityInfo(result);
1895 2975 : }
1896 :
1897 : bool
1898 5541 : nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
1899 : {
1900 5541 : return mConn->IsPersistent();
1901 : }
1902 :
1903 : bool
1904 2975 : nsHttpConnectionMgr::nsConnectionHandle::IsReused()
1905 : {
1906 2975 : return mConn->IsReused();
1907 : }
1908 :
1909 : nsresult
1910 0 : nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
1911 : {
1912 0 : return mConn->PushBack(buf, bufLen);
1913 : }
1914 :
1915 :
1916 : //////////////////////// nsHalfOpenSocket
1917 :
1918 :
1919 89001 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
1920 : nsIOutputStreamCallback,
1921 : nsITransportEventSink,
1922 : nsIInterfaceRequestor,
1923 : nsITimerCallback)
1924 :
1925 2977 : nsHttpConnectionMgr::
1926 : nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
1927 : nsHttpTransaction *trans)
1928 : : mEnt(ent),
1929 2977 : mTransaction(trans)
1930 : {
1931 2977 : NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
1932 2977 : LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
1933 : this, trans, ent->mConnInfo->Host()));
1934 2977 : }
1935 :
1936 5954 : nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
1937 : {
1938 2977 : NS_ABORT_IF_FALSE(!mStreamOut, "streamout not null");
1939 2977 : NS_ABORT_IF_FALSE(!mBackupStreamOut, "backupstreamout not null");
1940 2977 : NS_ABORT_IF_FALSE(!mSynTimer, "syntimer not null");
1941 2977 : LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
1942 :
1943 2977 : if (mEnt) {
1944 : // A failure to create the transport object at all
1945 : // will result in this not being present in the halfopen table
1946 : // so ignore failures of RemoveElement()
1947 2977 : mEnt->mHalfOpens.RemoveElement(this);
1948 : }
1949 2977 : }
1950 :
1951 : nsresult
1952 2977 : nsHttpConnectionMgr::
1953 : nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
1954 : nsIAsyncInputStream **instream,
1955 : nsIAsyncOutputStream **outstream,
1956 : bool isBackup)
1957 : {
1958 : nsresult rv;
1959 :
1960 : const char* types[1];
1961 2977 : types[0] = (mEnt->mConnInfo->UsingSSL()) ?
1962 2977 : "ssl" : gHttpHandler->DefaultSocketType();
1963 2977 : PRUint32 typeCount = (types[0] != nsnull);
1964 :
1965 5954 : nsCOMPtr<nsISocketTransport> socketTransport;
1966 5954 : nsCOMPtr<nsISocketTransportService> sts;
1967 :
1968 2977 : sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1969 2977 : NS_ENSURE_SUCCESS(rv, rv);
1970 :
1971 2977 : rv = sts->CreateTransport(types, typeCount,
1972 2977 : nsDependentCString(mEnt->mConnInfo->Host()),
1973 : mEnt->mConnInfo->Port(),
1974 2977 : mEnt->mConnInfo->ProxyInfo(),
1975 8931 : getter_AddRefs(socketTransport));
1976 2977 : NS_ENSURE_SUCCESS(rv, rv);
1977 :
1978 2976 : PRUint32 tmpFlags = 0;
1979 2976 : if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
1980 1923 : tmpFlags = nsISocketTransport::BYPASS_CACHE;
1981 :
1982 2976 : if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
1983 1 : tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
1984 :
1985 : // For backup connections, we disable IPv6. That's because some users have
1986 : // broken IPv6 connectivity (leading to very long timeouts), and disabling
1987 : // IPv6 on the backup connection gives them a much better user experience
1988 : // with dual-stack hosts, though they still pay the 250ms delay for each new
1989 : // connection. This strategy is also known as "happy eyeballs".
1990 2976 : if (isBackup && gHttpHandler->FastFallbackToIPv4())
1991 0 : tmpFlags |= nsISocketTransport::DISABLE_IPV6;
1992 :
1993 2976 : socketTransport->SetConnectionFlags(tmpFlags);
1994 :
1995 2976 : socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
1996 :
1997 2976 : rv = socketTransport->SetEventSink(this, nsnull);
1998 2976 : NS_ENSURE_SUCCESS(rv, rv);
1999 :
2000 2976 : rv = socketTransport->SetSecurityCallbacks(this);
2001 2976 : NS_ENSURE_SUCCESS(rv, rv);
2002 :
2003 5952 : nsCOMPtr<nsIOutputStream> sout;
2004 2976 : rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
2005 : 0, 0,
2006 2976 : getter_AddRefs(sout));
2007 2976 : NS_ENSURE_SUCCESS(rv, rv);
2008 :
2009 5952 : nsCOMPtr<nsIInputStream> sin;
2010 2976 : rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
2011 : 0, 0,
2012 2976 : getter_AddRefs(sin));
2013 2976 : NS_ENSURE_SUCCESS(rv, rv);
2014 :
2015 2976 : socketTransport.forget(transport);
2016 2976 : CallQueryInterface(sin, instream);
2017 2976 : CallQueryInterface(sout, outstream);
2018 :
2019 2976 : rv = (*outstream)->AsyncWait(this, 0, 0, nsnull);
2020 2976 : if (NS_SUCCEEDED(rv))
2021 2976 : gHttpHandler->ConnMgr()->StartedConnect();
2022 :
2023 2976 : return rv;
2024 : }
2025 :
2026 : nsresult
2027 2977 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
2028 : {
2029 2977 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2030 :
2031 : nsresult rv;
2032 :
2033 2977 : rv = SetupStreams(getter_AddRefs(mSocketTransport),
2034 2977 : getter_AddRefs(mStreamIn),
2035 2977 : getter_AddRefs(mStreamOut),
2036 2977 : false);
2037 2977 : LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
2038 : this, mEnt->mConnInfo->Host(), rv));
2039 2977 : if (NS_FAILED(rv)) {
2040 1 : if (mStreamOut)
2041 0 : mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2042 1 : mStreamOut = nsnull;
2043 1 : mStreamIn = nsnull;
2044 1 : mSocketTransport = nsnull;
2045 : }
2046 2977 : return rv;
2047 : }
2048 :
2049 : nsresult
2050 0 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
2051 : {
2052 0 : nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
2053 0 : getter_AddRefs(mBackupStreamIn),
2054 0 : getter_AddRefs(mBackupStreamOut),
2055 0 : true);
2056 0 : LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
2057 : this, mEnt->mConnInfo->Host(), rv));
2058 0 : if (NS_FAILED(rv)) {
2059 0 : if (mBackupStreamOut)
2060 0 : mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2061 0 : mBackupStreamOut = nsnull;
2062 0 : mBackupStreamIn = nsnull;
2063 0 : mBackupTransport = nsnull;
2064 : }
2065 0 : return rv;
2066 : }
2067 :
2068 : void
2069 2969 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
2070 : {
2071 2969 : PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
2072 2969 : NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
2073 :
2074 2969 : if (timeout) {
2075 : // Setup the timer that will establish a backup socket
2076 : // if we do not get a writable event on the main one.
2077 : // We do this because a lost SYN takes a very long time
2078 : // to repair at the TCP level.
2079 : //
2080 : // Failure to setup the timer is something we can live with,
2081 : // so don't return an error in that case.
2082 : nsresult rv;
2083 2969 : mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
2084 2969 : if (NS_SUCCEEDED(rv)) {
2085 2969 : mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
2086 2969 : LOG(("nsHalfOpenSocket::SetupBackupTimer()"));
2087 : }
2088 : }
2089 2969 : }
2090 :
2091 : void
2092 5811 : nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
2093 : {
2094 : // If the syntimer is still armed, we can cancel it because no backup
2095 : // socket should be formed at this point
2096 5811 : if (!mSynTimer)
2097 2842 : return;
2098 :
2099 2969 : LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
2100 2969 : mSynTimer->Cancel();
2101 2969 : mSynTimer = nsnull;
2102 : }
2103 :
2104 : void
2105 0 : nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
2106 : {
2107 0 : LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
2108 : this, mEnt->mConnInfo->Host()));
2109 :
2110 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2111 :
2112 0 : nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
2113 :
2114 0 : if (mStreamOut) {
2115 0 : gHttpHandler->ConnMgr()->RecvdConnect();
2116 0 : mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2117 0 : mStreamOut = nsnull;
2118 : }
2119 0 : if (mBackupStreamOut) {
2120 0 : gHttpHandler->ConnMgr()->RecvdConnect();
2121 0 : mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
2122 0 : mBackupStreamOut = nsnull;
2123 : }
2124 :
2125 0 : CancelBackupTimer();
2126 :
2127 0 : mEnt = nsnull;
2128 0 : }
2129 :
2130 : NS_IMETHODIMP // method for nsITimerCallback
2131 0 : nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
2132 : {
2133 0 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2134 0 : NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
2135 :
2136 0 : if (!gHttpHandler->ConnMgr()->
2137 0 : AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
2138 0 : SetupBackupStreams();
2139 : }
2140 :
2141 0 : mSynTimer = nsnull;
2142 0 : return NS_OK;
2143 : }
2144 :
2145 : // method for nsIAsyncOutputStreamCallback
2146 : NS_IMETHODIMP
2147 2976 : nsHttpConnectionMgr::
2148 : nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
2149 : {
2150 2976 : NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2151 2976 : NS_ABORT_IF_FALSE(out == mStreamOut ||
2152 : out == mBackupStreamOut, "stream mismatch");
2153 2976 : LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
2154 : this, mEnt->mConnInfo->Host(),
2155 : out == mStreamOut ? "primary" : "backup"));
2156 : PRInt32 index;
2157 : nsresult rv;
2158 :
2159 2976 : gHttpHandler->ConnMgr()->RecvdConnect();
2160 :
2161 2976 : CancelBackupTimer();
2162 :
2163 : // assign the new socket to the http connection
2164 5952 : nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
2165 2976 : LOG(("nsHalfOpenSocket::OnOutputStreamReady "
2166 : "Created new nshttpconnection %p\n", conn.get()));
2167 :
2168 5952 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2169 5952 : nsCOMPtr<nsIEventTarget> callbackTarget;
2170 5952 : mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
2171 5952 : getter_AddRefs(callbackTarget));
2172 2976 : if (out == mStreamOut) {
2173 : rv = conn->Init(mEnt->mConnInfo,
2174 2976 : gHttpHandler->ConnMgr()->mMaxRequestDelay,
2175 : mSocketTransport, mStreamIn, mStreamOut,
2176 5952 : callbacks, callbackTarget);
2177 :
2178 : // The nsHttpConnection object now owns these streams and sockets
2179 2976 : mStreamOut = nsnull;
2180 2976 : mStreamIn = nsnull;
2181 2976 : mSocketTransport = nsnull;
2182 : }
2183 : else {
2184 : rv = conn->Init(mEnt->mConnInfo,
2185 0 : gHttpHandler->ConnMgr()->mMaxRequestDelay,
2186 : mBackupTransport, mBackupStreamIn, mBackupStreamOut,
2187 0 : callbacks, callbackTarget);
2188 :
2189 : // The nsHttpConnection object now owns these streams and sockets
2190 0 : mBackupStreamOut = nsnull;
2191 0 : mBackupStreamIn = nsnull;
2192 0 : mBackupTransport = nsnull;
2193 : }
2194 :
2195 2976 : if (NS_FAILED(rv)) {
2196 0 : LOG(("nsHalfOpenSocket::OnOutputStreamReady "
2197 : "conn->init (%p) failed %x\n", conn.get(), rv));
2198 0 : return rv;
2199 : }
2200 :
2201 : // if this is still in the pending list, remove it and dispatch it
2202 2976 : index = mEnt->mPendingQ.IndexOf(mTransaction);
2203 2976 : if (index != -1) {
2204 2975 : mEnt->mPendingQ.RemoveElementAt(index);
2205 2975 : nsHttpTransaction *temp = mTransaction;
2206 2975 : NS_RELEASE(temp);
2207 2975 : gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
2208 : rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
2209 2975 : mTransaction->Caps(),
2210 5950 : conn);
2211 : }
2212 : else {
2213 : // this transaction was dispatched off the pending q before all the
2214 : // sockets established themselves.
2215 :
2216 : // We need to establish a small non-zero idle timeout so the connection
2217 : // mgr perceives this socket as suitable for persistent connection reuse
2218 1 : const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
2219 1 : if (k5Sec < gHttpHandler->IdleTimeout())
2220 1 : conn->SetIdleTimeout(k5Sec);
2221 : else
2222 0 : conn->SetIdleTimeout(gHttpHandler->IdleTimeout());
2223 :
2224 : // After about 1 second allow for the possibility of restarting a
2225 : // transaction due to server close. Keep at sub 1 second as that is the
2226 : // minimum granularity we can expect a server to be timing out with.
2227 1 : conn->SetIsReusedAfter(950);
2228 :
2229 2 : nsRefPtr<nsHttpConnection> copy(conn); // because onmsg*() expects to drop a reference
2230 1 : gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn.forget().get());
2231 : }
2232 :
2233 2976 : return rv;
2234 : }
2235 :
2236 : // method for nsITransportEventSink
2237 : NS_IMETHODIMP
2238 11756 : nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
2239 : nsresult status,
2240 : PRUint64 progress,
2241 : PRUint64 progressMax)
2242 : {
2243 11756 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2244 :
2245 11756 : if (mTransaction)
2246 11756 : mTransaction->OnTransportStatus(trans, status, progress);
2247 :
2248 11756 : if (trans != mSocketTransport)
2249 0 : return NS_OK;
2250 :
2251 : // if we are doing spdy coalescing and haven't recorded the ip address
2252 : // for this entry before then make the hash key if our dns lookup
2253 : // just completed
2254 :
2255 20269 : if (status == nsISocketTransport::STATUS_CONNECTED_TO &&
2256 2835 : gHttpHandler->IsSpdyEnabled() &&
2257 2835 : gHttpHandler->CoalesceSpdy() &&
2258 2835 : mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
2259 4 : !mEnt->mConnInfo->UsingHttpProxy() &&
2260 4 : mEnt->mCoalescingKey.IsEmpty()) {
2261 :
2262 : PRNetAddr addr;
2263 4 : nsresult rv = mSocketTransport->GetPeerAddr(&addr);
2264 4 : if (NS_SUCCEEDED(rv)) {
2265 4 : mEnt->mCoalescingKey.SetCapacity(72);
2266 4 : PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64);
2267 : mEnt->mCoalescingKey.SetLength(
2268 4 : strlen(mEnt->mCoalescingKey.BeginReading()));
2269 :
2270 4 : if (mEnt->mConnInfo->GetAnonymous())
2271 0 : mEnt->mCoalescingKey.AppendLiteral("~A:");
2272 : else
2273 4 : mEnt->mCoalescingKey.AppendLiteral("~.:");
2274 4 : mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
2275 :
2276 4 : LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
2277 : "STATUS_CONNECTED_TO Established New Coalescing Key for host "
2278 : "%s [%s]", mEnt->mConnInfo->Host(),
2279 : mEnt->mCoalescingKey.get()));
2280 :
2281 4 : gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
2282 : }
2283 : }
2284 :
2285 11756 : switch (status) {
2286 : case nsISocketTransport::STATUS_CONNECTING_TO:
2287 : // Passed DNS resolution, now trying to connect, start the backup timer
2288 : // only prevent creating another backup transport.
2289 : // We also check for mEnt presence to not instantiate the timer after
2290 : // this half open socket has already been abandoned. It may happen
2291 : // when we get this notification right between main-thread calls to
2292 : // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
2293 : // where the first abandones all half open socket instances and only
2294 : // after that the second stops the socket thread.
2295 2969 : if (mEnt && !mBackupTransport && !mSynTimer)
2296 2969 : SetupBackupTimer();
2297 2969 : break;
2298 :
2299 : case nsISocketTransport::STATUS_CONNECTED_TO:
2300 : // TCP connection's up, now transfer or SSL negotiantion starts,
2301 : // no need for backup socket
2302 2835 : CancelBackupTimer();
2303 2835 : break;
2304 :
2305 : default:
2306 5952 : break;
2307 : }
2308 :
2309 11756 : return NS_OK;
2310 : }
2311 :
2312 : // method for nsIInterfaceRequestor
2313 : NS_IMETHODIMP
2314 0 : nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
2315 : void **result)
2316 : {
2317 0 : if (mTransaction) {
2318 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
2319 0 : mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull);
2320 0 : if (callbacks)
2321 0 : return callbacks->GetInterface(iid, result);
2322 : }
2323 0 : return NS_ERROR_NO_INTERFACE;
2324 : }
2325 :
2326 :
2327 : nsHttpConnection *
2328 0 : nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
2329 : {
2330 : // return our connection object to the caller and clear it internally
2331 : // do not drop our reference - the caller now owns it.
2332 :
2333 0 : NS_ASSERTION(mConn, "no connection");
2334 0 : nsHttpConnection *conn = mConn;
2335 0 : mConn = nsnull;
2336 0 : return conn;
2337 : }
2338 :
2339 : bool
2340 2830 : nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
2341 : {
2342 2830 : return mConn->LastTransactionExpectedNoContent();
2343 : }
2344 :
2345 : void
2346 2819 : nsHttpConnectionMgr::
2347 : nsConnectionHandle::SetLastTransactionExpectedNoContent(bool val)
2348 : {
2349 2819 : mConn->SetLastTransactionExpectedNoContent(val);
2350 2819 : }
2351 :
2352 : nsISocketTransport *
2353 0 : nsHttpConnectionMgr::nsConnectionHandle::Transport()
2354 : {
2355 0 : if (!mConn)
2356 0 : return nsnull;
2357 0 : return mConn->Transport();
2358 : }
|