Repository: servo/servo
File: components/script/fetch.rs (Rust)

Code before:
    keep_alive: bool,
}

impl FetchCanceller {
    /// Create a FetchCanceller associated with a request,
    /// and a particular(public vs private) resource thread.
    pub(crate) fn new(
        request_id: RequestId,
        keep_alive: bool,
        core_resource_thread: CoreResourceThread,
    ) -> Self {
        Self {
            request_id: Some(request_id),
            core_resource_thread: Some(core_resource_thread),
            keep_alive,
        }
    }

    /// Step 1 of <https://fetch.spec.whatwg.org/#concept-fetch-group-terminate>
    pub(crate) fn cancel(&mut self) {
        // Step 1. For each fetch record record of fetchGroup’s fetch records,
        // if record’s controller is non-null and record’s request’s done flag
        // is unset and keepalive is false, terminate record’s controller.
        if self.keep_alive {
            return;
        }
        if let Some(request_id) = self.request_id.take() {
            // stop trying to make fetch happen
            // it's not going to happen

            if let Some(ref core_resource_thread) = self.core_resource_thread {
                // No error handling here. Cancellation is a courtesy call,
                // we don't actually care if the other side heard.
                cancel_async_fetch(vec![request_id], core_resource_thread);
            }
        }
    }

    /// Use this if you don't want it to send a cancellation request
    /// on drop (e.g. if the fetch completes)
    pub(crate) fn ignore(&mut self) {
        let _ = self.request_id.take();
    }
}

impl Drop for FetchCanceller {
    fn drop(&mut self) {
        self.cancel()
    }
}


Review comment (performance):
This doesn't seem to be correct, because even keep alive fetches can be aborted, see "Run these steps, but [abort when](https://infra.spec.whatwg.org/#abort-when) fetchParams is [canceled](https://fetch.spec.whatwg.org/#fetch-params-canceled):" of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch

As I understand it, this method is called as part of the fetch abort flow: https://github.com/servo/servo/blob/6c030bf585ffff04fe9dd2e794b3567cd5769e04/components/script/fetch.rs#L464

So the spec link is for "terminate", which is not the same thing. 

I think this method should instead follow https://fetch.spec.whatwg.org/#fetch-controller-abort. 

Regarding terminating a fetch group, I don't think we implement fetch group yets, and actually from the spec it's not clear to me when a fetch group is terminated. The spec just says what to do when it happens, but not what triggers it: see https://fetch.spec.whatwg.org/#concept-fetch-group-terminate I'm guessing a fetch group should terminate when the associated environment goes away(navigated away). 

So keep alive is something to handle among others at https://github.com/servo/servo/blob/575b7ab87b3ce0cb9bc5d7ce4ca99b3ab8e54c38/components/net/http_loader.rs#L1400

And it is also a way to run deferred fetches, which happen after the fetch group has terminated. 

But it is important to avoid conflating fetch aborting/cancelling with terminating a fetch group. 



Code after:
    keep_alive: bool,
}

impl FetchCanceller {
    /// Create a FetchCanceller associated with a request,
    /// and a particular(public vs private) resource thread.
    pub(crate) fn new(
        request_id: RequestId,
        keep_alive: bool,
        core_resource_thread: CoreResourceThread,
    ) -> Self {
        Self {
            request_id: Some(request_id),
            core_resource_thread: Some(core_resource_thread),
            keep_alive,
        }
    }

    pub(crate) fn cancel(&mut self) {
        if let Some(request_id) = self.request_id.take() {
            // stop trying to make fetch happen
            // it's not going to happen

            if let Some(ref core_resource_thread) = self.core_resource_thread {
                // No error handling here. Cancellation is a courtesy call,
                // we don't actually care if the other side heard.
                cancel_async_fetch(vec![request_id], core_resource_thread);
            }
        }
    }

    /// Use this if you don't want it to send a cancellation request
    /// on drop (e.g. if the fetch completes)
    pub(crate) fn ignore(&mut self) {
        let _ = self.request_id.take();
    }
}

impl Drop for FetchCanceller {
    fn drop(&mut self) {
        self.cancel()
    }
}

fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
    let mut builder =
        RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
            .method(request.method)
            .headers(request.headers)
            .unsafe_request(request.unsafe_request)
            .body(request.body)