nsDocShell as an example client of the nsIHttpChannel API
nsDocShell::LoadURI(string)
nsIURI from stringnsDocShell::LoadURI(nsIURI)
nsIInputStream for read response from; passes them with URI to ...nsDocShell::InternalLoadnsDocShell::DoURILoad
nsIChannel for the URI (NS_NewChannel)nsIHttpChannelnsDocShell::DoChannelLoadnsURILoader::OpenURI
nsIStreamListener pointer, 'loader' to nsURILoader::OpenChannel - it creates an nsDocumentOpenInfo object, which implements nsIStreamListener, i.e. has OnStartRequest, OnStopRequest, OnDataAvailable, the three functions in which channel responses are received asynchronously.nsHttpChannel::AsyncOpen
OpenURI; OpenChannel isn't named the best, since the opening happens in the context of OpenURI, its calling function.This is where it hits Necko code. But the interface for clients of Necko is important to consider:
channel->AsyncOpen.nsIStreamListener::OnStartRequest (header info)nsIStreamListener::OnDataAvailable (data in single or multiple chunks)nsIStreamListener::OnStopRequest (no more data from http)This all happens on the main thread, in a non-blocking fashion: make your request on the main thread, then carry on and get the async response later, also on the main thread. There is an option to receive OnDataAvailable specifically on a non-main thread, but all other calls happen on the main thread.
Then in Necko Http code (still on the main thread for now):
nsHttpChannel::AsyncOpennsHttpChannel::BeginConnect()
nsHttpConnectionInfo object for the channelnsHttpChannel::Connect
nsHttpChannel::ContinueConnect
nsHttpChannel::SetupTransaction
nsHttpTransaction, and Inits it with mRequestHead (the request headers) and mUploadStream (which was created from the request data in channel setup)nsIAsyncInputStream (for the response; corresponds to the nsPipeInputStream for the response stream pipe)nsInputStreamPumpnsHttpChannel::gHttpHandler->InitiateTransaction (called from Connect)
nsHttpHandler object, which adds the transaction to the nsHttpConnectionMgr (one of these per nsHttpHandler).nsHttpConnectionMgr::PostEvent
nsConnEvent with params including the handler function, nsHttpConnectionMgr::OnMsgNewTransaction, and the recently created nsHttpTransaction.nsConnEvent to the socket threadBack to the context of nsHttpChannel::ContinueConnect:
nsInputStreamPump->AsyncRead
nsIAsyncInputStream::AsyncWait (the input for the response stream pipe created with the nsHttpTransaction, i.e. nsPipeInputStream::AsyncWait), with target thread set to the current thread, i.e. main. So, subsequent callbacks will be dispatched to the main thread.nsPipeInputStream::AsyncWait
nsInputStreamReadyEvent, which is created now and may be called laterEt voila, the transaction has been posted to the socket thread, and the main thread continues on, unblocked from network IO.
So, on the socket thread...
The event queue works around to nsHttpConnectionMgr::OnMsgNewTransaction (with the nsHttpTransaction passed as a param - Remember the event was posted earlier by InitiateTransaction from the main thread).
nsHttpConnectionMgr::OnMsgNewTransactionnsHttpConnectionMgr::ProcessNewTransaction
gSocketThread)nsClassHashtable<nsCStringHashKey, nsConnectionEntry> called mCT in nsHttpConnectionMgr.MakeNewConnection - creates socket etc.).nsConnectionEntry has a single nsHttpConnectionInfo object attached, a pending queue of nsHttpTransactions, and 3 arrays for connections:
nsHttpConnectionsnsHttpConnectionsnsHalfOpenSocketsnsHttpConnectionMgr::TryDispatchTransaction
DispatchTransaction is called, along with good code comments: best to read the code for more detail.nsHttpConnectionMgr::DispatchTransactionnsHttpConnectionMgr::DispatchAbstractTransaction
nsHttpConnection::Activate
nsHttpConnection::OnOutputStreamReadynsHttpConnection::OnSocketWritable
mTransaction)`ResumeRecv)nsHttpConnection::ResumeRecvnsHttpTransaction::ReadSegments
ReadRequestSegment is passed to mRequestStream->ReadSegments - this function pointer is called and used to read the request bytes, which in turn calls ...nsHttpConnection::OnReadSegment
mSocketOut->WriteBack to the context of OnSocketWritable:
nsIAsyncInputStream::AsyncWait (i.e. mSocketIn)
The HTTP request is now written to the socket, which has a callback to the nsHttpConnection...
Note: from what I can tell, there are some cases where the transaction is queued up to be written to the socket later if it's not writable now, or in the case of pipelining or SPDY where it's done in batches. But I'm not an expert in either of these things.
Once the socket is readable (more async behavior), nsHttpConnection::OnInputStreamReady is called on the socket thread.
nsHttpConnection::OnInputStreamReadynsHttpConnection::OnSocketReadablensHttpTransaction::WriteSegmentsnsHttpConnection::OnWriteSegment
mSocketIn->Read to the transaction's pipensPipeOutputStream::WriteSegmentsnsPipe::AdvanceWriteCursornsPipeInputStream::OnInputReadable
nsPipeEvents::NotifyInputReady
nsPipeEvents object. Once this object goes out of scope, mCallback->OnInputStreamReady is called.nsInputStreamReadyEvent: it is a runnable that dispatches itself to a previously set target thread, and calls its internal mCallback->OnInputStreamReady function.Remember that nsPipeInputStream::AsyncWait was called earlier, after the transaction was initially created and posted to the connection manager on the socket thread. And in that function it created a proxy callback because it wished to have OnInputStreamReady called on the main thread.
Back on the main thread:
nsInputStreamPump::OnInputStreamReady
nsInputStreamPump::OnStateStart, nsInputStreamPump::OnStateTransfer and nsInputStreamPump::OnStateStop.nsIStreamListener::OnStartRequest, nsIStreamListener::OnDataAvailable and nsIStreamListener::OnStopRequest respectively.And that brings us back to the nsHttpChannel API and nsDocShell gets its data.