在特殊的應用情境中,客戶端請求某些資源時可能會需要透過中介 Proxy 去存取資料,本篇文章引用 【用 Elixir 和 hackney 做 proxy】這篇文章所實作的 Proxy ,雖然作者最後自己寫了一個 PlugProxy 套件去取代這樣的手法,但本文就以當作練習的角度觀察 cowboy, hackney。
原本文章使用的 http client 是 hackney ,他是原來 erlang 的原生套件,最後用來開啟 http proxy server 的是 cowboy 的框架。
建立一個相關的專案:
mix new proxyserver --sup
相依性套件安裝
mix.exs:
defp deps do
[
{:hackney, github: "benoitc/hackney"},
{:plug_cowboy, "~> 2.0"}
]
end新增後,使用指令安裝:
mix deps.get
安裝完成後,在 proxyserver/lib/proxyserver 建立一個檔案 Proxy.ex,內容是:
defmodule Proxyserver.Proxy do
import Plug.Conn
def init(options), do: options
# 會被任何的 http 進來的流量呼叫
def call(conn, _) do
target_method = method2atom(conn.method)
target_url = url_builder(conn)
{:ok, c} = :hackney.request(target_method, target_url, conn.req_headers, :stream, [])
conn |> handle_transfer_out(c) |> handle_transfer_back(c)
end
# 把 method 字串轉成 atom 模式: "GET" -> :get
defp method2atom(method), do: method |> String.downcase |> String.to_atom
# 製作一個字串串接的 url with query string
defp url_builder(conn) do
dist_url = "http://localhost:8888" <> conn.request_path
case conn.query_string do
"" -> dist_url
any_query_string -> dist_url <> "?" <> any_query_string
end
end
# 將內容轉手請求出去
defp handle_transfer_out(conn, client) do
case read_body(conn, []) do
# 如果是 :ok 讀完的情形,就把 conn 丟回去
{:ok, body, conn} ->
:hackney.send_body(client, body)
conn
# 如果是 :more 還沒讀完的情形,就繼續處理自己
{:more, body, conn} ->
:hackney.send_body(client, body)
handle_transfer_out(conn, client)
end
end
# 將 proxy 內容丟回去給使用者
defp handle_transfer_back(conn, client) do
{:ok, status, headers, client} = :hackney.start_response(client)
{:ok, body} = :hackney.body(client)
%{conn | resp_headers: headers} |> send_resp(status, body)
end
end然後,在 proxyserver/lib/proxyserver/ 目錄底下可以找到 application.ex ,改寫 children 內容如下:
children = [
{Plug.Cowboy, scheme: :http, plug: Proxyserver.Proxy, options: [port: 8081]}
]啟動伺服器:
mix run --no-halt
也可以用 iex 來啟動伺服器做 debug:
iex -S mix
然後,針對 localhost:8888/test.html 以取得該資料為主,在 elixir 啟動時,訪問 localhost:8081/test.html 就會看到轉手 test.html 的資料。
Reference:
https://zespia.tw/blog/2016/07/24/build-proxy-with-elixir-and-hackney/
https://github.com/benoitc/hackney/tree/master/doc
https://elixirforum.com/t/accessing-the-request-body-in-plug/15975/2
https://hexdocs.pm/plug/Plug.Conn.Adapter.html#c:read_req_body/2
https://github.com/tallarium/reverse_proxy_plug/tree/master/lib
https://github.com/elixir-plug/plug_cowboy
沒有留言:
張貼留言