nsDocShell
as an example client of the nsIHttpChannel
API
nsDocShell::LoadURI(string)
- create
nsIURI
from string
- create
nsDocShell::LoadURI(nsIURI)
- creates 2
nsIInputStream
for read response from; passes them with URI to ...
- creates 2
nsDocShell::InternalLoad
nsDocShell::DoURILoad
- opens the
nsIChannel
for the URI (NS_NewChannel
) - if "http:", it will be an
nsIHttpChannel
- opens the
nsDocShell::DoChannelLoad
nsURILoader::OpenURI
- passes an
nsIStreamListener
pointer, 'loader' tonsURILoader::OpenChannel
- it creates annsDocumentOpenInfo
object, which implementsnsIStreamListener
, i.e. hasOnStartRequest
,OnStopRequest
,OnDataAvailable
, the three functions in which channel responses are received asynchronously.
- passes an
nsHttpChannel::AsyncOpen
- called from
OpenURI
;OpenChannel
isn't named the best, since the opening happens in the context ofOpenURI
, its calling function.
- called from
This is where it hits Necko code. But the interface for clients of Necko is important to consider:
- SEND REQUEST
- URI helps creates channel
- setup channel (headers, request data, response callback...)
channel->AsyncOpen
.
- RECEIVE RESPONSE
- get a callback to each of these:
nsIStreamListener::OnStartRequest
(header info)nsIStreamListener::OnDataAvailable
(data in single or multiple chunks)nsIStreamListener::OnStopRequest
(no more data from http)
- get a callback to each of these:
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::AsyncOpen
nsHttpChannel::BeginConnect()
- creates
nsHttpConnectionInfo
object for the channel - checks if we're proxying or not
- fires off the DNS prefetch request (dispatched to DNS thread pool)
- some other things
- creates
nsHttpChannel::Connect
- might to a SpeculativeConnect (pre open TCP socket)
nsHttpChannel::ContinueConnect
- some cache stuff
nsHttpChannel::SetupTransaction
- creates new
nsHttpTransaction
, andInit
s it withmRequestHead
(the request headers) andmUploadStream
(which was created from the request data in channel setup) - gets an
nsIAsyncInputStream
(for the response; corresponds to thensPipeInputStream
for the response stream pipe) - passes it to
nsInputStreamPump
- creates new
nsHttpChannel::gHttpHandler->InitiateTransaction
(called fromConnect
)- This is the global
nsHttpHandler
object, which adds the transaction to thensHttpConnectionMgr
(one of these pernsHttpHandler
).
- This is the global
nsHttpConnectionMgr::PostEvent
- creates an
nsConnEvent
with params including the handler function,nsHttpConnectionMgr::OnMsgNewTransaction
, and the recently creatednsHttpTransaction
. - dispatches the
nsConnEvent
to the socket thread
- creates an
Back to the context of nsHttpChannel::ContinueConnect
:
nsInputStreamPump->AsyncRead
- this pump calls
nsIAsyncInputStream::AsyncWait
(the input for the response stream pipe created with thensHttpTransaction
, 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.
- this pump calls
nsPipeInputStream::AsyncWait
- sets the callback to be used later for a response
- if a target is specified (in this case, the main thread), callback is proxied via an
nsInputStreamReadyEvent
, which is created now and may be called later - Otherwise, the callback would be called directly, when the socket is readable
Et 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::OnMsgNewTransaction
nsHttpConnectionMgr::ProcessNewTransaction
- (Notice the check that we're on
gSocketThread
) - Gets conn info (i.e. hostname and port) from the transaction, and then gets or creates a connection entry from the connection table, an
nsClassHashtable<nsCStringHashKey, nsConnectionEntry>
calledmCT
innsHttpConnectionMgr
. - If a connection entry already exists matching the required conn info, then that one will be used; otherwise a new one is created (
MakeNewConnection
- creates socket etc.). - Note:
nsConnectionEntry
has a singlensHttpConnectionInfo
object attached, a pending queue ofnsHttpTransactions
, and 3 arrays for connections:- active
nsHttpConnection
s - idle
nsHttpConnection
s nsHalfOpenSocket
s
- active
- (Notice the check that we're on
nsHttpConnectionMgr::TryDispatchTransaction
- There is a series of decisions about whether
DispatchTransaction
is called, along with good code comments: best to read the code for more detail. - Basically, if a connection is available...
- There is a series of decisions about whether
nsHttpConnectionMgr::DispatchTransaction
nsHttpConnectionMgr::DispatchAbstractTransaction
- the transaction is given an indirect reference to the connection (for use later, when the socket has received data from the far end).
nsHttpConnection::Activate
- this connection is passed the transaction
nsHttpConnection::OnOutputStreamReady
nsHttpConnection::OnSocketWritable
- tries to write the request data from the current transaction (
mTransaction
) - tells the transaction to now wait (
`ResumeRecv
)
- tries to write the request data from the current transaction (
nsHttpConnection::ResumeRecv
nsHttpTransaction::ReadSegments
ReadRequestSegment
is passed tomRequestStream->ReadSegments
- this function pointer is called and used to read the request bytes, which in turn calls ...
nsHttpConnection::OnReadSegment
- passes bytes read from request to
mSocketOut->Write
- passes bytes read from request to
Back to the context of OnSocketWritable
:
nsIAsyncInputStream::AsyncWait
(i.e.mSocketIn
)- sets the callback and waits.
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::OnInputStreamReady
nsHttpConnection::OnSocketReadable
nsHttpTransaction::WriteSegments
nsHttpConnection::OnWriteSegment
- passes bytes from
mSocketIn->Read
to the transaction's pipe
- passes bytes from
nsPipeOutputStream::WriteSegments
nsPipe::AdvanceWriteCursor
nsPipeInputStream::OnInputReadable
- so now the response is hitting the other end of the pipe, saying it has bytes to read
nsPipeEvents::NotifyInputReady
- puts the callback object into an
nsPipeEvents
object. Once this object goes out of scope,mCallback->OnInputStreamReady
is called. - Note: this callback may be a proxy object,
nsInputStreamReadyEvent
: it is a runnable that dispatches itself to a previously set target thread, and calls its internalmCallback->OnInputStreamReady
function.
- puts the callback object into an
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
- This function in turn calls
nsInputStreamPump::OnStateStart
,nsInputStreamPump::OnStateTransfer
andnsInputStreamPump::OnStateStop
. - These functions call
nsIStreamListener::OnStartRequest
,nsIStreamListener::OnDataAvailable
andnsIStreamListener::OnStopRequest
respectively.
- This function in turn calls
And that brings us back to the nsHttpChannel
API and nsDocShell
gets its data.