2014-06-17 9 views
6

को निरस्त करने के आंतरिक (क्लाइंट और सर्वर) तो मैं एसिंक जावास्क्रिप्ट अनुरोध को निरस्त करते समय होने वाले वास्तविक अंतर्निहित व्यवहारों के बारे में उत्सुक हूं। this question में कुछ संबंधित जानकारी थी लेकिन मुझे अभी तक कुछ भी व्यापक नहीं मिला है।एक XMLHttpRequest

मेरी धारणा हमेशा यह रही है कि अनुरोध को निरस्त करने से ब्राउजर कनेक्शन बंद कर देता है और इसे पूरी तरह से संसाधित करना बंद कर देता है, इस प्रकार सर्वर को ऐसा करने के लिए सेटअप करने के कारण ऐसा ही होता है। मुझे कल्पना है कि यहां ब्राउज़र-विशिष्ट क्विर्क या किनारे के मामले हो सकते हैं, जिन पर मैं सोच नहीं रहा हूं।

मेरी समझ निम्नानुसार है, मुझे आशा है कि अगर कोई आवश्यक हो तो कोई इसे सही कर सकता है और यह दूसरों के आगे जाने के लिए एक अच्छा संदर्भ हो सकता है।

  • एक्सएचआर अनुरोध क्लाइंटसाइड को छोड़कर ब्राउज़र को आंतरिक रूप से सॉकेट को बंद करने और इसे संसाधित करना बंद कर देता है। मैं स्मृति में बर्बाद होने और बर्बाद करने वाले डेटा को अनदेखा करने के बजाय इस व्यवहार की अपेक्षा करता हूं। मैं उस पर आईई पर सट्टा नहीं कर रहा हूं।
  • सर्वर पर एक गर्भपात अनुरोध वहाँ क्या चल रहा है अप करने के लिए होगा:
    • मैं जानता हूँ कि डिफ़ॉल्ट व्यवहार पीएचपी साथ प्रसंस्करण को रोकने के लिए जब ग्राहक सॉकेट बंद कर दिया है, जब तक कि ignore_user_abort() बुलाया गया है है। तो एक्सएचआर कनेक्शन बंद करने से आपको सर्वर की शक्ति भी बचाती है।
    • मुझे वास्तव में यह जानने में दिलचस्पी है कि इसे node.js में कैसे प्रबंधित किया जा सकता है, मुझे लगता है कि कुछ मैन्युअल कार्य की आवश्यकता होगी।
    • मुझे वास्तव में अन्य सर्वर भाषाओं/ढांचे के बारे में कोई जानकारी नहीं है और वे कैसे व्यवहार करते हैं लेकिन यदि कोई विनिर्देशों का योगदान करना चाहता है तो मुझे उन्हें यहां जोड़ने में खुशी होगी।
+0

एचएम, [spec] (http://www.w3.org/TR/XMLHttpRequest/#the-abort() -method) असामान्य रूप से अस्पष्ट है क्या "fetch' उदाहरणों को रद्द करना" का अर्थ है – Bergi

+0

हाँ, अनुरोध में समाप्त होने की परिभाषा के तहत, "इस ऑब्जेक्ट द्वारा खोले गए फ़ेच एल्गोरिदम के किसी भी उदाहरण को रद्द करें" में सबसे अच्छी जानकारी है। सॉर्टोफ का तात्पर्य है कि सॉकेट बंद होना चाहिए लेकिन यह निश्चित रूप से स्पष्ट नहीं है .. – pospi

उत्तर

10

ग्राहक के लिए, सबसे अच्छी जगह देखने के लिए स्रोत में है, इसलिए चलो इसे करते हैं! :)

के XMLHttpRequest के abort विधि (लाइनों XMLHttpRequest.cpp में 1083-1119) की Blink के कार्यान्वयन पर नजर डालते हैं:

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; 
} 

मैं कोई सी ++ विशेषज्ञ हूँ लेकिन इसके बारे में दिखता से, internalAbort कुछ चीजें करता है:

  • किसी भी प्रसंस्करण यह वर्तमान में एक दिया भेजे प्रतिक्रिया पर कर रहा है रोकता
  • अनुरोध/प्रतिक्रिया
  • निरीक्षक को बताता है कि रिपोर्ट करने के लिए है कि एक्सएचआर में विफल रहा है के साथ जुड़े किसी भी आंतरिक एक्सएचआर राज्य बाहर साफ़ करता है (यह वाकई दिलचस्प है! मैं शर्त लगाता हूं कि यह अच्छा कंसोल संदेश उत्पन्न होता है)
  • या तो प्रतिक्रिया स्ट्रीम का "विरासत" संस्करण, या प्रतिक्रिया स्ट्रीम का आधुनिक संस्करण (यह शायद आपके प्रश्न से संबंधित सबसे दिलचस्प हिस्सा है)
  • सौदे कुछ थ्रेडिंग मुद्दों के साथ यह सुनिश्चित करने के लिए कि त्रुटि ठीक से प्रसारित हो (धन्यवाद, टिप्पणियां)।

चारों ओर से खुदाई का एक बहुत करने के बाद, मैं 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, प्रतिनिधियों, जो जब एक non-reusable ध्वज (जो होने के लिए जब अनुरोध निरस्त किया गया है, के रूप में HttpResponseDrainer में देखा प्रतीत होता है) को देखते हुए बंद करता है सॉकेट:

void HttpStreamParser::Close(bool not_reusable) { 
    if (not_reusable && connection_->socket()) 
    connection_->socket()->Disconnect(); 
    connection_->Reset(); 
} 

तो, क्या, ग्राहक पर होता है कम से कम क्रोम के मामले में के मामले में, ऐसा लगता है अपने प्रारंभिक अंतर्ज्ञान की तरह जहाँ तक सही रूप में मैं बता सकता हूँ :) quirks के सबसे तरह लगता थे और किनारे के मामलों को शेड्यूलिंग/घटना अधिसूचना/थ्रेडिंग मुद्दों के साथ-साथ ब्राउज़र-विशिष्ट हैंडलिंग, उदाहरण के साथ करना होता है devtools कंसोल को निरस्त XHR की रिपोर्टिंग।

सर्वर के संदर्भ में, नोडजेएस के मामले में आप http प्रतिक्रिया ऑब्जेक्ट पर 'close' event को सुनना चाहते हैं। यहां एक साधारण उदाहरण दिया गया है:

'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); 

इसे चलाने का प्रयास करें और इसे पूरा करने से पहले अनुरोध को रद्द कर दें। आपको अपने कंसोल पर एक त्रुटि दिखाई देगी।

आशा है कि आपको यह उपयोगी लगेगा। क्रोमियम/ब्लिंक स्रोत के माध्यम से खुदाई बहुत मजेदार थी :)

+0

वाह! आपने निश्चित रूप से वहां एक गहरी गोताखोरी की है। एक नज़र डालने के लिए धन्यवाद ढेर: डी मैंने अभी तक नोड कोड का परीक्षण नहीं किया है (मुझे यह भी याद नहीं है कि जब मैं यह पूछता हूं तो मूल रूप से किस परियोजना के साथ खेल रहा था), लेकिन मैं कल्पना करता हूं कि ब्राउजर के इस्तेमाल के आधार पर वाईएमएमवी की कल्पना है। – pospi

+0

यहां तक ​​कि गहराई से गोता लगाने के लिए, यहां 2 बहुत हालिया spec मुद्दे हैं: https://github.com/whatwg/xhr/issues/88 और https://github.com/whatwg/xhr/issues/94 – bernie