Skip to content

Commit

Permalink
Merge 317c425 into dbb74b9
Browse files Browse the repository at this point in the history
  • Loading branch information
zhongwencool committed Dec 5, 2016
2 parents dbb74b9 + 317c425 commit d94d9aa
Show file tree
Hide file tree
Showing 21 changed files with 771 additions and 651 deletions.
7 changes: 7 additions & 0 deletions lib/maxwell.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,12 @@ defmodule Maxwell do
"""
use Maxwell.Builder

middleware Maxwell.Middleware.BaseUrl, "http://httpbin.org/"
middleware Maxwell.Middleware.Logger
middleware Maxwell.Middleware.Json

def test(path) do
put_path(path) |> get!
end
end

7 changes: 5 additions & 2 deletions lib/maxwell/adapter/adapter.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
defmodule Maxwell.Adapter do
@moduledoc """
Example see `Maxwell.Adapter.Ibrowse`
Define adapter behaviour
## Example
See `Maxwell.Adapter.Ibrowse`
"""
@type return_t :: {:ok, Maxwell.Conn.t} | {:ok, reference} | {:error, any}
@type return_t :: {:ok, Maxwell.Conn.t} | {:ok, reference} | {:error, any, Maxwell.Conn.t}
@callback call(Maxwell.Conn.t) :: return_t

end

83 changes: 28 additions & 55 deletions lib/maxwell/adapter/hackney.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,48 @@ if Code.ensure_loaded?(:hackney) do
"""

@doc """
Receives `%Maxwell.Conn{}`
Returns `{:ok, %Maxwell.Conn{}}` or `{:error, reason_term}` when synchronous request
Returns `{:ok, ref_integer}` or `{:error, reason_term}` when asynchronous requests(options add [respond_to: target_self])
* `conn` - `%Maxwell.Conn{}`
Returns `{:ok, %Maxwell.Conn{}}` or `{:error, reason_term, %Maxwell.Conn{}}`.
"""
def call(env) do
env = if target = env.opts[:respond_to] do
gatherer = spawn_link fn -> receive_response(env, target, nil , nil, nil) end
opts = env.opts |> List.keyreplace(:respond_to, 0, {:stream_to, gatherer})
%{env |opts: [:async| opts]}
else
env
end

env
def call(conn) do
conn
|> send_req
|> format_response(env)
|> format_response(conn)
end

defp send_req(%Maxwell.Conn{url: url, headers: headers, method: method, opts: opts, body: body}) do
headers = headers |> Map.to_list
body = body || ""
:hackney.request(method, url, headers, body, opts)
defp send_req(%Maxwell.Conn{url: url, req_headers: req_headers,
path: path,method: method, query_string: query_string,
opts: opts, req_body: req_body}) do
req_headers = req_headers |> Map.to_list
req_body = req_body || ""
url = url |> Maxwell.Conn.append_query_string(path, query_string)
:hackney.request(method, url, req_headers, req_body, opts)
end

defp format_response({:ok, status, headers, body}, env) when is_binary(body) do
{:ok, %{env | status: status,
headers: Enum.into(headers, %{}),
body: body}}
defp format_response({:ok, status, headers, body}, conn) when is_binary(body) do
{:ok, %{conn | status: status,
resp_headers: headers|> :maps.from_list,
req_body: nil,
state: :sent,
resp_body: body}}
end
defp format_response({:ok, status, headers, body}, env) do
defp format_response({:ok, status, headers, body}, conn) do
with {:ok, body} <- :hackney.body(body) do
{:ok,
%{env |status: status,
headers: Enum.into(headers, %{}),
body: body}}
%{conn |status: status,
resp_headers: headers|> :maps.from_list,
req_body: nil,
state: :sent,
resp_body: body}}
end
end
defp format_response({:ok, ref}, _env) do
{:ok, ref}
end
defp format_response({:ok, status, headers}, env) do
format_response({:ok, status, headers, ""}, env)
end
defp format_response({:error, _} = error, _env) do
error
defp format_response({:ok, status, headers}, conn) do
format_response({:ok, status, headers, ""}, conn)
end

# todo deal redicrect
# {:hackney_response, id, {redirect, to, headers}} when redirect in [:redirect, :see_other] ->
defp receive_response(env, target, status, headers, body) do
receive do
{:hackney_response, _id, {:status, new_status, _reason}} ->
receive_response(env, target, new_status, headers, body)
{:hackney_response, _id, {:headers, new_headers}} ->
receive_response(env, target, status, new_headers, body)
{:hackney_response, _id, {:error, reason}} ->
send(target, {:maxwell_response, {:error, reason, env}})
{:hackney_response, _id, :done} ->
response =
{:ok, status, headers, body}
|> format_response(env)
send(target, {:maxwell_response, response})
{:hackney_response, _id, append_body} ->
new_body = if body, do: body <> append_body, else: append_body
receive_response(env, target, status, headers, new_body)
end
defp format_response({:error, reason}, conn) do
{:error, reason, %{conn | state: :error}}
end

end
Expand Down
67 changes: 23 additions & 44 deletions lib/maxwell/adapter/ibrowse.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,40 @@ if Code.ensure_loaded?(:ibrowse) do
"""

@doc """
Receives `%Maxwell.Conn{}`
Returns `{:ok, %Maxwell.Conn{}}` or `{:error, reason_term}` when synchronous request
Returns `{:ok, ref_integer}` when asynchronous requests(options add [respond_to: target_pid])
* `conn` - `%Maxwell.Conn{}`
Returns `{:ok, %Maxwell.Conn{}}` or `{:error, reason_term, %Maxwell.Conn{}}`.
"""
def call(env) do
if target = env.opts[:respond_to] do
gatherer = spawn_link fn -> receive_response(env, target, nil, nil, nil) end
opts = env.opts |> List.keyreplace(:respond_to, 0, {:stream_to, gatherer})
env = %{env |opts: opts}
else
env
end
def call(conn) do
conn
|> send_req
|> format_response(env)
end

defp send_req(%Maxwell.Conn{url: url, headers: headers, method: method, opts: opts, body: body}) do
url = url |> to_char_list
headers = headers |> Map.to_list
{headers, body} = need_multipart_encode(headers, body)
:ibrowse.send_req(url, headers, method, body, opts)
|> format_response(conn)
end

defp receive_response(env, target, status, headers, body) do
receive do
{:ibrowse_async_headers, _, new_status, new_headers} ->
receive_response(env, target, new_status, new_headers, body)

{:ibrowse_async_response, _, append_body} ->
new_body = if body, do: body <> append_body, else: append_body
receive_response(env, target, status, headers, new_body)

{:ibrowse_async_response_end, _} ->
response = format_response({:ok, status, headers, body}, env)
send(target, {:maxwell_response, response})
end
defp send_req(%Maxwell.Conn{url: url, req_headers: req_headers,
query_string: query_string, path: path,
method: method, opts: opts, req_body: req_body}) do
url = url |> Maxwell.Conn.append_query_string(path, query_string) |> to_char_list
req_body = req_body || ""
req_headers = req_headers |> Map.to_list
:ibrowse.send_req(url, req_headers, method, req_body, opts)
end

defp format_response({:ibrowse_req_id, id}, _env), do: {:ok, id}
defp format_response({:ok, status, headers, body}, env) do
defp format_response({:ok, status, headers, body}, conn) do
{status, _} = status |> to_string |> Integer.parse
headers = Enum.into(headers, %{})
{:ok, %{env |status: status,
headers: headers,
body: body}
{:ok, %{conn |status: status,
resp_headers: headers |> :maps.from_list,
resp_body: body,
state: :sent,
req_body: nil}
}
end
defp format_response({:error, _} = error, _env) do
error
defp format_response({:error, reason}, conn) do
{:error, reason, %{conn | state: :error}}
end

defp need_multipart_encode(headers, {:multipart, multipart}) do
## todo support multipart
def need_multipart_encode(headers, {:multipart, multipart}) do
boundary = Maxwell.Multipart.new_boundary
body =
{fn(true) ->
Expand All @@ -71,7 +50,7 @@ if Code.ensure_loaded?(:ibrowse) do
headers = [{'Content-Type', "multipart/form-data; boundary=#{boundary}"}, {'Content-Length', len}|headers]
{headers, body}
end
defp need_multipart_encode(headers, body), do: {headers, body || []}
def need_multipart_encode(headers, body), do: {headers, body || []}

end

Expand Down

0 comments on commit d94d9aa

Please sign in to comment.