在特殊的應用情境中,客戶端請求某些資源時可能會需要透過中介 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
沒有留言:
張貼留言