nsDocShell as an example client of the nsIHttpChannel API
nsDocShell::LoadURI(string)- create
nsIURIfrom string
- create
nsDocShell::LoadURI(nsIURI)- creates 2
nsIInputStreamfor read response from; passes them with URI to ...
- creates 2
nsDocShell::InternalLoadnsDocShell::DoURILoad- opens the
nsIChannelfor the URI (NS_NewChannel) - if "http:", it will be an
nsIHttpChannel
- opens the
nsDocShell::DoChannelLoadnsURILoader::OpenURI- passes an
nsIStreamListenerpointer, 'loader' tonsURILoader::OpenChannel- it creates annsDocumentOpenInfoobject, which implementsnsIStreamListener, i.e. hasOnStartRequest,OnStopRequest,OnDataAvailable, the three functions in which channel responses are received asynchronously.
- passes an
nsHttpChannel::AsyncOpen- called from
OpenURI;OpenChannelisn'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::AsyncOpennsHttpChannel::BeginConnect()- creates
nsHttpConnectionInfoobject 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, andInits it withmRequestHead(the request headers) andmUploadStream(which was created from the request data in channel setup) - gets an
nsIAsyncInputStream(for the response; corresponds to thensPipeInputStreamfor the response stream pipe) - passes it to
nsInputStreamPump
- creates new
nsHttpChannel::gHttpHandler->InitiateTransaction(called fromConnect)- This is the global
nsHttpHandlerobject, which adds the transaction to thensHttpConnectionMgr(one of these pernsHttpHandler).
- This is the global
nsHttpConnectionMgr::PostEvent- creates an
nsConnEventwith params including the handler function,nsHttpConnectionMgr::OnMsgNewTransaction, and the recently creatednsHttpTransaction. - dispatches the
nsConnEventto 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::OnMsgNewTransactionnsHttpConnectionMgr::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>calledmCTinnsHttpConnectionMgr. - 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:
nsConnectionEntryhas a singlensHttpConnectionInfoobject attached, a pending queue ofnsHttpTransactions, and 3 arrays for connections:- active
nsHttpConnections - idle
nsHttpConnections nsHalfOpenSockets
- active
- (Notice the check that we're on
nsHttpConnectionMgr::TryDispatchTransaction- There is a series of decisions about whether
DispatchTransactionis 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::DispatchTransactionnsHttpConnectionMgr::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::OnOutputStreamReadynsHttpConnection::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::ResumeRecvnsHttpTransaction::ReadSegmentsReadRequestSegmentis 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::OnInputStreamReadynsHttpConnection::OnSocketReadablensHttpTransaction::WriteSegmentsnsHttpConnection::OnWriteSegment- passes bytes from
mSocketIn->Readto the transaction's pipe
- passes bytes from
nsPipeOutputStream::WriteSegmentsnsPipe::AdvanceWriteCursornsPipeInputStream::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
nsPipeEventsobject. Once this object goes out of scope,mCallback->OnInputStreamReadyis 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->OnInputStreamReadyfunction.
- 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::OnStateTransferandnsInputStreamPump::OnStateStop. - These functions call
nsIStreamListener::OnStartRequest,nsIStreamListener::OnDataAvailableandnsIStreamListener::OnStopRequestrespectively.
- This function in turn calls
And that brings us back to the nsHttpChannel API and nsDocShell gets its data.
