Only get response headers with Req?

I’m currently using Req in an application, and as I’m thinking about potential security concerns, I have two scenarios in mind where I would like to check the response headers before getting the full body.

For webhooks, I really only care about the headers. If, for example, someone were to sign up and respond to my webhook request with a large payload, I would rather not load the full response payload into memory. Since all I really care about is the HTTP response status code, is it possible to get only that? I was looking at doing something like this, but wondered if there’s a better way:

resp = Req.get!(endpoint.url, into: :self)
Req.cancel_async_response(resp)

For downloading files from URLs sent to my API, I would like to check that the file size and mime type are within our expected parameters before downloading the body. Is this the best way to do that?

resp = Req.get!(file.url, into: :self)
mime_type = Req.Response.get_header(resp, "content-type")
content_length = Req.Response.get_header(resp, "content-length")
  
cond do
  mime_type not in @supported_mime_types ->
    Req.cancel_async_response(resp)
    {:error, :unsupported_file_type}
  
  content_length > @max_file_size ->
    Req.cancel_async_response(resp)
    {:error, :file_too_big}
  
  true ->
    {:ok, resp.body}
end

Thanks!

Req.head/2 ?

3 Likes

I think making a HEAD request is the most straightforward. Other than that, either into: :self + cancel (though you’d probably have received some messages by then) or into: fun:

iex> Req.get!("https://httpbin.org/status/201", into: fn {req, resp}, acc -> {:halt, {req, resp}} end)
%Req.Response{
  status: 201,
  headers: %{
    "access-control-allow-credentials" => ["true"],
    "access-control-allow-origin" => ["*"],
    "connection" => ["keep-alive"],
    "content-length" => ["0"],
    "content-type" => ["text/html; charset=utf-8"],
    "date" => ["Fri, 08 Nov 2024 19:55:52 GMT"],
    "server" => ["gunicorn/19.9.0"]
  },
  body: "",
  trailers: %{},
  private: %{}
}
4 Likes

Oh, for checking content-type, file size, etc, I’d use into: fun too. It’s more efficient than into: :self in that it uses the socket in passive mode so you can easily halt before reading any body part.

1 Like

I think this makes a lot of sense for the download scenario if I make the HEAD request and then perform the GET if everything matches. But for the webhook use case, I’m POSTing data to the endpoint, and unless I don’t understand the spec properly, you can’t do that with a HEAD request, right?

Perfect. Thank you!