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

introduce and fix dogma #21

Merged
merged 1 commit into from
Jan 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
use Mix.Config

import_config "#{Mix.env}.exs"

config :dogma,
rule_set: Dogma.RuleSet.All,
override: %{
LineLength => [ max_length: 128 ]
}
16 changes: 11 additions & 5 deletions lib/http_proxy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ defmodule HttpProxy do
@moduledoc """
HttpProxy is a simple http proxy.

If you access to particular URL like `http://localhost:8080`, then the http_proxy forward the request to other URL based on configuration.
If you access to particular URL like `http://localhost:8080`, then the http_proxy forward the request to other URL
based on configuration.

HttpProcy support two features.

1. HttpProxy support multiport proxy fearue.
2. HttpProxy support play/record proxied request.

Multiport proxy means that the proxy receives request with particular port and the proxy send request to other address. And you can set the feature against several mulatiple port.
Multiport proxy means that the proxy receives request with particular port and the proxy send request to other address.
And you can set the feature against several mulatiple port.

# Multiport proxy

For example, you set configuratio like the followings in your project and do `mix proxy`. Then the proxy send request to "http://google.com" if anyone snds to "http://localhost:4000". And the proxy send request to "http://yahoo.com" if anyone send request to "http://localhost:4001".
For example, you set configuratio like the followings in your project and do `mix proxy`. Then the proxy send request to
"http://google.com" if anyone snds to "http://localhost:4000". And the proxy send request to "http://yahoo.com" if anyone
send request to "http://localhost:4001".

## example
1. Set configuration as the following in `config/config.exs`.
Expand All @@ -39,9 +43,11 @@ defmodule HttpProxy do

# Play/Record proxied request.

If you set `record: true` in the configuration, the proxy export request into local file as JSON. You can export requests in particular path which is set as `export_path: "test/example"`. Default is "default".
If you set `record: true` in the configuration, the proxy export request into local file as JSON.
You can export requests in particular path which is set as `export_path: "test/example"`. Default is "default".

If you set `play: true` in the configuration, the proxy read mapping files and reply them when anyone accesses to the proxy via particular ports.
If you set `play: true` in the configuration, the proxy read mapping files and reply them when anyone accesses
to the proxy via particular ports.

Please read `test/data/mappings/*.json` if you would like to know the format of playing the reqponse.
"""
Expand Down
3 changes: 3 additions & 0 deletions lib/http_proxy/agent.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
defmodule HttpProxy.Agent do
@moduledoc """
Store play responses which is read from files.
"""

alias HttpProxy.Play.Data
alias HttpProxy.Play.Paths
Expand Down
6 changes: 5 additions & 1 deletion lib/http_proxy/format.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ defmodule HttpProxy.Format do
@type t :: %Plug.Conn{}

@spec pretty_json!(t, binary, binary, boolean) :: binary
def pretty_json!(conn, req_body, res_body_file, pretty) when pretty == true, do: pretty_json!(conn, req_body, res_body_file, false) |> JSX.prettify!
def pretty_json!(conn, req_body, res_body_file, true) do
conn
|> pretty_json!(req_body, res_body_file, false)
|> JSX.prettify!
end
def pretty_json!(conn, req_body, res_body_file, _) do
{a, b, c, d} = conn.remote_ip

Expand Down
15 changes: 10 additions & 5 deletions lib/http_proxy/handle.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ defmodule HttpProxy.Handle do
"""
@spec dispatch(t, param) :: t
def dispatch(conn, _opts) do
{:ok, client} = String.downcase(conn.method)
{:ok, client} = conn.method
|> String.downcase
|> String.to_atom
|> :hackney.request(uri(conn), conn.req_headers, :stream, [connect_timeout: req_timeout, recv_timeout: req_timeout])
|> :hackney.request(uri(conn), conn.req_headers, :stream,
[connect_timeout: req_timeout, recv_timeout: req_timeout])
{conn, ""}
|> write_proxy(client)
|> read_proxy(client)
Expand Down Expand Up @@ -125,7 +127,7 @@ defmodule HttpProxy.Handle do
end

defp play_conn(conn) do
conn = matched_path? conn, PlayPaths.has_path?(conn.request_path) || PlayPaths.has_path_pattern?(conn.request_path)
conn = matched_path? conn, PlayPaths.path?(conn.request_path) || PlayPaths.path_pattern?(conn.request_path)
# TODO: conn.resp_bodyに返すべき値を代入する
send_resp conn, conn.status, conn.resp_body
end
Expand Down Expand Up @@ -153,7 +155,9 @@ defmodule HttpProxy.Handle do
[
"body": PlayBody.get_body(resp),
"cookies": Map.to_list(Map.fetch!(res_json, "cookies")),
"headers": Map.to_list(Map.fetch!(res_json, "headers"))
"headers": res_json
|> Map.fetch!("headers")
|> Map.to_list
|> List.insert_at(0, {"Date", hd(Conn.get_resp_header conn, "Date")}),
"status_code": Map.fetch!(res_json, "status_code")
]
Expand All @@ -175,7 +179,8 @@ defmodule HttpProxy.Handle do
end

defp target_proxy(conn) do
Enum.reduce(proxies, [], fn proxy, acc ->
proxies
|> Enum.reduce([], fn proxy, acc ->
cond do
proxy.port == conn.port ->
[proxy | acc]
Expand Down
8 changes: 4 additions & 4 deletions lib/http_proxy/play/paths.ex
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ defmodule HttpProxy.Play.Paths do
@spec clear_path_patterns() :: :ok
def clear_path_patterns, do: ProxyAgent.put @patterns, nil

@spec has_path?(binary) :: binary | nil
def has_path?(path) do
@spec path?(binary) :: binary | nil
def path?(path) do
case Enum.member? paths, path do
false ->
nil
Expand All @@ -58,8 +58,8 @@ defmodule HttpProxy.Play.Paths do
end
end

@spec has_path_pattern?(binary) :: binary | nil
def has_path_pattern?(path) do
@spec path_pattern?(binary) :: binary | nil
def path_pattern?(path) do
Enum.find path_patterns, nil, fn pattern ->
Regex.match?(Regex.compile!(pattern), path)
end
Expand Down
27 changes: 18 additions & 9 deletions lib/http_proxy/play/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ defmodule HttpProxy.Play.Response do
defp validate(json) do
unless Map.has_key?(json, "request"), do: raise ArgumentError, "Should have request"

request_key = Map.keys(json["request"]) |> Enum.into(MapSet.new)
request_key = json["request"] |> Map.keys |> Enum.into(MapSet.new)
request_diff = MapSet.difference(@request_key_map, request_key)

response_key = Map.keys(json["response"]) |> Enum.into(MapSet.new)
response_key = json["response"] |> Map.keys |> Enum.into(MapSet.new)
response_diff = MapSet.difference(@response_key_map, response_key)

case {MapSet.member?(request_diff, @path_pattern), MapSet.member?(request_diff, @path)} do
case member_path?(request_diff, request_diff) do
{true, true} ->
raise ArgumentError, format_error_message(request_diff)
{false, false} ->
Expand All @@ -65,7 +65,7 @@ defmodule HttpProxy.Play.Response do
:ok
end

case {MapSet.member?(response_diff, @body), MapSet.member?(response_diff, @body_file)} do
case member_body?(response_diff, response_diff) do
{true, true} ->
raise ArgumentError, format_error_message(response_diff)
{false, false} ->
Expand All @@ -77,12 +77,21 @@ defmodule HttpProxy.Play.Response do
json
end

defp member_path?(request_diff, request_diff) do
{MapSet.member?(request_diff, @path_pattern), MapSet.member?(request_diff, @path)}
end

defp member_body?(response_diff, response_diff) do
{MapSet.member?(response_diff, @body), MapSet.member?(response_diff, @body_file)}
end

defp format_error_message(mapset) do
message = MapSet.to_list(mapset)
|> Enum.reduce("", fn item, acc ->
"#{item} #{acc}"
end)
|> IO.inspect
message = mapset
|> MapSet.to_list
|> Enum.reduce("", fn item, acc ->
"#{item} #{acc}"
end)
|> IO.inspect

"Response jsons must include arrtibute: #{message}"
end
Expand Down
3 changes: 2 additions & 1 deletion lib/http_proxy/record/response.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ defmodule HttpProxy.Record.Response do
export_body = HttpProxyFile.get_export_binary_path(conn.port)
filename = HttpProxyFile.filename(conn.path_info)

Format.pretty_json!(conn, req_body, export_body <> "/" <> filename, true)
conn
|> Format.pretty_json!(req_body, export_body <> "/" <> filename, true)
|> HttpProxyFile.export(export_mapping, filename)

res_body
Expand Down
3 changes: 2 additions & 1 deletion lib/http_proxy/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ defmodule HttpProxy.Supervisor do
## Callbacks

def init(:ok) do
proxies?(Handle.proxies)
Handle.proxies
|> proxies?
|> Enum.reduce([], fn proxy, acc ->
module_name = "HttpProxy.Handle#{proxy.port}"
[worker(Handle, [[proxy, module_name]], [id: String.to_atom(module_name)]) | acc]
Expand Down
4 changes: 3 additions & 1 deletion lib/http_proxy/utils/file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ defmodule HttpProxy.Utils.File do

@spec get_export_binary_path(integer | binary) :: String.t
def get_export_binary_path, do: export_path <> "/" <> response_files
def get_export_binary_path(port) when is_integer(port), do: export_path <> "/" <> Integer.to_string(port) <> "/" <> response_files
def get_export_binary_path(port) when is_integer(port) do
export_path <> "/" <> Integer.to_string(port) <> "/" <> response_files
end
def get_export_binary_path(port) when is_binary(port), do: export_path <> "/" <> port <> "/" <> response_files

@spec get_response_path() :: String.t
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ defmodule HttpProxy.Mixfile do
{:ex_doc, "~> 0.10", only: :dev},
{:exvcr, "~> 0.6", only: :test},
{:ex_parameterized, "~> 1.0", only: :test},
{:excoveralls, "~> 0.4", only: :test}
{:excoveralls, "~> 0.4", only: :test},
{:dogma, "~> 0.0", only: [:dev, :test]}
]
end

Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"coverex": {:hex, :coverex, "1.4.8"},
"cowboy": {:hex, :cowboy, "1.0.4"},
"cowlib": {:hex, :cowlib, "1.0.2"},
"dogma": {:hex, :dogma, "0.0.11"},
"earmark": {:hex, :earmark, "0.2.1"},
"ex_doc": {:hex, :ex_doc, "0.11.3"},
"ex_parameterized": {:hex, :ex_parameterized, "1.0.2"},
Expand Down
21 changes: 11 additions & 10 deletions test/http_proxy/http_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ defmodule HttpProxy.HttpTest do
File.rm_rf!(Application.get_env(:http_proxy, :export_path))
HttpProxy.TestHelper.set_record_mode

conn(:get, "http://localhost:8080/hoge/inu?email=neko&pass=123") |> HttpProxy.Handle.dispatch([])
conn(:post, "http://localhost:8080/hoge/inu", "nekoneko") |> HttpProxy.Handle.dispatch([])
conn(:put, "http://localhost:8080/hoge/inu", "nekoneko") |> HttpProxy.Handle.dispatch([])
conn(:delete, "http://localhost:8080/hoge/inu", "nekoneko") |> HttpProxy.Handle.dispatch([])
HttpProxy.Handle.dispatch(conn(:get, "http://localhost:8080/hoge/inu?email=neko&pass=123"), [])
HttpProxy.Handle.dispatch(conn(:post, "http://localhost:8080/hoge/inu", "nekoneko"), [])
HttpProxy.Handle.dispatch(conn(:put, "http://localhost:8080/hoge/inu", "nekoneko"), [])
HttpProxy.Handle.dispatch(conn(:delete, "http://localhost:8080/hoge/inu", "nekoneko"), [])

exported_files = case File.ls("test/example/8080/mappings") do
{:ok, files} -> files
Expand All @@ -31,8 +31,7 @@ defmodule HttpProxy.HttpTest do
File.rm_rf!(Application.get_env(:http_proxy, :export_path))
HttpProxy.TestHelper.set_play_mode

conn(:get, "http://localhost:8080/hoge/inu?email=neko&pass=123")
|> HttpProxy.Handle.dispatch([])
HttpProxy.Handle.dispatch(conn(:get, "http://localhost:8080/hoge/inu?email=neko&pass=123"), [])

exported_files = case File.ls("test/example/8080/mappings") do
{:ok, files} -> files
Expand All @@ -50,7 +49,7 @@ defmodule HttpProxy.HttpTest do
test_with_params "play responses agains particular request",
fn method, uri, expected_body ->
HttpProxy.TestHelper.set_play_mode
conn = conn(method, uri) |> HttpProxy.Handle.dispatch([])
conn = HttpProxy.Handle.dispatch(conn(method, uri), [])
assert conn.resp_body == expected_body
end do
[
Expand All @@ -66,7 +65,9 @@ defmodule HttpProxy.HttpTest do

test "no mached scheme" do
assert_raise ArgumentError, "no scheme", fn ->
conn(:get, "http://localhost:8082/")
conn = conn(:get, "http://localhost:8082/")

conn
|> Map.put(:scheme, :ftp)
|> HttpProxy.Handle.uri
end
Expand All @@ -75,15 +76,15 @@ defmodule HttpProxy.HttpTest do
test "raise error with play and record mode" do
HttpProxy.TestHelper.set_play_and_record_mode
assert_raise ArgumentError, "Can't set record and play at the same time.", fn ->
conn(:get, "http://localhost:8080/") |> HttpProxy.Handle.dispatch([])
HttpProxy.Handle.dispatch(conn(:get, "http://localhost:8080/"), [])
end
HttpProxy.TestHelper.set_play_mode
end

# send real request to outside server
test "set play and record false" do
HttpProxy.TestHelper.set_proxy_mode
conn = conn(:get, "http://localhost:8081/") |> HttpProxy.Handle.dispatch([])
conn = HttpProxy.Handle.dispatch(conn(:get, "http://localhost:8081/"), [])
assert conn.status == 200
HttpProxy.TestHelper.set_play_mode
end
Expand Down
8 changes: 4 additions & 4 deletions test/http_proxy/play/paths_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ defmodule HttpProxy.Play.PathsTest do

alias HttpProxy.Play.Paths

test_with_params "HttpProxy.Play.Paths#has_path?",
test_with_params "HttpProxy.Play.Paths#path?",
fn path, expected_path ->
assert Paths.has_path?(path) == expected_path
assert Paths.path?(path) == expected_path
end do
[
{"/request/path/neko", nil},
Expand All @@ -18,9 +18,9 @@ defmodule HttpProxy.Play.PathsTest do
]
end

test_with_params "HttpProxy.Play.Paths#has_path_pattern?",
test_with_params "HttpProxy.Play.Paths#path_pattern?",
fn path, expected_pattern ->
assert Paths.has_path_pattern?(path) == expected_pattern
assert Paths.path_pattern?(path) == expected_pattern
end do
[
{"/request_ok_case_neko", "\\A/request.*neko\\z"},
Expand Down
2 changes: 1 addition & 1 deletion test/http_proxy/supervisor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule HttpProxy.SupervisorTest do
assert modules == [HttpProxy.Handle]

{id, _, _, modules} = List.last(children)
assert "#{id}" == "Elixir.HttpProxy.Agent"
assert id == HttpProxy.Agent
assert modules == [HttpProxy.Agent]
end
end