Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing connection information to the application. #87

Open
CrowdHailer opened this issue Feb 7, 2018 · 2 comments
Open

Exposing connection information to the application. #87

CrowdHailer opened this issue Feb 7, 2018 · 2 comments

Comments

@CrowdHailer
Copy link
Owner

CrowdHailer commented Feb 7, 2018

Certain applications require knowledge of the connection and not just the HTTP message that was sent. For example inspecting a client ssl certificate or blacklisting particular ip addresses.

Adding this to the request is not desirable because:

1 it conflates the transport information with the HTTP protocol
2 The request struct sent by the client should be identical to the request struct handled by the server. Assuming both client and server use Raxx.
3 It is also possible to have HTTP without a connection. Mailing Requests and Responses would work if anyone wanted to do that.

Option 1

Ace could have an entry in the process dictionary of the worker. The ace module could the expose several reader functions that work only in the context of a worker.

defmodule MyApp do
  def handle_request(_request, _state) do
    peer_ip = Ace.peer_ip()

    response(:ok)
  end
end

Somewhat ugly to use the process dictionary, however as all exposed functions are for derived properties the cannot be used to cause side effects.

Option 2

handle_request/2 (and all other callbacks) can have a handle_request/3 partner that takes request, channel, state with the default behaviour to fallback to the arity 2 implementation.

The downside of this approach is raxx is understanding both HTTP and some concept of the transport layer. e.g. would the channel object be an Ace.Connection or a Raxx.Connection.

Option 3

A server specific callback for connecting. e.g.

defmodule MyApp do
  use Ace.HTTP.Service, [port: 8080]

  @impl Ace.HTTP.Server
  def connect(channel, config) do # 1.
    Logger.metadata(client_ip: channel.ip)
    {:ok, config}
  end

  @impl Raxx.Server
  def handle_request(_request, _state) do
    response(:ok)
  end
end

Advantages this gives people an answer to what does init look like, it's replaced with connect.

There is a choice at 1. do we pass a connection or channel object. if channel then no error case should stop the complete connection, but the callback is executed in the same process as the handle_*. If connection then its only executed once for potentially many requests. but it would be possible to use it to stop the connection. i.e. blacklisted ips

@CrowdHailer
Copy link
Owner Author

This has been partially tackled by #96

It is not at least possible to access the socket and with that access as anything about peer ip etc

@Nicd
Copy link

Nicd commented Jun 26, 2018

Just adding a workaround that I use in the meantime, for anyone else that might stumble upon this issue report:

  def get_ace_ip() do
    with info <- Process.get(Ace.HTTP.Channel),
         {:tcp, socket} <- info.socket,
         {:ok, {ip, _}} <- :inet.peername(socket) do
      ip
    else
      _ -> nil
    end
  end

You can run that in a request handler and you'll get either the requesting IP or nil as a result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants