Back to index

lightning-sunbird  0.9+nobinonly
nsHttpConnection.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sw=4 sts=4 et cin: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Darin Fisher <darin@netscape.com> (original author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsHttpConnection.h"
00041 #include "nsHttpTransaction.h"
00042 #include "nsHttpRequestHead.h"
00043 #include "nsHttpResponseHead.h"
00044 #include "nsHttpHandler.h"
00045 #include "nsISocketTransportService.h"
00046 #include "nsISocketTransport.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsISSLSocketControl.h"
00049 #include "nsIStringStream.h"
00050 #include "netCore.h"
00051 #include "nsNetCID.h"
00052 #include "nsAutoLock.h"
00053 #include "prmem.h"
00054 #include "plevent.h"
00055 
00056 #ifdef DEBUG
00057 // defined by the socket transport service while active
00058 extern PRThread *gSocketThread;
00059 #endif
00060 
00061 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00062 
00063 //-----------------------------------------------------------------------------
00064 // nsHttpConnection <public>
00065 //-----------------------------------------------------------------------------
00066 
00067 nsHttpConnection::nsHttpConnection()
00068     : mTransaction(nsnull)
00069     , mConnInfo(nsnull)
00070     , mLock(nsnull)
00071     , mLastReadTime(0)
00072     , mIdleTimeout(0)
00073     , mKeepAlive(PR_TRUE) // assume to keep-alive by default
00074     , mKeepAliveMask(PR_TRUE)
00075     , mSupportsPipelining(PR_FALSE) // assume low-grade server
00076     , mIsReused(PR_FALSE)
00077     , mCompletedSSLConnect(PR_FALSE)
00078 {
00079     LOG(("Creating nsHttpConnection @%x\n", this));
00080 
00081     // grab a reference to the handler to ensure that it doesn't go away.
00082     nsHttpHandler *handler = gHttpHandler;
00083     NS_ADDREF(handler);
00084 }
00085 
00086 nsHttpConnection::~nsHttpConnection()
00087 {
00088     LOG(("Destroying nsHttpConnection @%x\n", this));
00089  
00090     NS_IF_RELEASE(mConnInfo);
00091     NS_IF_RELEASE(mTransaction);
00092 
00093     if (mLock) {
00094         PR_DestroyLock(mLock);
00095         mLock = nsnull;
00096     }
00097 
00098     // release our reference to the handler
00099     nsHttpHandler *handler = gHttpHandler;
00100     NS_RELEASE(handler);
00101 }
00102 
00103 nsresult
00104 nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime)
00105 {
00106     LOG(("nsHttpConnection::Init [this=%x]\n", this));
00107 
00108     NS_ENSURE_ARG_POINTER(info);
00109     NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
00110 
00111     mLock = PR_NewLock();
00112     if (!mLock)
00113         return NS_ERROR_OUT_OF_MEMORY;
00114 
00115     mConnInfo = info;
00116     NS_ADDREF(mConnInfo);
00117 
00118     mMaxHangTime = maxHangTime;
00119     mLastReadTime = NowInSeconds();
00120     return NS_OK;
00121 }
00122 
00123 // called on the socket thread
00124 nsresult
00125 nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
00126 {
00127     nsresult rv;
00128 
00129     LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
00130          this, trans, caps));
00131 
00132     NS_ENSURE_ARG_POINTER(trans);
00133     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
00134 
00135     // take ownership of the transaction
00136     mTransaction = trans;
00137     NS_ADDREF(mTransaction);
00138 
00139     // set mKeepAlive according to what will be requested
00140     mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
00141 
00142     // if we don't have a socket transport then create a new one
00143     if (!mSocketTransport) {
00144         rv = CreateTransport();
00145         if (NS_FAILED(rv))
00146             goto loser;
00147     }
00148 
00149     // need to handle SSL proxy CONNECT if this is the first time.
00150     if (mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy() && !mCompletedSSLConnect) {
00151         rv = SetupSSLProxyConnect();
00152         if (NS_FAILED(rv))
00153             goto loser;
00154     }
00155 
00156     // wait for the output stream to be readable
00157     rv = mSocketOut->AsyncWait(this, 0, 0, nsnull);
00158     if (NS_SUCCEEDED(rv))
00159         return rv;
00160 
00161 loser:
00162     NS_RELEASE(mTransaction);
00163     return rv;
00164 }
00165 
00166 void
00167 nsHttpConnection::Close(nsresult reason)
00168 {
00169     LOG(("nsHttpConnection::Close [this=%x reason=%x]\n", this, reason));
00170 
00171     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00172 
00173     if (NS_FAILED(reason)) {
00174         if (mSocketTransport) {
00175             mSocketTransport->SetSecurityCallbacks(nsnull);
00176             mSocketTransport->SetEventSink(nsnull, nsnull);
00177             mSocketTransport->Close(reason);
00178         }
00179         mKeepAlive = PR_FALSE;
00180     }
00181 }
00182 
00183 // called on the socket thread
00184 nsresult
00185 nsHttpConnection::ProxyStartSSL()
00186 {
00187     LOG(("nsHttpConnection::ProxyStartSSL [this=%x]\n", this));
00188 #ifdef DEBUG
00189     NS_PRECONDITION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00190 #endif
00191 
00192     nsCOMPtr<nsISupports> securityInfo;
00193     nsresult rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
00194     if (NS_FAILED(rv)) return rv;
00195 
00196     nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
00197     if (NS_FAILED(rv)) return rv;
00198 
00199     return ssl->ProxyStartSSL();
00200 }
00201 
00202 PRBool
00203 nsHttpConnection::CanReuse()
00204 {
00205     return IsKeepAlive() && (NowInSeconds() - mLastReadTime < mIdleTimeout)
00206                          && IsAlive();
00207 }
00208 
00209 PRBool
00210 nsHttpConnection::IsAlive()
00211 {
00212     if (!mSocketTransport)
00213         return PR_FALSE;
00214 
00215     PRBool alive;
00216     nsresult rv = mSocketTransport->IsAlive(&alive);
00217     if (NS_FAILED(rv))
00218         alive = PR_FALSE;
00219 
00220 //#define TEST_RESTART_LOGIC
00221 #ifdef TEST_RESTART_LOGIC
00222     if (!alive) {
00223         LOG(("pretending socket is still alive to test restart logic\n"));
00224         alive = PR_TRUE;
00225     }
00226 #endif
00227 
00228     return alive;
00229 }
00230 
00231 PRBool
00232 nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
00233 {
00234     // XXX there should be a strict mode available that disables this
00235     // blacklisting.
00236 
00237     // assuming connection is HTTP/1.1 with keep-alive enabled
00238     if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
00239         // XXX check for bad proxy servers...
00240         return PR_TRUE;
00241     }
00242 
00243     // XXX what about checking for a Via header? (transparent proxies)
00244 
00245     // check for bad origin servers
00246     const char *val = responseHead->PeekHeader(nsHttp::Server);
00247     if (!val)
00248         return PR_FALSE; // no header, no love
00249 
00250     // the list of servers known to do bad things with pipelined requests
00251     static const char *bad_servers[] = {
00252         "Microsoft-IIS/4.",
00253         "Microsoft-IIS/5.",
00254         "Netscape-Enterprise/3.",
00255         nsnull
00256     };
00257 
00258     for (const char **server = bad_servers; *server; ++server) {
00259         if (PL_strcasestr(val, *server) != nsnull) {
00260             LOG(("looks like this server does not support pipelining"));
00261             return PR_FALSE;
00262         }
00263     }
00264 
00265     // ok, let's allow pipelining to this server
00266     return PR_TRUE;
00267 }
00268 
00269 //----------------------------------------------------------------------------
00270 // nsHttpConnection::nsAHttpConnection compatible methods
00271 //----------------------------------------------------------------------------
00272 
00273 nsresult
00274 nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
00275                                      nsHttpRequestHead *requestHead,
00276                                      nsHttpResponseHead *responseHead,
00277                                      PRBool *reset)
00278 {
00279     LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n",
00280         this, trans, responseHead));
00281 
00282     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00283     NS_ENSURE_ARG_POINTER(trans);
00284     NS_ASSERTION(responseHead, "No response head?");
00285 
00286     // If the server issued an explicit timeout, then we need to close down the
00287     // socket transport.  We pass an error code of NS_ERROR_NET_RESET to
00288     // trigger the transactions 'restart' mechanism.  We tell it to reset its
00289     // response headers so that it will be ready to receive the new response.
00290     if (responseHead->Status() == 408) {
00291         Close(NS_ERROR_NET_RESET);
00292         *reset = PR_TRUE;
00293         return NS_OK;
00294     }
00295 
00296     // we won't change our keep-alive policy unless the server has explicitly
00297     // told us to do so.
00298 
00299     // inspect the connection headers for keep-alive info provided the
00300     // transaction completed successfully.
00301     const char *val = responseHead->PeekHeader(nsHttp::Connection);
00302     if (!val)
00303         val = responseHead->PeekHeader(nsHttp::Proxy_Connection);
00304 
00305     // reset to default (the server may have changed since we last checked)
00306     mSupportsPipelining = PR_FALSE;
00307 
00308     if ((responseHead->Version() < NS_HTTP_VERSION_1_1) ||
00309         (requestHead->Version() < NS_HTTP_VERSION_1_1)) {
00310         // HTTP/1.0 connections are by default NOT persistent
00311         if (val && !PL_strcasecmp(val, "keep-alive"))
00312             mKeepAlive = PR_TRUE;
00313         else
00314             mKeepAlive = PR_FALSE;
00315     }
00316     else {
00317         // HTTP/1.1 connections are by default persistent
00318         if (val && !PL_strcasecmp(val, "close")) 
00319             mKeepAlive = PR_FALSE;
00320         else {
00321             mKeepAlive = PR_TRUE;
00322             mSupportsPipelining = SupportsPipelining(responseHead);
00323         }
00324     }
00325     mKeepAliveMask = mKeepAlive;
00326 
00327     // if this connection is persistent, then the server may send a "Keep-Alive"
00328     // header specifying the maximum number of times the connection can be
00329     // reused as well as the maximum amount of time the connection can be idle
00330     // before the server will close it.  we ignore the max reuse count, because
00331     // a "keep-alive" connection is by definition capable of being reused, and
00332     // we only care about being able to reuse it once.  if a timeout is not 
00333     // specified then we use our advertized timeout value.
00334     if (mKeepAlive) {
00335         val = responseHead->PeekHeader(nsHttp::Keep_Alive);
00336 
00337         const char *cp = PL_strcasestr(val, "timeout=");
00338         if (cp)
00339             mIdleTimeout = (PRUint32) atoi(cp + 8);
00340         else
00341             mIdleTimeout = gHttpHandler->IdleTimeout();
00342         
00343         LOG(("Connection can be reused [this=%x idle-timeout=%u]\n", this, mIdleTimeout));
00344     }
00345 
00346     // if we're doing an SSL proxy connect, then we need to check whether or not
00347     // the connect was successful.  if so, then we have to reset the transaction
00348     // and step-up the socket connection to SSL. finally, we have to wake up the
00349     // socket write request.
00350     if (mSSLProxyConnectStream) {
00351         mSSLProxyConnectStream = 0;
00352         if (responseHead->Status() == 200) {
00353             LOG(("SSL proxy CONNECT succeeded!\n"));
00354             *reset = PR_TRUE;
00355             nsresult rv = ProxyStartSSL();
00356             if (NS_FAILED(rv)) // XXX need to handle this for real
00357                 LOG(("ProxyStartSSL failed [rv=%x]\n", rv));
00358             mCompletedSSLConnect = PR_TRUE;
00359             rv = mSocketOut->AsyncWait(this, 0, 0, nsnull);
00360             // XXX what if this fails -- need to handle this error
00361             NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
00362         }
00363         else {
00364             LOG(("SSL proxy CONNECT failed!\n"));
00365             // NOTE: this cast is valid since this connection cannot be
00366             // processing a transaction pipeline until after the first HTTP/1.1
00367             // response.
00368             nsHttpTransaction *trans =
00369                     NS_STATIC_CAST(nsHttpTransaction *, mTransaction);
00370             trans->SetSSLConnectFailed();
00371         }
00372     }
00373 
00374     return NS_OK;
00375 }
00376 
00377 void
00378 nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
00379 {
00380     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00381 
00382     if (mSocketTransport) {
00383         if (NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))
00384             *secinfo = nsnull;
00385     }
00386 }
00387 
00388 nsresult
00389 nsHttpConnection::ResumeSend()
00390 {
00391     LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
00392 
00393     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00394 
00395     if (mSocketOut)
00396         return mSocketOut->AsyncWait(this, 0, 0, nsnull);
00397 
00398     NS_NOTREACHED("no socket output stream");
00399     return NS_ERROR_UNEXPECTED;
00400 }
00401 
00402 nsresult
00403 nsHttpConnection::ResumeRecv()
00404 {
00405     LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
00406 
00407     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00408 
00409     if (mSocketIn)
00410         return mSocketIn->AsyncWait(this, 0, 0, nsnull);
00411 
00412     NS_NOTREACHED("no socket input stream");
00413     return NS_ERROR_UNEXPECTED;
00414 }
00415 
00416 //-----------------------------------------------------------------------------
00417 // nsHttpConnection <private>
00418 //-----------------------------------------------------------------------------
00419 
00420 nsresult
00421 nsHttpConnection::CreateTransport()
00422 {
00423     nsresult rv;
00424 
00425     NS_PRECONDITION(!mSocketTransport, "unexpected");
00426 
00427     nsCOMPtr<nsISocketTransportService> sts =
00428             do_GetService(kSocketTransportServiceCID, &rv);
00429     if (NS_FAILED(rv)) return rv;
00430 
00431     // configure the socket type based on the connection type requested.
00432     const char* types[1];
00433 
00434     if (mConnInfo->UsingSSL())
00435         types[0] = "ssl";
00436     else
00437         types[0] = gHttpHandler->DefaultSocketType();
00438 
00439     nsCOMPtr<nsISocketTransport> strans;
00440     PRUint32 typeCount = (types[0] != nsnull);
00441 
00442     rv = sts->CreateTransport(types, typeCount,
00443                               nsDependentCString(mConnInfo->Host()),
00444                               mConnInfo->Port(),
00445                               mConnInfo->ProxyInfo(),
00446                               getter_AddRefs(strans));
00447     if (NS_FAILED(rv)) return rv;
00448 
00449     // NOTE: these create cyclical references, which we break inside
00450     //       nsHttpConnection::Close
00451     rv = strans->SetEventSink(this, nsnull);
00452     if (NS_FAILED(rv)) return rv;
00453     rv = strans->SetSecurityCallbacks(this);
00454     if (NS_FAILED(rv)) return rv;
00455 
00456     // next open the socket streams
00457     nsCOMPtr<nsIOutputStream> sout;
00458     rv = strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
00459                                   getter_AddRefs(sout));
00460     if (NS_FAILED(rv)) return rv;
00461     nsCOMPtr<nsIInputStream> sin;
00462     rv = strans->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
00463                                  getter_AddRefs(sin));
00464     if (NS_FAILED(rv)) return rv;
00465 
00466     mSocketTransport = strans;
00467     mSocketIn = do_QueryInterface(sin);
00468     mSocketOut = do_QueryInterface(sout);
00469     return NS_OK;
00470 }
00471 
00472 void
00473 nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
00474 {
00475     LOG(("nsHttpConnection::CloseTransaction[this=%x trans=%x reason=%x]\n",
00476         this, trans, reason));
00477 
00478     NS_ASSERTION(trans == mTransaction, "wrong transaction");
00479     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00480 
00481     // mask this error code because its not a real error.
00482     if (reason == NS_BASE_STREAM_CLOSED)
00483         reason = NS_OK;
00484 
00485     mTransaction->Close(reason);
00486 
00487     NS_RELEASE(mTransaction);
00488     mTransaction = 0;
00489 
00490     if (NS_FAILED(reason))
00491         Close(reason);
00492 
00493     // flag the connection as reused here for convenience sake.  certainly
00494     // it might be going away instead ;-)
00495     mIsReused = PR_TRUE;
00496 }
00497 
00498 NS_METHOD
00499 nsHttpConnection::ReadFromStream(nsIInputStream *input,
00500                                  void *closure,
00501                                  const char *buf,
00502                                  PRUint32 offset,
00503                                  PRUint32 count,
00504                                  PRUint32 *countRead)
00505 {
00506     // thunk for nsIInputStream instance
00507     nsHttpConnection *conn = (nsHttpConnection *) closure;
00508     return conn->OnReadSegment(buf, count, countRead);
00509 }
00510 
00511 nsresult
00512 nsHttpConnection::OnReadSegment(const char *buf,
00513                                 PRUint32 count,
00514                                 PRUint32 *countRead)
00515 {
00516     if (count == 0) {
00517         // some ReadSegments implementations will erroneously call the writer
00518         // to consume 0 bytes worth of data.  we must protect against this case
00519         // or else we'd end up closing the socket prematurely.
00520         NS_ERROR("bad ReadSegments implementation");
00521         return NS_ERROR_FAILURE; // stop iterating
00522     }
00523 
00524     nsresult rv = mSocketOut->Write(buf, count, countRead);
00525     if (NS_FAILED(rv))
00526         mSocketOutCondition = rv;
00527     else if (*countRead == 0)
00528         mSocketOutCondition = NS_BASE_STREAM_CLOSED;
00529     else
00530         mSocketOutCondition = NS_OK; // reset condition
00531 
00532     return mSocketOutCondition;
00533 }
00534 
00535 nsresult
00536 nsHttpConnection::OnSocketWritable()
00537 {
00538     LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n", this));
00539 
00540     nsresult rv;
00541     PRUint32 n;
00542     PRBool again = PR_TRUE;
00543 
00544     do {
00545         // if we're doing an SSL proxy connect, then we need to bypass calling
00546         // into the transaction.
00547         //
00548         // NOTE: this code path can't be shared since the transaction doesn't
00549         // implement nsIInputStream.  doing so is not worth the added cost of
00550         // extra indirections during normal reading.
00551         //
00552         if (mSSLProxyConnectStream) {
00553             LOG(("  writing CONNECT request stream\n"));
00554             rv = mSSLProxyConnectStream->ReadSegments(ReadFromStream, this,
00555                                                       NS_HTTP_SEGMENT_SIZE, &n);
00556         }
00557         else {
00558             LOG(("  writing transaction request stream\n"));
00559             rv = mTransaction->ReadSegments(this, NS_HTTP_SEGMENT_SIZE, &n);
00560         }
00561 
00562         LOG(("  ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
00563             rv, n, mSocketOutCondition));
00564 
00565         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
00566         if (rv == NS_BASE_STREAM_CLOSED) {
00567             rv = NS_OK;
00568             n = 0;
00569         }
00570 
00571         if (NS_FAILED(rv)) {
00572             // if the transaction didn't want to write any more data, then
00573             // wait for the transaction to call ResumeSend.
00574             if (rv == NS_BASE_STREAM_WOULD_BLOCK)
00575                 rv = NS_OK;
00576             again = PR_FALSE;
00577         }
00578         else if (NS_FAILED(mSocketOutCondition)) {
00579             if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK)
00580                 rv = mSocketOut->AsyncWait(this, 0, 0, nsnull); // continue writing
00581             else
00582                 rv = mSocketOutCondition;
00583             again = PR_FALSE;
00584         }
00585         else if (n == 0) {
00586             // 
00587             // at this point we've written out the entire transaction, and now we
00588             // must wait for the server's response.  we manufacture a status message
00589             // here to reflect the fact that we are waiting.  this message will be
00590             // trumped (overwritten) if the server responds quickly.
00591             //
00592             mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR,
00593                                             LL_ZERO);
00594 
00595             rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
00596             again = PR_FALSE;
00597         }
00598         // write more to the socket until error or end-of-request...
00599     } while (again);
00600 
00601     return rv;
00602 }
00603 
00604 nsresult
00605 nsHttpConnection::OnWriteSegment(char *buf,
00606                                  PRUint32 count,
00607                                  PRUint32 *countWritten)
00608 {
00609     if (count == 0) {
00610         // some WriteSegments implementations will erroneously call the reader
00611         // to provide 0 bytes worth of data.  we must protect against this case
00612         // or else we'd end up closing the socket prematurely.
00613         NS_ERROR("bad WriteSegments implementation");
00614         return NS_ERROR_FAILURE; // stop iterating
00615     }
00616 
00617     nsresult rv = mSocketIn->Read(buf, count, countWritten);
00618     if (NS_FAILED(rv))
00619         mSocketInCondition = rv;
00620     else if (*countWritten == 0)
00621         mSocketInCondition = NS_BASE_STREAM_CLOSED;
00622     else
00623         mSocketInCondition = NS_OK; // reset condition
00624 
00625     return mSocketInCondition;
00626 }
00627 
00628 nsresult
00629 nsHttpConnection::OnSocketReadable()
00630 {
00631     LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
00632 
00633     PRUint32 now = NowInSeconds();
00634 
00635     if (mKeepAliveMask && (now - mLastReadTime >= PRUint32(mMaxHangTime))) {
00636         LOG(("max hang time exceeded!\n"));
00637         // give the handler a chance to create a new persistent connection to
00638         // this host if we've been busy for too long.
00639         mKeepAliveMask = PR_FALSE;
00640         gHttpHandler->ProcessPendingQ(mConnInfo);
00641     }
00642     mLastReadTime = now;
00643 
00644     nsresult rv;
00645     PRUint32 n;
00646     PRBool again = PR_TRUE;
00647 
00648     do {
00649         rv = mTransaction->WriteSegments(this, NS_HTTP_SEGMENT_SIZE, &n);
00650         if (NS_FAILED(rv)) {
00651             // if the transaction didn't want to take any more data, then
00652             // wait for the transaction to call ResumeRecv.
00653             if (rv == NS_BASE_STREAM_WOULD_BLOCK)
00654                 rv = NS_OK;
00655             again = PR_FALSE;
00656         }
00657         else if (NS_FAILED(mSocketInCondition)) {
00658             // continue waiting for the socket if necessary...
00659             if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
00660                 rv = mSocketIn->AsyncWait(this, 0, 0, nsnull);
00661             else
00662                 rv = mSocketInCondition;
00663             again = PR_FALSE;
00664         }
00665         // read more from the socket until error...
00666     } while (again);
00667 
00668     return rv;
00669 }
00670 
00671 nsresult
00672 nsHttpConnection::SetupSSLProxyConnect()
00673 {
00674     const char *val;
00675 
00676     LOG(("nsHttpConnection::SetupSSLProxyConnect [this=%x]\n", this));
00677 
00678     NS_ENSURE_TRUE(!mSSLProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
00679 
00680     nsCAutoString buf;
00681     buf.Assign(mConnInfo->Host());
00682     buf.Append(':');
00683     buf.AppendInt(mConnInfo->Port());
00684 
00685     // CONNECT host:port HTTP/1.1
00686     nsHttpRequestHead request;
00687     request.SetMethod(nsHttp::Connect);
00688     request.SetVersion(gHttpHandler->HttpVersion());
00689     request.SetRequestURI(buf);
00690     request.SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent());
00691 
00692     // send this header for backwards compatibility.
00693     request.SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive"));
00694 
00695     // NOTE: this cast is valid since this connection cannot be processing a
00696     // transaction pipeline until after the first HTTP/1.1 response.
00697     nsHttpTransaction *trans = NS_STATIC_CAST(nsHttpTransaction *, mTransaction);
00698     
00699     val = trans->RequestHead()->PeekHeader(nsHttp::Host);
00700     if (val) {
00701         // all HTTP/1.1 requests must include a Host header (even though it
00702         // may seem redundant in this case; see bug 82388).
00703         request.SetHeader(nsHttp::Host, nsDependentCString(val));
00704     }
00705 
00706     val = trans->RequestHead()->PeekHeader(nsHttp::Proxy_Authorization);
00707     if (val) {
00708         // we don't know for sure if this authorization is intended for the
00709         // SSL proxy, so we add it just in case.
00710         request.SetHeader(nsHttp::Proxy_Authorization, nsDependentCString(val));
00711     }
00712 
00713     buf.Truncate();
00714     request.Flatten(buf, PR_FALSE);
00715     buf.AppendLiteral("\r\n");
00716 
00717     return NS_NewCStringInputStream(getter_AddRefs(mSSLProxyConnectStream), buf);
00718 }
00719 
00720 //-----------------------------------------------------------------------------
00721 // nsHttpConnection::nsISupports
00722 //-----------------------------------------------------------------------------
00723 
00724 NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnection,
00725                               nsIInputStreamCallback,
00726                               nsIOutputStreamCallback,
00727                               nsITransportEventSink,
00728                               nsIInterfaceRequestor)
00729 
00730 //-----------------------------------------------------------------------------
00731 // nsHttpConnection::nsIInputStreamCallback
00732 //-----------------------------------------------------------------------------
00733 
00734 // called on the socket transport thread
00735 NS_IMETHODIMP
00736 nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
00737 {
00738     NS_ASSERTION(in == mSocketIn, "unexpected stream");
00739     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00740 
00741     // if the transaction was dropped...
00742     if (!mTransaction) {
00743         LOG(("  no transaction; ignoring event\n"));
00744         return NS_OK;
00745     }
00746 
00747     nsresult rv = OnSocketReadable();
00748     if (NS_FAILED(rv))
00749         CloseTransaction(mTransaction, rv);
00750 
00751     return NS_OK;
00752 }
00753 
00754 //-----------------------------------------------------------------------------
00755 // nsHttpConnection::nsIOutputStreamCallback
00756 //-----------------------------------------------------------------------------
00757 
00758 NS_IMETHODIMP
00759 nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
00760 {
00761     NS_ASSERTION(out == mSocketOut, "unexpected stream");
00762     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00763 
00764     // if the transaction was dropped...
00765     if (!mTransaction) {
00766         LOG(("  no transaction; ignoring event\n"));
00767         return NS_OK;
00768     }
00769 
00770     nsresult rv = OnSocketWritable();
00771     if (NS_FAILED(rv))
00772         CloseTransaction(mTransaction, rv);
00773 
00774     return NS_OK;
00775 }
00776 
00777 //-----------------------------------------------------------------------------
00778 // nsHttpConnection::nsITransportEventSink
00779 //-----------------------------------------------------------------------------
00780 
00781 NS_IMETHODIMP
00782 nsHttpConnection::OnTransportStatus(nsITransport *trans,
00783                                     nsresult status,
00784                                     PRUint64 progress,
00785                                     PRUint64 progressMax)
00786 {
00787     if (mTransaction)
00788         mTransaction->OnTransportStatus(status, progress);
00789     return NS_OK;
00790 }
00791 
00792 //-----------------------------------------------------------------------------
00793 // nsHttpConnection::nsIInterfaceRequestor
00794 //-----------------------------------------------------------------------------
00795 
00796 // not called on the socket transport thread
00797 NS_IMETHODIMP
00798 nsHttpConnection::GetInterface(const nsIID &iid, void **result)
00799 {
00800     // NOTE: This function is only called on the UI thread via sync proxy from
00801     //       the socket transport thread.  If that weren't the case, then we'd
00802     //       have to worry about the possibility of mTransaction going away
00803     //       part-way through this function call.  See CloseTransaction.
00804     NS_ASSERTION(PR_GetCurrentThread() != gSocketThread, "wrong thread");
00805  
00806     if (mTransaction) {
00807         nsCOMPtr<nsIInterfaceRequestor> callbacks;
00808         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
00809         if (callbacks)
00810             return callbacks->GetInterface(iid, result);
00811     }
00812 
00813     return NS_ERROR_NO_INTERFACE;
00814 }