XMLHttpRequest를 중단하는 내부(클라이언트 및 서버)
그래서 저는 비동기 자바스크립트 요청을 중단할 때 발생하는 실제 기저 동작이 궁금합니다.이 질문에 관련된 내용이 있었지만 아직 포괄적인 내용을 찾지 못했습니다.
제 가정은 항상 요청을 중단하면 브라우저가 연결을 닫고 처리를 완전히 중지하므로, 서버가 그렇게 설정되어 있으면 서버가 동일하게 처리하게 된다는 것입니다.하지만 여기에는 브라우저 특유의 별난 점이나 엣지 케이스가 있을 수 있다고 생각합니다.
제가 이해하는 바는 다음과 같습니다. 필요하다면 누군가가 수정할 수 있고 앞으로 다른 사람들에게 좋은 참고가 될 수 있기를 바랍니다.
- XHR 요청 클라이언트 측을 중단하면 브라우저가 내부적으로 소켓을 닫고 처리를 중지합니다.저는 단순히 들어오는 데이터를 무시하고 메모리를 낭비하는 것보다는 이러한 행동을 기대합니다.하지만 저는 IE에 돈을 걸지 않을 겁니다.
- 서버에서 중단된 요청은 해당 서버에서 실행 중인 작업을 수행합니다.
- PHP의 기본 동작은 클라이언트 소켓이 닫혔을 때 처리를 중지하는 것으로 알고 있습니다.
ignore_user_abort()
호출되었습니다.따라서 XHR 연결을 닫으면 서버 전력도 절약됩니다. - node.js에서 이것이 어떻게 처리될 수 있는지 정말 궁금합니다. 거기서 약간의 수동 작업이 필요할 것 같습니다.
- 다른 서버 언어/프레임워크와 그들이 어떻게 작동하는지에 대해서는 전혀 모르지만, 만약 누군가가 구체적으로 기여하고 싶다면 기꺼이 여기에 그것들을 추가하겠습니다.
- PHP의 기본 동작은 클라이언트 소켓이 닫혔을 때 처리를 중지하는 것으로 알고 있습니다.
고객 입장에서는 가장 보기 좋은 곳이 소스에 있으니 이렇게 해요! :)
XMLHtpRequest 의 구현에 abort
메서드(XMLHtpRequest.cpp의 줄 1083-1119):
void XMLHttpRequest::abort()
{
WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
// internalAbort() clears |m_loader|. Compute |sendFlag| now.
//
// |sendFlag| corresponds to "the send() flag" defined in the XHR spec.
//
// |sendFlag| is only set when we have an active, asynchronous loader.
// Don't use it as "the send() flag" when the XHR is in sync mode.
bool sendFlag = m_loader;
// internalAbort() clears the response. Save the data needed for
// dispatching ProgressEvents.
long long expectedLength = m_response.expectedContentLength();
long long receivedLength = m_receivedLength;
if (!internalAbort())
return;
// The script never gets any chance to call abort() on a sync XHR between
// send() call and transition to the DONE state. It's because a sync XHR
// doesn't dispatch any event between them. So, if |m_async| is false, we
// can skip the "request error steps" (defined in the XHR spec) without any
// state check.
//
// FIXME: It's possible open() is invoked in internalAbort() and |m_async|
// becomes true by that. We should implement more reliable treatment for
// nested method invocations at some point.
if (m_async) {
if ((m_state == OPENED && sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
ASSERT(!m_loader);
handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength);
}
}
m_state = UNSENT;
}
그래서 이것으로 볼 때, 대부분의 그룬트 작업은 그 안에서 이루어 진 것으로 보입니다.internalAbort
, 다음과 같이 보입니다.
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) {
// When the stream is already closed (including canceled from the
// user), |error| does nothing.
// FIXME: Create a more specific error.
m_responseStream->error(DOMException::create(!m_async && m_exceptionCode ? m_exceptionCode : AbortError, "XMLHttpRequest::abort"));
}
clearResponse();
clearRequest();
if (!m_loader)
return true;
// Cancelling the ThreadableLoader m_loader may result in calling
// window.onload synchronously. If such an onload handler contains open()
// call on the same XMLHttpRequest object, reentry happens.
//
// If, window.onload contains open() and send(), m_loader will be set to
// non 0 value. So, we cannot continue the outer open(). In such case,
// just abort the outer open() by returning false.
RefPtr<ThreadableLoader> loader = m_loader.release();
loader->cancel();
// If abort() called internalAbort() and a nested open() ended up
// clearing the error flag, but didn't send(), make sure the error
// flag is still set.
bool newLoadStarted = m_loader;
if (!newLoadStarted)
m_error = true;
return !newLoadStarted;
}
저는 C++ 전문가는 아니지만 보기엔internalAbort
몇 가지 작업을 수행합니다.
- 지정된 수신 응답에 대해 현재 수행 중인 모든 처리를 중지합니다.
- 요청/응답과 관련된 모든 내부 XHR 상태를 지웁니다.
- 검사관에게 XHR이 실패했음을 보고하라고 말합니다. (정말 흥미롭군요!좋은 콘솔 메시지가 바로 여기서 비롯되었을 겁니다.)
- 응답 스트림의 "레거시" 버전 또는 응답 스트림의 최신 버전을 닫습니다(이 부분이 질문과 관련된 가장 흥미로운 부분일 수 있습니다).
- 오류가 제대로 전파되는지 확인하기 위해 몇 가지 스레딩 문제를 다룹니다(댓글 감사합니다).
한참을 헤집다가 HttpResponseBodyDrainer(110-124번 행)에서 재미있는 기능을 발견했습니다.Finish
제가 보기엔 요청이 취소되면 결국 호출되는 것처럼 보입니다.
void HttpResponseBodyDrainer::Finish(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
if (session_)
session_->RemoveResponseDrainer(this);
if (result < 0) {
stream_->Close(true /* no keep-alive */);
} else {
DCHECK_EQ(OK, result);
stream_->Close(false /* keep-alive */);
}
delete this;
}
알고보니.stream_->Close
, BasicHttpStream에서는 HttpStreamParser로 위임합니다.::Close
, A가 주어졌을 때non-reusable
flag(에 표시된 것처럼 요청이 중단될 때 발생하는 것으로 보임)HttpResponseDrainer
), 소켓을 닫습니다.
void HttpStreamParser::Close(bool not_reusable) {
if (not_reusable && connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
}
따라서 클라이언트에서 발생하는 일에 관해서는, 적어도 Chrome의 경우에는 초기 직관이 정확했던 것 같습니다. :) 대부분의 별난 점과 엣지 케이스는 브라우저별 처리뿐만 아니라 스케줄링/이벤트 알림/스레딩 문제와 관련이 있는 것 같습니다.중단된 XHR을 devtools 콘솔에 보고합니다.
서버 측면에서는 노드의 경우JS http response 개체에서 'close' 이벤트를 듣고 싶습니다.간단한 예는 다음과 같습니다.
'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);
완료하기 전에 이를 실행하고 요청을 취소해 보십시오.콘솔에 오류가 나타납니다.
이것이 유용한 것을 발견하셨길 바랍니다.크롬/블링크 소스를 파헤치는 것은 정말 재미있었습니다 :)
언급URL : https://stackoverflow.com/questions/24258279/internals-client-and-server-of-aborting-an-xmlhttprequest
'source' 카테고리의 다른 글
j쿼리 텍스트() 및 줄 바꿈 (0) | 2023.10.29 |
---|---|
같은 값을 가진 연속 행의 최대 카운트(Max Count) maria(마리아)를 선택합니다.DB mysql (0) | 2023.10.29 |
Is sending data via UDP sockets on the same machine reliable? (0) | 2023.10.29 |
리눅스에서 C/C++로 웹 서버를 쓰는 방법 (0) | 2023.10.29 |
Oracle - 독립 실행형 프로시저 또는 기능 대신 패키지를 사용해야 하는 이유 (0) | 2023.10.29 |