For the client, the best place to look is in the source, so let me do it! :)
Let's look at the Blink implementation of the XMLHttpRequest abort method (lines 1083-1119 in XMLHttpRequest.cpp ):
void XMLHttpRequest::abort() { WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
So it looks like most of grunt's work is done inside internalAbort , which looks like this:
bool XMLHttpRequest::internalAbort() { m_error = true; if (m_responseDocumentParser && !m_responseDocumentParser->isStopped()) m_responseDocumentParser->stopParsing(); clearVariablesForLoading(); InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this); if (m_responseLegacyStream && m_state != DONE) m_responseLegacyStream->abort(); if (m_responseStream) {
I am not an expert in C ++, but from his views, internalAbort performs several actions:
- Stops processing that is currently running on a given incoming response
- Removes any internal XHR state associated with the request / response
- Tells the inspector to report that XHR failed (this is really interesting! I bet where these nice console messages come up)
- Closes either the "obsolete" version of the response stream or the modern version of the response stream (this is probably the most interesting part related to your question).
- Contains some streaming issues to ensure the error propagates correctly (thanks, comments).
After many searches, I met an interesting function in the HttpResponseBodyDrainer (lines 110-124) called Finish , which for me looks like something that will eventually be called when the request is canceled:
void HttpResponseBodyDrainer::Finish(int result) { DCHECK_NE(ERR_IO_PENDING, result); if (session_) session_->RemoveResponseDrainer(this); if (result < 0) { stream_->Close(true ); } else { DCHECK_EQ(OK, result); stream_->Close(false ); } delete this; }
It turns out that stream_->Close , at least in BasicHttpStream, delegates to HttpStreamParser ::Close , which, if the flag is non-reusable (which seems to happen when the request is interrupted, as shown in HttpResponseDrainer ), closes the socket:
void HttpStreamParser::Close(bool not_reusable) { if (not_reusable && connection_->socket()) connection_->socket()->Disconnect(); connection_->Reset(); }
So, from the point of view of what happens on the client, at least in the case of Chrome, it looks like your initial intuitions were correct, as far as I can tell :) it looks like most quirks and regional affairs have to be done with a schedule / notification about events / streams, as well as browser processing, for example reporting interrupted XHR to the devtools console.
In server terms, in the case of NodeJS, you want to listen to the close " event in the HTTP response object. Here is a simple example:
'use strict'; var http = require('http'); var server = http.createServer(function(req, res) { res.on('close', console.error.bind(console, 'Connection terminated before response could be sent!')); setTimeout(res.end.bind(res, 'yo'), 2000); }); server.listen(8080);
Try to start and cancel the request before it is completed. On the console you will see an error message.
Hope you found this helpful. Digging through the source of Chromium / Blink was a lot of fun :)