1
0
Fork 0
mirror of https://github.com/edgurgel/httparrot synced 2025-04-06 00:32:34 -04:00

Compare commits

..

No commits in common. "master" and "0.0.1" have entirely different histories.

83 changed files with 710 additions and 2343 deletions

View file

@ -1,4 +0,0 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

View file

@ -1,54 +0,0 @@
name: CI
on: [push, pull_request]
jobs:
format:
name: Format & credo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.1
- name: Install OTP and Elixir
uses: erlef/setup-beam@v1
with:
otp-version: 26.x
elixir-version: 1.16.x
- name: Install dependencies
run: mix deps.get
- name: Compile with --warnings-as-errors
run: mix compile --warnings-as-errors
- name: Run "mix format"
run: mix format --check-formatted
test:
name: Test (Elixir ${{matrix.elixir}} | Erlang/OTP ${{matrix.otp}})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- otp: 26.x
elixir: 1.16.x
coverage: true
- otp: 27.x
elixir: 1.17.x
env:
MIX_ENV: test
steps:
- uses: actions/checkout@v2.3.1
- name: Install OTP and Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- name: Install dependencies
run: mix deps.get --only test
- name: Run tests
run: mix test --trace

26
.gitignore vendored
View file

@ -1,26 +1,4 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
/_build
/deps
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
httparrot-*.tar
# Temporary files, for example, from tests.
/tmp/

13
.travis.yml Normal file
View file

@ -0,0 +1,13 @@
language: erlang
notifications:
recipients:
- eduardo@gurgel.me
otp_release:
- R16B
- R16B01
- R16B02
before_install:
- git clone https://github.com/elixir-lang/elixir
- cd elixir && git checkout v0.12.1 && make && cd ..
before_script: "export PATH=`pwd`/elixir/bin:$PATH"
script: "MIX_ENV=test mix do deps.get, test --no-start"

View file

@ -1,38 +0,0 @@
# Based on https://github.com/hexpm/hexpm/blob/08e80ed4fe82b145f6cee1d01da16e162add2a56/Dockerfile
FROM elixir:1.9.0-alpine as build
ENV MIX_ENV=prod
RUN mkdir /app
WORKDIR /app
RUN mix local.hex --force && mix local.rebar --force
# install mix dependencies
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile
# build project
COPY priv priv
COPY lib lib
RUN mix compile
# build release
COPY rel rel
RUN mix release
# prepare release image
FROM alpine:3.9 AS app
RUN apk add --update bash openssl
RUN mkdir /app
WORKDIR /app
COPY --from=build /app/_build/prod/rel/httparrot ./
RUN chown -R nobody: /app
USER nobody
ENV HOME=/app
CMD /app/bin/httparrot start

View file

@ -1,22 +0,0 @@
# The MIT License
Copyright (c) 2013 Eduardo Gurgel <eduardo@gurgel.me>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,56 +1,44 @@
# HTTParrot
# HTTParrot [![Build Status](https://travis-ci.org/edgurgel/httparrot.png?branch=master)](https://travis-ci.org/edgurgel/httparrot)
[![Build Status](https://travis-ci.org/edgurgel/httparrot.png?branch=master)](https://travis-ci.org/edgurgel/httparrot)
[![Module Version](https://img.shields.io/hexpm/v/httparrot.svg)](https://hex.pm/packages/httparrot)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/httparrot/)
[![Total Download](https://img.shields.io/hexpm/dt/httparrot.svg)](https://hex.pm/packages/httparrot)
[![License](https://img.shields.io/hexpm/l/httparrot.svg)](https://github.com/edgurgel/httparrot/blob/master/LICENSE.md)
[![Last Updated](https://img.shields.io/github/last-commit/edgurgel/httparrot.svg)](https://github.com/edgurgel/httparrot/commits/master)
HTTP server built on top of [Cowboy](https://hex.pm/packages/cowboy) using (mostly) `cowboy_rest` handlers to serve useful endpoints for testing purposes. Its goal is to be as close as possible to [HTTPBin](http://httpbin.org).
http://httpbin.org clone
## Endpoints
* `/` This page.
* `/ip` Returns Origin IP.
* `/user-agent` Returns user-agent.
* `/headers` Returns header dict.
* `/get` Returns GET data.
* `/post` Returns POST data.
* `/put` Returns PUT data.
* `/patch` Returns PATCH data.
* `/delete` Returns DELETE data
* `/gzip` Returns gzip-encoded data.
* `/status/:code` Returns given HTTP Status code.
* `/response-headers?key=val` Returns given response headers.
* `/redirect/:n` 301 Redirects n times.
* `/redirect-to?url=foo` 301 Redirects to the foo URL.
* `/relative-redirect/:n` 301 Relative redirects n times.
* `/cookies` Returns cookie data.
* `/cookies/set?name=value` Sets one or more simple cookies.
* `/cookies/set/name/value` Sets one cookie .
* `/cookies/delete?name` Deletes one or more simple cookies.
* `/basic-auth/:user/:passwd` Challenges HTTPBasic Auth.
* `/hidden-basic-auth/:user/:passwd` 404'd BasicAuth.
* `/digest-auth/:qop/:user/:passwd` Challenges HTTP Digest Auth.
* `/stream/:n` Streams n100 lines.
* `/delay/:n` Delays responding for n10 seconds.
* `/html` Renders an HTML Page.
* `/robots.txt` Returns some robots.txt rules.
* `/deny` Denied by robots.txt file.
* `/cache` Returns 200 unless an If-Modified-Since header is provided, when it returns a 304 Not Modified.
* `/base64/:value` Decodes base64url-encoded string.
* `/image` Return an image based on Accept header.
* `/websocket` Echo message received through websocket
* / This page.
* /ip Returns Origin IP.
* /user-agent Returns user-agent.
* /headers Returns header dict.
* /get Returns GET data.
* /post Returns POST data.
* /put Returns PUT data.
* /patch Returns PATCH data.
* /delete Returns DELETE data
* /gzip Returns gzip-encoded data.
* /status/:code Returns given HTTP Status code.
* /response-headers?key=val Returns given response headers.
* /redirect/:n 302 Redirects n times.
* /redirect-to?url=foo 302 Redirects to the foo URL.
* /relative-redirect/:n 302 Relative redirects n times.
* /cookies Returns cookie data.
* /cookies/set?name=value Sets one or more simple cookies.
* /cookies/delete?name Deletes one or more simple cookies.
* /basic-auth/:user/:passwd Challenges HTTPBasic Auth.
* /hidden-basic-auth/:user/:passwd 404'd BasicAuth.
* /digest-auth/:qop/:user/:passwd Challenges HTTP Digest Auth.
* /stream/:n Streams n100 lines.
* /delay/:n Delays responding for n10 seconds.
* /html Renders an HTML Page.
* /robots.txt Returns some robots.txt rules.
* /deny Denied by robots.txt file.
* /cache Returns 200 unless an If-Modified-Since header is provided, when it returns a 304 Not Modified.
## TODO
* [ ] `/deflate` Returns deflate-encoded data.
* [ ] `/digest-auth/:qop/:user/:passwd` Challenges HTTP Digest Auth.
## Copyright and License
Copyright (c) 2013 Eduardo Gurgel <eduardo@gurgel.me>
This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the [LICENSE.md](./LICENSE.md) file for more details.
* / This page.
* /gzip Returns gzip-encoded data.
* /response-headers?key=val Returns given response headers.
* /relative-redirect/:n 302 Relative redirects n times.
* /digest-auth/:qop/:user/:passwd Challenges HTTP Digest Auth.
* /robots.txt Returns some robots.txt rules.
* /deny Denied by robots.txt file.
* /cache Returns 200 unless an If-Modified-Since header is provided, when it returns a 304 Not Modified.

View file

@ -1,10 +0,0 @@
#!/usr/bin/env bash
# exit on error
set -o errexit
# Initial setup
mix deps.get --only prod
MIX_ENV=prod mix compile
# Build the release and overwrite the existing release directory
MIX_ENV=prod mix release --overwrite

View file

@ -1,16 +0,0 @@
import Config
port = System.get_env("PORT", "8080")
port = String.to_integer(port)
ssl_port = System.get_env("SSL_PORT", "8433")
ssl_port = String.to_integer(ssl_port)
unix_socket = System.get_env("UNIX_SOCKET", "false")
unix_socket = if unix_socket == "true", do: true, else: false
config :httparrot,
http_port: port,
https_port: ssl_port,
unix_socket: unix_socket,
socket_path: System.get_env("SOCKET_PATH", "httparrot.sock")

View file

@ -1,16 +0,0 @@
import Config
port = System.get_env("PORT", "8080")
port = String.to_integer(port)
ssl_port = System.get_env("SSL_PORT", "8433")
ssl_port = String.to_integer(ssl_port)
unix_socket = System.get_env("UNIX_SOCKET", "false")
unix_socket = if unix_socket == "true", do: true, else: false
config :httparrot,
http_port: port,
https_port: ssl_port,
unix_socket: unix_socket,
socket_path: System.get_env("SOCKET_PATH", "httparrot.sock")

View file

@ -1,128 +1,51 @@
defmodule HTTParrot do
use Application
use Supervisor
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg)
end
def init(_) do
Supervisor.start_link([], strategy: :one_for_one)
end
use Application.Behaviour
def start(_type, _args) do
dispatch =
:cowboy_router.compile([
{:_,
[
{"/", :cowboy_static, {:priv_file, :httparrot, "index.html"}},
{"/ip", HTTParrot.IPHandler, []},
{"/user-agent", HTTParrot.UserAgentHandler, []},
{"/headers", HTTParrot.HeadersHandler, []},
{"/get", HTTParrot.GetHandler, []},
{"/post", HTTParrot.PHandler, []},
{"/put", HTTParrot.PHandler, []},
{"/patch", HTTParrot.PHandler, []},
{"/delete", HTTParrot.DeleteHandler, []},
{"/deflate", HTTParrot.DeflateHandler, []},
{"/gzip", HTTParrot.GzipHandler, []},
{"/status/:code", [code: :int], HTTParrot.StatusCodeHandler, []},
{"/redirect/:n", HTTParrot.RedirectHandler, []},
{"/redirect-to", HTTParrot.RedirectToHandler, []},
{"/relative-redirect/:n", HTTParrot.RelativeRedirectHandler, []},
{"/cookies", HTTParrot.CookiesHandler, []},
{"/cookies/set[/:name/:value]", HTTParrot.SetCookiesHandler, []},
{"/cookies/delete", HTTParrot.DeleteCookiesHandler, []},
{"/basic-auth/:user/:passwd", HTTParrot.BasicAuthHandler, []},
{"/hidden-basic-auth/:user/:passwd", HTTParrot.HiddenBasicAuthHandler, []},
{"/stream/:n", HTTParrot.StreamHandler, []},
{"/stream-bytes/:n", HTTParrot.StreamBytesHandler, []},
{"/delay/:n", HTTParrot.DelayedHandler, []},
{"/html", :cowboy_static, {:priv_file, :httparrot, "html.html"}},
{"/deny", HTTParrot.DenyHandler, []},
{"/cache", HTTParrot.CacheHandler, []},
{"/robots.txt", HTTParrot.RobotsHandler, []},
{"/base64/:value", HTTParrot.Base64Handler, []},
{"/image", HTTParrot.ImageHandler, []},
{"/websocket", HTTParrot.WebsocketHandler, []},
{"/response-headers", HTTParrot.ResponseHeadersHandler, []},
{"/store/:key", HTTParrot.StoreRequestHandler, []},
{"/retrieve/:key", HTTParrot.RetrieveRequestHandler, []}
]}
dispatch = :cowboy_router.compile([
{:_, [ {'/ip', HTTParrot.IPHandler, []},
{'/user-agent', HTTParrot.UserAgentHandler, []},
{'/headers', HTTParrot.HeadersHandler, []},
{'/get', HTTParrot.GetHandler, []},
{'/post', HTTParrot.PHandler, []},
{'/put', HTTParrot.PHandler, []},
{'/patch', HTTParrot.PHandler, []},
{'/delete', HTTParrot.DeleteHandler, []},
{'/status/:code', HTTParrot.StatusCodeHandler, []},
{'/redirect/:n', HTTParrot.RedirectHandler, []},
{'/redirect-to', HTTParrot.RedirectToHandler, []},
{'/cookies', HTTParrot.CookiesHandler, []},
{'/cookies/set', HTTParrot.SetCookiesHandler, []},
{'/cookies/delete', HTTParrot.DeleteCookiesHandler, []},
{'/basic-auth/:user/:passwd', HTTParrot.BasicAuthHandler, []},
{'/hidden-basic-auth/:user/:passwd', HTTParrot.HiddenBasicAuthHandler, []},
{'/stream/:n', HTTParrot.StreamHandler, []},
{'/delay/:n', HTTParrot.DelayedHandler, []},
{'/html', :cowboy_static, {:priv_file, :httparrot, "html.html"}},
{'/deny', HTTParrot.DenyHandler, []} ] }
])
{:ok, http_port} = :application.get_env(:httparrot, :http_port)
{:ok, https_port} = :application.get_env(:httparrot, :https_port)
{:ok, _} = :cowboy.start_http(:http, 100, [port: http_port],
[env: [dispatch: dispatch], onresponse: &prettify_json/4])
{:ok, http_port} = Application.fetch_env(:httparrot, :http_port)
IO.puts("Starting HTTParrot on port #{http_port}")
{:ok, _} =
:cowboy.start_clear(
:http,
[port: http_port],
%{env: %{dispatch: dispatch}}
)
if Application.get_env(:httparrot, :ssl, false) do
{:ok, https_port} = Application.fetch_env(:httparrot, :https_port)
priv_dir = :code.priv_dir(:httparrot)
IO.puts("Starting HTTParrot on port #{https_port} (SSL)")
{:ok, _} =
:cowboy.start_tls(
:https,
[
port: https_port,
cacertfile: priv_dir ++ ~c"/ssl/server-ca.crt",
certfile: priv_dir ++ ~c"/ssl/server.crt",
keyfile: priv_dir ++ ~c"/ssl/server.key"
],
%{env: %{dispatch: dispatch}}
)
end
if unix_socket_supported?() && Application.get_env(:httparrot, :unix_socket, false) do
{:ok, socket_path} = Application.fetch_env(:httparrot, :socket_path)
if File.exists?(socket_path), do: File.rm(socket_path)
IO.puts("Starting HTTParrot on unix socket #{socket_path}")
{:ok, _} =
:cowboy.start_clear(
:http_unix,
[port: 0, ip: {:local, socket_path}],
%{env: %{dispatch: dispatch}}
)
end
Supervisor.start_link(
[
{ConCache,
[
ttl_check_interval: :timer.minutes(5),
global_ttl: :timer.hours(12),
name: :requests_registry
]}
],
strategy: :one_for_one
)
{:ok, _} = :cowboy.start_https(:https, 100,
[port: https_port, cacertfile: priv_dir ++ '/ssl/server-ca.crt',
certfile: priv_dir ++ '/ssl/server.crt', keyfile: priv_dir ++ '/ssl/server.key'],
[env: [dispatch: dispatch], onresponse: &prettify_json/4])
IO.puts "Starting HTTParrot on port #{http_port} and SSL on #{https_port}"
HTTParrot.Supervisor.start_link
end
def stop(_State), do: :ok
def prettify_json(status, headers, body, req) do
if JSX.is_json?(body) do
body = JSX.prettify!(body)
headers = Map.put(headers, "content-length", Integer.to_charlist(String.length(body)))
:cowboy_req.reply(status, headers, body, req)
else
if JSEX.is_json? body do
body = JSEX.prettify!(body)
headers = ListDict.put(headers, "content-length", integer_to_list(String.length(body)))
{:ok, req} = :cowboy_req.reply(status, headers, body, req)
end
req
end
end
def unix_socket_supported? do
case {:os.type(), Integer.parse("#{:erlang.system_info(:otp_release)}")} do
{{:unix, _}, {n, _}} when n >= 19 -> true
_ -> false
end
end
end

View file

@ -1,35 +0,0 @@
defmodule HTTParrot.Base64Handler do
@moduledoc """
Returns urlsafe base64 decoded data.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"application", "octet-stream", []}, :get_binary}], req, state}
end
def malformed_request(req, state) do
value = :cowboy_req.binding(:value, req)
case decode(value) do
{:ok, result} -> {false, req, result}
:error -> {true, req, state}
end
end
defp decode(value) do
pad(value) |> Base.url_decode64()
end
defp pad(value) do
case byte_size(value) |> rem(4) do
2 -> value <> "=="
3 -> value <> "="
_ -> value
end
end
def get_binary(req, result) do
{result, req, result}
end
end

View file

@ -2,15 +2,20 @@ defmodule HTTParrot.BasicAuthHandler do
@moduledoc """
Challenges HTTPBasic Auth
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def is_authorized(req, state) do
user = :cowboy_req.binding(:user, req)
passwd = :cowboy_req.binding(:passwd, req)
auth = :cowboy_req.parse_header("authorization", req)
{user, req} = :cowboy_req.binding(:user, req)
{passwd, req} = :cowboy_req.binding(:passwd, req)
{:ok, auth, req} = :cowboy_req.parse_header("authorization", req)
case auth do
{:basic, ^user, ^passwd} -> {true, req, user}
{"basic", {^user, ^passwd}} -> {true, req, user}
_ -> {{false, "Basic realm=\"Fake Realm\""}, req, state}
end
end
@ -24,6 +29,8 @@ defmodule HTTParrot.BasicAuthHandler do
end
defp response(user) do
[authenticated: true, user: user] |> JSX.encode!()
[authenticated: true, user: user] |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,24 +0,0 @@
defmodule HTTParrot.CacheHandler do
@moduledoc """
Returns 200 unless an If-Modified-Since or If-None-Match header is provided, when it returns a 304.
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def last_modified(req, state) do
{{{2012, 9, 21}, {22, 36, 14}}, req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req)
{response(info), req, state}
end
defp response(info) do
info |> JSX.encode!()
end
end

View file

@ -2,19 +2,28 @@ defmodule HTTParrot.CookiesHandler do
@moduledoc """
Returns cookie data.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
cookies = :cowboy_req.parse_cookies(req)
cookies = if cookies == [], do: [{}], else: cookies
{cookies, req} = :cowboy_req.cookies(req)
if cookies == [], do: cookies = [{}]
{response(cookies), req, state}
end
defp response(cookies) do
[cookies: cookies] |> JSX.encode!()
[cookies: cookies] |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,15 +0,0 @@
defmodule HTTParrot.Cowboy do
defmacro __using__(opts) do
methods = Keyword.get(opts, :methods, [])
quote bind_quoted: [methods: methods] do
def init(req, state) do
{:cowboy_rest, req, state}
end
def allowed_methods(req, state) do
{unquote(methods), req, state}
end
end
end
end

View file

@ -1,27 +0,0 @@
defmodule HTTParrot.DeflateHandler do
@moduledoc """
Encode body using deflate encoding
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
zlib = :zlib.open()
:zlib.deflateInit(zlib)
{info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_header("content-encoding", "deflate", req)
json = response(info) |> JSX.prettify!()
response = :zlib.deflate(zlib, json, :finish)
:zlib.deflateEnd(zlib)
{response, req, state}
end
defp response(info) do
info |> JSX.encode!()
end
end

View file

@ -1,12 +1,18 @@
defmodule HTTParrot.DelayedHandler do
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def malformed_request(req, state) do
n = :cowboy_req.binding(:n, req)
{n, req} = :cowboy_req.binding(:n, req)
try do
n = n |> String.to_integer() |> min(10) |> max(0)
n = n |> binary_to_integer |> min(10) |> max(0)
{false, req, n}
rescue
ArgumentError -> {true, req, state}
@ -24,6 +30,8 @@ defmodule HTTParrot.DelayedHandler do
end
defp response(info) do
info |> JSX.encode!()
info |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -2,10 +2,17 @@ defmodule HTTParrot.DeleteCookiesHandler do
@moduledoc """
Deletes one or more simple cookies.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def malformed_request(req, state) do
qs_vals = :cowboy_req.parse_qs(req)
{qs_vals, req} = :cowboy_req.qs_vals(req)
if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals}
end
@ -14,18 +21,17 @@ defmodule HTTParrot.DeleteCookiesHandler do
end
def get_json(req, qs_vals) do
req =
Enum.reduce(qs_vals, req, fn {name, value}, req ->
req = Enum.reduce qs_vals, req, fn({name, value}, req) ->
delete_cookie(name, value, req)
end)
req = :cowboy_req.reply(302, %{"location" => "/cookies"}, "Redirecting...", req)
{:stop, req, qs_vals}
end
{:ok, req} = :cowboy_req.reply(302, [{"location", "/cookies"}], "Redirecting...", req)
{:halt, req, qs_vals}
end
defp delete_cookie(name, true, req), do: delete_cookie(name, "", req)
defp delete_cookie(name, value, req) do
:cowboy_req.set_resp_cookie(name, value, req, %{path: "/", max_age: 0})
:cowboy_req.set_resp_cookie(name, value, [path: "/", max_age: 0], req)
end
def terminate(_, _, _), do: :ok
end

View file

@ -3,10 +3,13 @@ defmodule HTTParrot.DeleteHandler do
Returns DELETE data.
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(DELETE)
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["DELETE"], req, state}
end
def delete_resource(req, state) do
@ -16,6 +19,8 @@ defmodule HTTParrot.DeleteHandler do
end
defp response(info) do
info |> JSX.encode!()
info |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,8 +1,16 @@
defmodule HTTParrot.DenyHandler do
@moduledoc """
Returns a simple HTML page.
Returns GET data.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
alias HTTParrot.GeneralRequestInfo
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET", "HEAD", "OPTIONS"], req, state}
end
def content_types_provided(req, state) do
{[{{"text", "plain", []}, :get_plain}], req, state}
@ -18,9 +26,11 @@ defmodule HTTParrot.DenyHandler do
\ .-"` `"-. /
'. .'
'-......-'
YOU SHOULDN'T BE HERE
YOU SHOUDN'T BE HERE
"""
def get_plain(req, state) do
{@body, req, state}
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,38 +1,13 @@
defmodule HTTParrot.GeneralRequestInfo do
def retrieve(req) do
args = :cowboy_req.parse_qs(req)
headers = :cowboy_req.headers(req)
url = IO.iodata_to_binary(:cowboy_req.uri(req))
{ip, _port} = :cowboy_req.peer(req)
{args, req} = :cowboy_req.qs_vals(req)
{headers, req} = :cowboy_req.headers(req)
{url, req} = :cowboy_req.url(req)
{{ip, _port}, req} = :cowboy_req.peer(req)
ip =
case ip do
{127, 0, 0, 1} -> ""
:local -> ""
_ -> :inet_parse.ntoa(ip) |> to_string
end
args = group_by_keys(args)
ip = :inet_parse.ntoa(ip) |> to_string
if args == [], do: args = [{}]
{[args: args, headers: headers, url: url, origin: ip], req}
end
@doc """
Group by keys and if duplicated keys, aggregate them as a list
iex> group_by_keys([{"a", "v1"}, {"a", "v2"}, {"b", "v3"}, {"a", "v4"}])
%{"a" => ["v1", "v2", "v4"], "b" => "v3"}
"""
@spec group_by_keys(list) :: map
def group_by_keys([]), do: %{}
def group_by_keys(args) do
args
|> Enum.map(fn {k, v} -> %{k => v} end)
|> Enum.reduce(fn m, acc ->
Map.merge(m, acc, fn _k, v1, v2 ->
List.wrap(v2) ++ List.wrap(v1)
end)
end)
end
end

View file

@ -3,7 +3,14 @@ defmodule HTTParrot.GetHandler do
Returns GET data.
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET", "HEAD", "OPTIONS"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
@ -15,6 +22,8 @@ defmodule HTTParrot.GetHandler do
end
defp response(info) do
info |> JSX.encode!()
info |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,22 +0,0 @@
defmodule HTTParrot.GzipHandler do
@moduledoc """
Encode body using gzip encoding
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_header("content-encoding", "gzip", req)
response = response(info) |> JSX.prettify!() |> :zlib.gzip()
{response, req, state}
end
defp response(info) do
info |> JSX.encode!()
end
end

View file

@ -1,16 +1,24 @@
defmodule HTTParrot.HeadersHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
headers = :cowboy_req.headers(req)
{headers, req} = :cowboy_req.headers(req)
{response(headers), req, state}
end
defp response(headers) do
[headers: headers] |> JSX.encode!()
[headers: headers] |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -2,18 +2,23 @@ defmodule HTTParrot.HiddenBasicAuthHandler do
@moduledoc """
404'd BasicAuth
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
@doc """
This method should be `is_authorized`, but this handler will return 404 if the auth fails
"""
def resource_exists(req, state) do
user = :cowboy_req.binding(:user, req)
passwd = :cowboy_req.binding(:passwd, req)
auth = :cowboy_req.parse_header("authorization", req)
{user, req} = :cowboy_req.binding(:user, req)
{passwd, req} = :cowboy_req.binding(:passwd, req)
{:ok, auth, req} = :cowboy_req.parse_header("authorization", req)
case auth do
{:basic, ^user, ^passwd} -> {true, req, user}
{"basic", {^user, ^passwd}} -> {true, req, user}
_ -> {false, req, state}
end
end
@ -27,6 +32,8 @@ defmodule HTTParrot.HiddenBasicAuthHandler do
end
defp response(user) do
[authenticated: true, user: user] |> JSX.encode!()
[authenticated: true, user: user] |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,20 +0,0 @@
defmodule HTTParrot.ImageHandler do
@moduledoc """
Returns an image.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"image", "png", []}, :get_png}, {{"image", "jpeg", []}, :get_jpeg}], req, state}
end
@png Base.decode64!(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="
)
@jpeg Base.decode64!(
"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="
)
def get_png(req, state), do: {@png, req, state}
def get_jpeg(req, state), do: {@jpeg, req, state}
end

View file

@ -2,39 +2,28 @@ defmodule HTTParrot.IPHandler do
@moduledoc """
Returns Origin IP
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
{ip, _port} = :cowboy_req.peer(req)
forwarded_for = :cowboy_req.parse_header("x-forwarded-for", req)
case forwarded_for do
:undefined ->
{{ip, _port}, req} = :cowboy_req.peer(req)
{response(ip), req, state}
_ ->
{response(to_tuple(forwarded_for)), req, state}
end
end
defp to_tuple(client_ip) do
client_ip
|> hd
|> String.split(".")
|> Enum.map(fn x -> String.to_integer(x) end)
|> List.to_tuple()
end
defp response({127, 0, 0, 1}) do
[origin: ""] |> JSX.encode!()
end
defp response(ip) do
ip = :inet_parse.ntoa(ip) |> to_string
[origin: ip] |> JSX.encode!()
[origin: ip] |> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -4,27 +4,24 @@ defmodule HTTParrot.PHandler do
"""
alias HTTParrot.GeneralRequestInfo
def init(req, state) do
{:cowboy_rest, req, state}
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
@doc """
When a request is made to /post, allowed_methods will return POST for example
"""
def allowed_methods(req, state) do
path = :cowboy_req.path(req)
path = String.slice(path, 1..-1//1)
{path, req} = :cowboy_req.path(req)
path = String.slice(path, 1..-1)
{[String.upcase(path)], req, state}
end
def content_types_accepted(req, state) do
{[
{{"application", "json", :*}, :post_binary},
{[{{"application", "json", :*}, :post_binary},
{{"application", "octet-stream", :*}, :post_binary},
{{"text", "plain", :*}, :post_binary},
{{"application", "x-www-form-urlencoded", :*}, :post_form},
{{"multipart", "form-data", :*}, :post_multipart}
], req, state}
{{"application", "x-www-form-urlencoded", :*}, :post_form}], req, state}
end
def content_types_provided(req, state) do
@ -32,33 +29,22 @@ defmodule HTTParrot.PHandler do
end
def post_form(req, _state) do
{:ok, body, req} = :cowboy_req.read_urlencoded_body(req)
post(req, form: body, data: "", json: nil)
{:ok, body, req} = :cowboy_req.body_qs(req)
post(req, [form: body, data: "", json: nil])
end
def post_binary(req, _state) do
{:ok, body, req} = handle_binary(req)
{:ok, body, req} = :cowboy_req.body(req)
if String.valid?(body) do
if JSX.is_json?(body) do
post(req, form: [{}], data: body, json: JSX.decode!(body))
if JSEX.is_json?(body) do
post(req, [form: [{}], data: body, json: JSEX.decode!(body)])
else
post(req, form: [{}], data: body, json: nil)
post(req, [form: [{}], data: body, json: nil])
end
else
# Octet-stream
body = Base.encode64(body)
post(req, form: [{}], data: "data:application/octet-stream;base64," <> body, json: nil)
end
end
def handle_binary(req, chunks \\ []) do
case :cowboy_req.read_body(req) do
{:ok, chunk, req} ->
{:ok, Enum.join(chunks ++ [chunk]), req}
{:more, chunk, req} ->
handle_binary(req, chunks ++ [chunk])
body = :base64.encode(body)
post(req, [form: [{}], data: "data:application/octet-stream;base64," <> body, json: nil])
end
end
@ -69,74 +55,8 @@ defmodule HTTParrot.PHandler do
end
defp response(info, body) do
(info ++ body) |> JSX.encode!()
info ++ body |> JSEX.encode!
end
def post_multipart(req, _state) do
{:ok, parts, req} = handle_multipart(req)
file_parts = for file <- parts, elem(file, 0) == :file, do: {elem(file, 1), elem(file, 2)}
form_parts = for form <- parts, elem(form, 0) == :form, do: {elem(form, 1), elem(form, 2)}
post(req,
form: normalize_list(form_parts),
files: normalize_list(file_parts),
data: "",
json: nil
)
end
def handle_multipart(req, parts \\ []) do
case :cowboy_req.read_part(req) do
{:done, req} ->
{:ok, parts, req}
{:ok, headers, req} ->
content_disposition = headers["content-disposition"]
if content_disposition do
case parse_content_disposition_header(content_disposition) do
%{:type => "form-data", "name" => name, "filename" => _filename} ->
{:ok, file, req} = handle_multipart_body(req)
handle_multipart(req, parts ++ [{:file, name, file}])
%{:type => "form-data", "name" => name} ->
{:ok, form_part, req} = handle_multipart_body(req)
handle_multipart(req, parts ++ [{:form, name, form_part}])
_ ->
{:ok, parts, req}
end
else
{:ok, parts, req}
end
end
end
defp handle_multipart_body(req, parts \\ []) do
case :cowboy_req.read_part_body(req) do
{:ok, data, req} ->
{:ok, Enum.join(parts ++ [data]), req}
{:more, data, req} ->
handle_multipart_body(req, parts ++ [data])
end
end
defp parse_content_disposition_header(header) do
parts =
String.split(header, ";")
|> Enum.map(&String.trim/1)
for part <- parts, into: %{} do
case String.split(part, "=") |> Enum.map(&String.trim/1) do
[type] -> {:type, type}
[key, value] -> {key, String.replace(value, "\"", "")}
end
end
end
def normalize_list([]), do: [{}]
def normalize_list(list), do: list
def terminate(_, _, _), do: :ok
end

View file

@ -2,13 +2,19 @@ defmodule HTTParrot.RedirectHandler do
@moduledoc """
Redirects to the foo URL.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET", "HEAD", "OPTIONS"], req, state}
end
def malformed_request(req, state) do
n = :cowboy_req.binding(:n, req)
{n, req} = :cowboy_req.binding(:n, req)
try do
n = n |> String.to_integer() |> max(1)
n = n |> binary_to_integer |> max(1)
{false, req, n}
rescue
ArgumentError -> {true, req, state}
@ -18,13 +24,11 @@ defmodule HTTParrot.RedirectHandler do
def resource_exists(req, state), do: {false, req, state}
def previously_existed(req, state), do: {true, req, state}
def moved_permanently(req, n) do
host_url =
IO.iodata_to_binary(
:cowboy_req.uri(req, %{path: :undefined, qs: :undefined, fragment: :undefined})
)
def moved_temporarily(req, n) do
{host_url, req} = :cowboy_req.host_url(req)
url = if n > 1, do: "/redirect/#{n-1}", else: "/get"
{{true, host_url <> url}, req, nil}
end
def terminate(_, _, _), do: :ok
end

View file

@ -2,10 +2,17 @@ defmodule HTTParrot.RedirectToHandler do
@moduledoc """
Redirects to the foo URL.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET", "HEAD", "OPTIONS"], req, state}
end
def malformed_request(req, state) do
%{url: url} = :cowboy_req.match_qs([{:url, [], nil}], req)
{url, req} = :cowboy_req.qs_val("url", req, nil)
if url, do: {false, req, url}, else: {true, req, state}
end
@ -15,4 +22,6 @@ defmodule HTTParrot.RedirectToHandler do
def moved_permanently(req, url) do
{{true, url}, req, url}
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,18 +0,0 @@
defmodule HTTParrot.RelativeRedirectHandler do
@moduledoc """
Redirects to the relative foo URL.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do
HTTParrot.RedirectHandler.malformed_request(req, state)
end
def resource_exists(req, state), do: {false, req, state}
def previously_existed(req, state), do: {true, req, state}
def moved_permanently(req, n) do
url = if n > 1, do: "/redirect/#{n - 1}", else: "/get"
{{true, url}, req, nil}
end
end

View file

@ -1,34 +0,0 @@
defmodule HTTParrot.RequestStore do
@moduledoc """
Used to store and retrieved requests
"""
@doc """
Store the requests to the key
"""
def store(key, info) do
map = retrieve(key) ++ [info]
ConCache.put(:requests_registry, key, map)
end
@doc """
Get the saved request using the key
"""
def retrieve(key) do
entry = ConCache.get(:requests_registry, key)
case entry do
nil ->
[]
_ ->
entry
end
end
@doc """
Clear the saved data on the corresponding key
"""
def clear(key) do
ConCache.delete(:requests_registry, key)
end
end

View file

@ -1,33 +0,0 @@
defmodule HTTParrot.ResponseHeadersHandler do
@moduledoc """
Returns given response headers.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do
qs_vals = :cowboy_req.parse_qs(req)
if not Enum.empty?(qs_vals) do
{false, req, qs_vals}
else
{true, req, state}
end
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, qs_vals) do
req =
Enum.reduce(qs_vals, req, fn {key, value}, req ->
:cowboy_req.set_resp_header(key, value, req)
end)
{response(qs_vals), req, qs_vals}
end
defp response(qs_vals) do
qs_vals |> JSX.encode!()
end
end

View file

@ -1,21 +0,0 @@
defmodule HTTParrot.RetrieveRequestHandler do
@moduledoc """
Retrieve saved request and clear the conresponding :id
"""
use HTTParrot.Cowboy, methods: ~w(GET POST PUT HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"application", "json", []}, :retrieve_stored}], req, state}
end
def retrieve_stored(req, state) do
key = :cowboy_req.binding(:key, req)
requests = HTTParrot.RequestStore.retrieve(key)
HTTParrot.RequestStore.clear(key)
{response(requests), req, state}
end
defp response(info) do
info |> JSX.encode!()
end
end

View file

@ -1,17 +0,0 @@
defmodule HTTParrot.RobotsHandler do
@moduledoc """
Returns a robots.txt.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def content_types_provided(req, state) do
{[{{"text", "plain", []}, :get_text}], req, state}
end
@robots """
User-agent: *
Disallow: /deny
"""
def get_text(req, state), do: {@robots, req, state}
end

View file

@ -2,35 +2,35 @@ defmodule HTTParrot.SetCookiesHandler do
@moduledoc """
Sets one or more simple cookies.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def malformed_request(req, state) do
qs_vals = :cowboy_req.parse_qs(req)
name = :cowboy_req.binding(:name, req, nil)
if name do
value = :cowboy_req.binding(:value, req, nil)
{false, req, List.keystore(qs_vals, name, 0, {name, value})}
else
{qs_vals, req} = :cowboy_req.qs_vals(req)
if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals}
end
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, qs_vals) do
req =
Enum.reduce(qs_vals, req, fn {name, value}, req ->
req = Enum.reduce qs_vals, req, fn({name, value}, req) ->
set_cookie(name, value, req)
end)
req = :cowboy_req.reply(302, %{"location" => "/cookies"}, "Redirecting...", req)
{:stop, req, qs_vals}
end
{:ok, req} = :cowboy_req.reply(302, [{"location", "/cookies"}], "Redirecting...", req)
{:halt, req, qs_vals}
end
defp set_cookie(name, value, req) do
:cowboy_req.set_resp_cookie(name, value, req, %{path: "/"})
:cowboy_req.set_resp_cookie(name, value, [path: "/"], req)
end
def terminate(_, _, _), do: :ok
end

View file

@ -2,15 +2,24 @@ defmodule HTTParrot.StatusCodeHandler do
@moduledoc """
Returns given HTTP Status code.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
code = :cowboy_req.binding(:code, req)
req = :cowboy_req.reply(code, %{}, "", req)
{:stop, req, state}
{code, req} = :cowboy_req.binding(:code, req)
{:ok, req} = :cowboy_req.reply(code, [], "", req)
{:halt, req, state}
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,71 +0,0 @@
defmodule HTTParrot.StoreRequestHandler do
@moduledoc """
Store the sended request with the :id
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET POST PUT HEAD OPTIONS)
def content_types_accepted(req, state) do
{[
{{"application", "json", :*}, :post_binary},
{{"application", "octet-stream", :*}, :post_binary},
{{"text", "plain", :*}, :post_binary},
{{"application", "x-www-form-urlencoded", :*}, :post_form},
{{"multipart", "form-data", :*}, :post_multipart}
], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get}], req, state}
end
def get(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req)
key = :cowboy_req.binding(:key, req)
HTTParrot.RequestStore.store(key, info)
{~c'{"saved": "true"}', req, state}
end
def post_binary(req, _state) do
{:ok, body, req} = HTTParrot.PHandler.handle_binary(req)
if String.valid?(body) do
if JSX.is_json?(body) do
save_post(req, form: [{}], data: body, json: JSX.decode!(body))
else
save_post(req, form: [{}], data: body, json: nil)
end
else
# Octet-stream
body = Base.encode64(body)
save_post(req, form: [{}], data: "data:application/octet-stream;base64," <> body, json: nil)
end
end
def post_form(req, _state) do
{:ok, body, req} = :cowboy_req.read_urlencoded_body(req)
save_post(req, form: body, data: "", json: nil)
end
def post_multipart(req, _state) do
{:ok, parts, req} = HTTParrot.PHandler.handle_multipart(req)
file_parts = for file <- parts, elem(file, 0) == :file, do: {elem(file, 1), elem(file, 2)}
form_parts = for form <- parts, elem(form, 0) == :form, do: {elem(form, 1), elem(form, 2)}
save_post(req,
form: HTTParrot.PHandler.normalize_list(form_parts),
files: HTTParrot.PHandler.normalize_list(file_parts),
data: "",
json: nil
)
end
def save_post(req, body) do
{info, req} = GeneralRequestInfo.retrieve(req)
key = :cowboy_req.binding(:key, req)
HTTParrot.RequestStore.store(key, info ++ body)
req = :cowboy_req.reply(200, %{}, "{\"saved\": \"true\"}", req)
{:stop, req, nil}
end
end

View file

@ -1,46 +0,0 @@
defmodule HTTParrot.StreamBytesHandler do
@moduledoc """
Streams n bytes of data, with chunked transfer encoding.
"""
use HTTParrot.Cowboy, methods: ~w(GET)
def content_types_provided(req, state) do
{[{{"application", "octet-stream", []}, :get_bytes}], req, state}
end
def malformed_request(req, state) do
n = :cowboy_req.binding(:n, req)
%{seed: seed} = :cowboy_req.match_qs([{:seed, [], "1234"}], req)
%{chunk_size: chunk_size} = :cowboy_req.match_qs([{:chunk_size, [], "1024"}], req)
try do
n = n |> String.to_integer()
seed = seed |> String.to_integer()
chunk_size = chunk_size |> String.to_integer()
{false, req, {n, seed, chunk_size}}
rescue
ArgumentError -> {true, req, state}
end
end
def get_bytes(req, state) do
{n, seed, chunk_size} = state
:rand.seed(:exs64, {seed, seed, seed})
req = stream_response!(n, chunk_size, req)
{:stop, req, nil}
end
defp stream_response!(n, chunk_size, req) do
req = :cowboy_req.stream_reply(200, %{"content-type" => "application/octet-stream"}, req)
Stream.repeatedly(fn -> :rand.uniform(255) end)
|> Stream.take(n)
|> Enum.chunk_every(chunk_size, chunk_size, [])
|> Enum.each(fn chunk ->
:cowboy_req.stream_body(List.to_string(chunk), :nofin, req)
end)
:cowboy_req.stream_body("", :fin, req)
req
end
end

View file

@ -3,16 +3,22 @@ defmodule HTTParrot.StreamHandler do
Returns GET data.
"""
alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
@doc """
`n` must be an integer between 1-100
"""
def malformed_request(req, state) do
n = :cowboy_req.binding(:n, req)
{n, req} = :cowboy_req.binding(:n, req)
try do
n = n |> String.to_integer() |> min(100) |> max(1)
n = n |> binary_to_integer |> min(100) |> max(1)
{false, req, n}
rescue
ArgumentError -> {true, req, state}
@ -25,14 +31,16 @@ defmodule HTTParrot.StreamHandler do
def get_json(req, n) do
{info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.stream_reply(200, %{"content-type" => "application/json"}, req)
{{:chunked, stream_response(n, info)}, req, nil}
end
Enum.each(0..(n - 1), fn i ->
body = JSX.encode!([id: i] ++ info)
:cowboy_req.stream_body(body, :nofin, req)
end)
:cowboy_req.stream_body("", :fin, req)
{:stop, req, nil}
defp stream_response(n, info) do
fn(send_func) ->
Enum.each 0..n-1, fn (i) ->
send_func.([id: i] ++ info |> JSEX.encode!)
end
end
end
def terminate(_, _, _), do: :ok
end

View file

@ -0,0 +1,14 @@
defmodule HTTParrot.Supervisor do
use Supervisor.Behaviour
def start_link do
:supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
end
def init([]) do
children = []
supervise children, strategy: :one_for_one
end
end

View file

@ -2,19 +2,28 @@ defmodule HTTParrot.UserAgentHandler do
@moduledoc """
Returns user-agent.
"""
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def init(_transport, _req, _opts) do
{:upgrade, :protocol, :cowboy_rest}
end
def allowed_methods(req, state) do
{["GET"], req, state}
end
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def get_json(req, state) do
user_agent = :cowboy_req.header("user-agent", req, "null")
{user_agent, req} = :cowboy_req.header("user-agent", req, "null")
{response(user_agent), req, state}
end
defp response(user_agent) do
[{"user-agent", user_agent}]
|> JSX.encode!()
|> JSEX.encode!
end
def terminate(_, _, _), do: :ok
end

View file

@ -1,16 +0,0 @@
defmodule HTTParrot.WebsocketHandler do
@behaviour :cowboy_websocket
@moduledoc """
Echo given messages through websocket connection
"""
def init(req, state), do: {:cowboy_websocket, req, state}
def websocket_init(state), do: {:ok, state}
def websocket_handle({:text, text}, state), do: {:reply, {:text, text}, state}
def websocket_handle({:binary, binary}, state), do: {:reply, {:binary, binary}, state}
def websocket_info(_info, state), do: {:ok, state}
def terminate(_reason, _req, _state), do: :ok
end

64
mix.exs
View file

@ -1,62 +1,32 @@
defmodule Httparrot.Mixfile do
use Mix.Project
@source_url "https://github.com/edgurgel/httparrot"
@version "1.4.0"
def project do
[
app: :httparrot,
version: @version,
elixir: "~> 1.16",
[ app: :httparrot,
version: "0.0.1",
name: "HTTParrot",
package: package(),
deps: deps(),
docs: docs()
]
elixir: "~> 0.11.2 or ~> 0.12.0",
deps: deps(Mix.env) ]
end
def application do
[
extra_applications: [:logger],
mod: {HTTParrot, []}
]
[ applications: [ :compiler,
:syntax_tools,
:cowboy,
:jsex ],
mod: { HTTParrot, [] },
env: [ http_port: 8080, https_port: 8433 ] ]
end
defp deps do
[
{:cowboy, "~> 2.12"},
{:exjsx, "~> 3.0 or ~> 4.0"},
{:con_cache, "~> 1.1"},
{:earmark, "~> 1.0", only: :dev},
{:ex_doc, "~> 0.18", only: :dev},
{:meck, "~> 0.9", only: :test}
]
defp deps(:dev) do
[ {:cowboy, github: "extend/cowboy", tag: "0.9.0" },
{:jsex, github: "talentdeficit/jsex", ref: "c9df36f07b2089a73ab6b32074c01728f1e5a2e1" } ]
end
defp package do
[
description: "https://github.com/edgurgel/httparrot",
maintainers: ["Eduardo Gurgel Pinho"],
licenses: ["MIT"],
links: %{
"Github" => @source_url,
"HTTParrot" => "https://httparrot.onrender.com/",
"httpbin" => "http://httpbin.org"
}
]
defp deps(:test) do
deps(:dev) ++
[ {:meck, github: "eproxus/meck", tag: "0.8.1" } ]
end
defp docs do
[
extras: [
"LICENSE.md": [title: "License"],
"README.md": [title: "Overview"]
],
main: "readme",
source_url: @source_url,
source_ref: "v#{@version}",
formatters: ["html"]
]
end
defp deps(_), do: deps(:dev)
end

View file

@ -1,17 +1,6 @@
%{
"con_cache": {:hex, :con_cache, "1.1.0", "45c7c6cd6dc216e47636232e8c683734b7fe293221fccd9454fa1757bc685044", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8655f2ae13a1e56c8aef304d250814c7ed929c12810f126fc423ecc8e871593b"},
"cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"},
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
}
[ "cowboy": {:git, "git://github.com/extend/cowboy.git", "db52494371ea249f38d51108c7d79cf487ff6374", [{:tag, "0.9.0"}]},
"cowlib": {:git, "git://github.com/extend/cowlib.git", "63298e8e160031a70efff86a1acde7e7db1fcda6", [{:ref, "0.4.0"}]},
"jsex": {:git, "git://github.com/talentdeficit/jsex.git", "c9df36f07b2089a73ab6b32074c01728f1e5a2e1", [{:ref, "c9df36f07b2089a73ab6b32074c01728f1e5a2e1"}]},
"jsx": {:git, "git://github.com/talentdeficit/jsx.git", "e50af6e109cb03bd26acf715cbc77de746507d1d", [{:tag, "v1.4.3"}]},
"meck": {:git, "git://github.com/eproxus/meck.git", "1286aba1cb6bbd6c9fc3f817740758b224843ee7", [{:tag, "0.8.1"}]},
"ranch": {:git, "git://github.com/extend/ranch.git", "5df1f222f94e08abdcab7084f5e13027143cc222", [{:ref, "0.9.0"}]} ]

View file

@ -1,162 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='content-type' value='text/html;charset=utf8'>
<meta name='generator' value='Ronn/v0.7.3 (http://github.com/rtomayko/ronn/tree/0.7.3)'>
<title>httparrot(1): HTTP Client Testing Service</title>
<style type='text/css' media='all'>
/* style: man */
body#manpage {margin:0}
.mp {max-width:100ex;padding:0 9ex 1ex 4ex}
.mp p,.mp pre,.mp ul,.mp ol,.mp dl {margin:0 0 20px 0}
.mp h2 {margin:10px 0 0 0}
.mp > p,.mp > pre,.mp > ul,.mp > ol,.mp > dl {margin-left:8ex}
.mp h3 {margin:0 0 0 4ex}
.mp dt {margin:0;clear:left}
.mp dt.flush {float:left;width:8ex}
.mp dd {margin:0 0 0 9ex}
.mp h1,.mp h2,.mp h3,.mp h4 {clear:left}
.mp pre {margin-bottom:20px}
.mp pre+h2,.mp pre+h3 {margin-top:22px}
.mp h2+pre,.mp h3+pre {margin-top:5px}
.mp img {display:block;margin:auto}
.mp h1.man-title {display:none}
.mp,.mp code,.mp pre,.mp tt,.mp kbd,.mp samp,.mp h3,.mp h4 {font-family:monospace;font-size:14px;line-height:1.42857142857143}
.mp h2 {font-size:16px;line-height:1.25}
.mp h1 {font-size:20px;line-height:2}
.mp {text-align:justify;background:#fff}
.mp,.mp code,.mp pre,.mp pre code,.mp tt,.mp kbd,.mp samp {color:#131211}
.mp h1,.mp h2,.mp h3,.mp h4 {color:#030201}
.mp u {text-decoration:underline}
.mp code,.mp strong,.mp b {font-weight:bold;color:#131211}
.mp em,.mp var {font-style:italic;color:#232221;text-decoration:none}
.mp a,.mp a:link,.mp a:hover,.mp a code,.mp a pre,.mp a tt,.mp a kbd,.mp a samp {color:#0000ff}
.mp b.man-ref {font-weight:normal;color:#434241}
.mp pre {padding:0 4ex}
.mp pre code {font-weight:normal;color:#434241}
.mp h2+pre,h3+pre {padding-left:0}
ol.man-decor,ol.man-decor li {margin:3px 0 10px 0;padding:0;float:left;width:33%;list-style-type:none;text-transform:uppercase;color:#999;letter-spacing:1px}
ol.man-decor {width:100%}
ol.man-decor li.tl {text-align:left}
ol.man-decor li.tc {text-align:center;letter-spacing:4px}
ol.man-decor li.tr {text-align:right;float:right}
</style>
<style type='text/css' media='all'>
/* style: 80c */
.mp {max-width:86ex}
ul {list-style: None; margin-left: 1em!important}
.man-navigation {left:101ex}
</style>
</head>
<body id='manpage'>
<a href="http://github.com/edgurgel/httparrot"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
<div class='mp'>
<h1>httparrot(1): HTTP Request &amp; Response Service</h1>
<p>Freely hosted in <a href="https://httparrot.onrender.com/">HTTPS</a>
<h2 id="ENDPOINTS">ENDPOINTS</h2>
<ul>
<li><a href="/" data-bare-link="true"><code>/</code></a> This page.</li>
<li><a href="/ip" data-bare-link="true"><code>/ip</code></a> Returns Origin IP.</li>
<li><a href="/user-agent" data-bare-link="true"><code>/user-agent</code></a> Returns user-agent.</li>
<li><a href="/headers" data-bare-link="true"><code>/headers</code></a> Returns header dict.</li>
<li><a href="/response-headers?k1=v1&amp;k2=v2" data-bare-link="true"><code>/response-headers</code></a> Returns given response headers.</li>
<li><a href="/get" data-bare-link="true"><code>/get</code></a> Returns GET data.</li>
<li><code>/post</code> Returns POST data.</li>
<li><code>/put</code> Returns PUT data.</li>
<li><code>/patch</code> Returns PATCH data.</li>
<li><code>/delete</code> Returns DELETE data</li>
<li><a href="/gzip"><code>/gzip</code></a> Returns gzip-encoded data.</li>
<li><a href="/status/418"><code>/status/:code</code></a> Returns given HTTP Status code.</li>
<li><a href="/redirect/6"><code>/redirect/:n</code></a> 302 Redirects <em>n</em> times.</li>
<li><a href="/redirect-to?url=http://example.com/"><code>/redirect-to?url=foo</code></a> 302 Redirects to the <em>foo</em> URL.</li>
<li><a href="/relative-redirect/6"><code>/relative-redirect/:n</code></a> 302 Relative redirects <em>n</em> times.</li>
<li><a href="/cookies" data-bare-link="true"><code>/cookies</code></a> Returns cookie data.</li>
<li><a href="/cookies/set?k1=v1&amp;k2=v2"><code>/cookies/set?name=value</code></a> Sets one or more simple cookies.</li>
<li><a href="/cookies/delete?k1&amp;k2"><code>/cookies/delete?name</code></a> Deletes one or more simple cookies.</li>
<li><a href="/basic-auth/user/passwd"><code>/basic-auth/:user/:passwd</code></a> Challenges HTTPBasic Auth.</li>
<li><a href="/hidden-basic-auth/user/passwd"><code>/hidden-basic-auth/:user/:passwd</code></a> 404'd BasicAuth.</li>
<li><a href="/stream/20"><code>/stream/:n</code></a> Streams <em>n</em>-100 lines.</li>
<li><a href="/delay/3"><code>/delay/:n</code></a> Delays responding for <em>n</em>-10 seconds.</li>
<li><a href="/html" data-bare-link="true"><code>/html</code></a> Renders an HTML Page.</li>
<li><a href="/robots.txt" data-bare-link="true"><code>/robots.txt</code></a> Returns some robots.txt rules.</li>
<li><a href="/deny" data-bare-link="true"><code>/deny</code></a> Denied by robots.txt file.</li>
<li><a href="/cache" data-bare-link="true"><code>/cache</code></a> 200 unless If-Modified-Since was sent, then 304.</li>
<li><a href="/stream-bytes/1024"><code>/stream-bytes/:n</code></a> Streams <em>n</em> random bytes of binary data, accepts optional <em>seed</em> and <em>chunk_size</em> integer parameters.</li>
<li><a href="/base64/LytiYXNlNjQrLw" data-bare-link="true"><code>/base64/:value</code></a> Decodes <em>value</em> base64url-encoded string.</li>
<li><a href="/image" data-bare-link="true"><code>/image</code></a> Return an image based on Accept header.</li>
<li><code>/websocket</code> Echo message received through websocket.</li>
</ul>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<p>Testing an HTTP Library can become difficult sometimes. <a href="http://postbin.org">Postbin</a> is fantastic
for testing POST requests, but not much else. This exists to cover all kinds of HTTP
scenarios. Additional endpoints are being considered (e.g. <code>/deflate</code>).</p>
<p>All endpoint responses are JSON-encoded.</p>
<h2 id="EXAMPLES">EXAMPLES</h2>
<h3 id="-curl-http-httparrot-org-ip">$ curl https://httparrot.onrender.com/</h3>
<pre><code>{"origin": "24.127.96.129"}
</code></pre>
<h3 id="-curl-http-httparrot-org-user-agent">$ curl https://httparrot.onrender.com/user-agent</h3>
<pre><code>{"user-agent": "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"}
</code></pre>
<h3 id="-curl-http-httparrot-org-get">$ curl https://httparrot.onrender.com/get</h3>
<pre><code>
{
"args": {},
"headers": {
"accept": "*/*",
"accept-encoding": "gzip",
"cdn-loop": "cloudflare; subreqs=1",
"cf-connecting-ip": "118.148.71.18",
"cf-ew-via": "15",
"cf-ipcountry": "NZ",
"cf-ray": "899c4ca20494508c-AKL",
"cf-visitor": "{\"scheme\":\"https\"}",
"cf-worker": "onrender.com",
"host": "httparrot.onrender.com",
"render-proxy-ttl": "4",
"rndr-id": "1cf65e46-55f1-429d",
"true-client-ip": "118.148.71.18",
"user-agent": "curl/7.81.0",
"x-forwarded-for": "118.148.71.18, 10.213.36.192, 10.214.43.80",
"x-forwarded-proto": "https",
"x-request-start": "1719395492280333"
},
"url": "http://httparrot.onrender.com/get",
"origin": ""
}
</code></pre>
<h3 id="-curl-I-http-httparrot-org-status-418">$ curl -I https://httparrot.onrender.com/status/201</h3>
<pre><code>HTTP/1.1 201
Server: Cowboy
Date: Mon, 13 Jun 2011 04:25:38 GMT
Connection: close
Content-Length: 0
</code></pre>
<h2 id="SEE-ALSO">SEE ALSO</h2>
<p><a href="http://httpbin.org" data-bare-link="true">http://httpbin.org</a></p>
</div>
</body>
</html>

View file

@ -1,27 +1,16 @@
-----BEGIN CERTIFICATE-----
MIIEqjCCAxKgAwIBAgIRANXKM01Lqak+6N93VnHgkl4wDQYJKoZIhvcNAQELBQAw
bTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSEwHwYDVQQLDBhlZHVh
cmRvQHBvcC1vcyAoRWR1YXJkbykxKDAmBgNVBAMMH21rY2VydCBlZHVhcmRvQHBv
cC1vcyAoRWR1YXJkbykwHhcNMjQwNjIzMDU1MzA0WhcNMzQwNjIzMDU1MzA0WjBt
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExITAfBgNVBAsMGGVkdWFy
ZG9AcG9wLW9zIChFZHVhcmRvKTEoMCYGA1UEAwwfbWtjZXJ0IGVkdWFyZG9AcG9w
LW9zIChFZHVhcmRvKTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAK6p
DThGWoUHwCVf8DqHDFthUTvNIKRJzyxArh4GF71dHehiLVBZQ9i/jA1nRASzsVcu
UGyZT18N+XvCCeoPevHXOPjUJZtMkhPqlt55N9gzrniEsmRRQOPzIt8+BiFjJ5Jn
RzRD9S+9AwNIaZKUjiQ1Oexxgvd7pbWy3S/IzXCyFsKIZqYcvvpBY6FroDSEZbrk
xMA4Fhvy2370nTrlKzuoVTGZ8WdHOsh7Ef+mCZp1QwTtCkIPHdCa3OMs5F7u4q8P
tB4QgaWnvEmRtDwsst/CCQr4nIrQzjDwCMS41x0DZwMrQ8lzSjnAHxayWoll9aJK
38jaDviT9qvnIjjdMHN9jJ7fDBZeonrGqdGSs/754oxpSFAiErpBY6dPGYgzkjQM
IxKi4zqYW9ZWPSYacAmRwZsYWp50tvtwxtdY63GslFrnVjtG7e+H1rJJWsQ2Eq5w
sQekS/EqOpufBuoildwGMiyW1CEhXezW5lCiA++kyA6DkVA2zQRi0huJ4LPvDQID
AQABo0UwQzAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
HQ4EFgQUSGXO0OyaSJ4ztTJkQt8atnaqe7MwDQYJKoZIhvcNAQELBQADggGBAA6c
pCJO1U2S79McbKZylJ13Nv+qMSWvTF5ax+/tSahAgvauaQiDInLa9wOUHfiTqNwH
q42UUexfm04Yre+R9NR9jOVHCFwT9uq3MOPKDuRS3UUUCCUjy0b0CSDc1uCi9BGu
jykYViGNCDvTeOI8AZfIEtD77TGJt/8kn7Z6Qe9EFe1N+k8PtbFJgt1/rjIOtFz0
638iHBSky+U4ozznxQmwwseR3KSSNS0JWQDxm/gWqy4MP1m0pW5Pd543ms2BPUzO
2P1PkEV4jz8J4j2C3EctdoiCudqa8BCFMXIE+t/hKQ+LgbW6phoIN6DLp65Yhs7Z
DHPq64f8005EXB8GDHLoP/HL/wnO60e+hjjvsumvCWc7EZeZiHl2QwFMQ6csExeZ
9bky+h1WiQeV2poEyJSPOVAXThaxf3GOV+V/zrnY+DHNio/IRa4Z+/1Fwxs26+o7
Qnkf9UqSZrKr/B7ilQ/Cb1sv3dLzIPAmfNuDjqPWb4eYT+B26n264znHlhvmWw==
MIICeDCCAeGgAwIBAgIJAOvpU0y2e5J4MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV
BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G
A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MTAwMVoX
DTMzMDIyMzA1MTAwMVowVTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw
EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEDAOBgNVBAMMB1JP
T1QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzmY7Us06yjyUbpqwPx
Iv+xh/g3V7we07ClC9GEYnvr3OQvdA1jFEHccMBUUjRoQ8DPd6uSyK5UkixABs08
Tt5B3VsnGKr0DIN+IO4SN2PkmBqIU/BN3KdcwN65YNr3iM0KsKWeFtAZdYx4CakX
7REbO0wjK20AH3xSBn3uFGiBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfZ8KF2jlLBDm
NL6IuEuGY0pdbzAfBgNVHSMEGDAWgBRKfZ8KF2jlLBDmNL6IuEuGY0pdbzAMBgNV
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG1I0kBxXiLkM1b7rl2zPLizREYg
1m+ajb6rWzPOBg6TXjv58Be+H4tqoHIL/M/crixew5emftBkuAGjiKMhbIokjvan
aPTCV8U6HHvNvz9c68HpESWbd+56cHqfsS5XCKp1OpW5tbL2UQYpFKMP4qmbv3Ea
pBfPPmSFMBb1i2AI
-----END CERTIFICATE-----

View file

@ -1,26 +1,17 @@
-----BEGIN CERTIFICATE-----
MIIEZTCCAs2gAwIBAgIQIJarIG+LC7mjP0IRrFcI0zANBgkqhkiG9w0BAQsFADBt
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExITAfBgNVBAsMGGVkdWFy
ZG9AcG9wLW9zIChFZHVhcmRvKTEoMCYGA1UEAwwfbWtjZXJ0IGVkdWFyZG9AcG9w
LW9zIChFZHVhcmRvKTAeFw0yNDA2MjMwNTU0MTBaFw0yNjA5MjMwNTU0MTBaMEwx
JzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEhMB8GA1UE
CwwYZWR1YXJkb0Bwb3Atb3MgKEVkdWFyZG8pMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAx8lf20sm9KkWh/u6ehJ4YpDlx7DGaebBok3JR6rdV42c9flu
pXdi0Uu7tq4utk4rGu6gotIlPFDeRxokqNIaCUmpIW2msG6KzPBp2Lf+AR0EbdQS
EvqiMLx5dU3yEafGKeH1z4qMobhEWIWU9F0V8zuuJvCeHRCdBkse1XQ64X6gyN1m
5lYEEIlGfOj7rrAFdCkv63W5dUeph9yoIF//T9yhTx0yNGUeCOuqQEL5HsSdD6HL
hqIsjmlI33uHWJd9fr72ZTjmxawRca3tS4ljR6QeZw8zE51lh5P4f1pTGa2vUPxX
A11O8jmuXkflgLyDNlm1Pob0tuoNOpa2Zi+mAQIDAQABo4GhMIGeMA4GA1UdDwEB
/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBRIZc7Q7JpI
njO1MmRC3xq2dqp7szBWBgNVHREETzBNggtleGFtcGxlLmNvbYINKi5leGFtcGxl
LmNvbYIMZXhhbXBsZS50ZXN0gglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAA
AAAAAAEwDQYJKoZIhvcNAQELBQADggGBAKuzr0Vb6NZV5XlByLgMb1sddIhl3DWq
RGcAf556am51zvZQFz9W/SBm0Ww7+W1fLx7lasyawsxNhfgNHgP05WKZqghodqIw
1tQJAu8PRuXClrvADUnvMz4a3ELCkygSZNn1Oo84YXssEBe1vy70ZTGZS8hhq5qJ
uKTwN0/UtJXXF0dapd4T+N0kuWuoB1xsf7U9Hdn0Bmw5idWe+U2xvb4/Eo0rPUKa
+GJafosfJ17zV5D4oUbuf1uPg8MqQJzEcQDEOcVDgds7tyy3A3igQ+v1WYDkUcUZ
IfOoQxMfHF7mGDbAETGnn0pSRztgKdobpg7MItbdiI04XSIwgGZL4puLJZfSkRDC
ZLqZk9JJc8DpLRjGmH7lRcKkq8TkbrKGsYOQ3bWV6rLP1xH++rE7Sld7P8bLhapQ
hIwcy1rg38v6u2OmApfEbp+VBa62h7RqBDsW0JSNo8Ut1d0VS2HxHMNZ+pr0WVqO
nGcNj0IS+dchbyqN7WF4X9h1iqD09qWeaw==
MIICpTCCAg6gAwIBAgIJAOvpU0y2e5J5MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV
BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G
A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MjMzNFoX
DTMzMDIyMzA1MjMzNFowVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw
EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEjAQBgNVBAMMCWxv
Y2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzbW1GjECzHUc/WST
qLiAGqjCNccR5saVS+yoz2SPRhpoyf0/qBrX5BY0tzmgozoTiRfE4wCiVD99Cc+D
rp/FM49r4EpZdocIovprmOmv/gwkoj95zaA6PKNn1OdmDp2hwJsX2Zm3kpbGUZTx
jDkkccmgUb4EjL7qNHq7saQtivUCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB
hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE
FB6jTEIWI8T1ckORA4GezbyYxtbvMB8GA1UdIwQYMBaAFEp9nwoXaOUsEOY0voi4
S4ZjSl1vMA0GCSqGSIb3DQEBBQUAA4GBACMboVQjrx8u/fk3gl/sR0tbA0Wf/NcS
2Dzsy2czndgVUAG4Sqb+hfgn0dqAyUKghRrj3JDcYxYksGPIklDfPzZb7yJ39l16
6x5ZiIzhp8CAVdPvRxRznw5rZwaXesryXu1jVSZxTr3MYZdkG6KaAM0t90+YlGLZ
UG8fAicx0Bf+
-----END CERTIFICATE-----

View file

@ -1,28 +1,15 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHyV/bSyb0qRaH
+7p6EnhikOXHsMZp5sGiTclHqt1XjZz1+W6ld2LRS7u2ri62Tisa7qCi0iU8UN5H
GiSo0hoJSakhbaawborM8GnYt/4BHQRt1BIS+qIwvHl1TfIRp8Yp4fXPioyhuERY
hZT0XRXzO64m8J4dEJ0GSx7VdDrhfqDI3WbmVgQQiUZ86PuusAV0KS/rdbl1R6mH
3KggX/9P3KFPHTI0ZR4I66pAQvkexJ0PocuGoiyOaUjfe4dYl31+vvZlOObFrBFx
re1LiWNHpB5nDzMTnWWHk/h/WlMZra9Q/FcDXU7yOa5eR+WAvIM2WbU+hvS26g06
lrZmL6YBAgMBAAECggEAY2zlJnZdGa84hk4RfITKoornv5xK8hMj1EkP3Xm8E8Fn
FsaeePxUEkK1VXGTz6hRLWMKUF1yqHS6Wfo5ukZtLKga4ob2SKGKs/kFRBQ09Yri
VGIQ6J8Qrl5tt6Il0QGEzf0k3rddy3GvmbpI6d+Vd+oAfmKK3X4SFzq1SpdAYk1H
RcC+VLfu60v7yFMtk0IMKMvtXBNY4TPmvJFKv439mLDyIRA5Dsk8P6TZrf473PVJ
JpofVVIKtug8z++CFojhiQYX2bcyIxHWdbUer/snJq4UV+0pOjVYqtB+cVjToBSV
BGNcR+qe8Sg0HXVjpSpKUl3d3GOmCTjKeFzHbu4yTQKBgQD498zP1pW4jH1RVxo8
RVsZWKmZip7iD9MhrUakMW9G9pUrWzl6DG7Na8pnYi5TsZXXak/f5sWhhCPxkae0
0x4Pp7qnSqPvGuTMZQsuG8bvFBWg5XwwrLbx8AzKQ6WggnRumamV5KEwQ8pHMa20
b2fZWPEn3KNR5LASzp9QW3cS2wKBgQDNbfYZOmrEAZw9c53VJG9S1JiqClYHg99T
oTlklsfdNfot78odTto29LO9Tr997ZX0w93jDrxSdLyyiIdZEDVSSkbS3HSdSjfm
nqwlogNXf5TnH88j+EkVvQWs63Q7lyrFNcEftT44ebYXH5ehzPkVbKlocztY0AWw
iVgD8AlrUwKBgQCHE8L+fJks0KRpTk3Ap4x+mvkhgfEkC5vsaJusF9oc8UoLhOoR
Ss+PCk19e+pQpEf84QRHIWjWceAif3kzBVwI+GKvwzJgVwIN10CSmqV7LzVw3zFb
I0n6x9fYoY0qIb1UCHDXD31Yt17284AgJlV8ueHVU8MUenQBMDOFoKphAwKBgGGc
/5SLSpIqhbg5iOTvtCoytPwPf0OA6QyPDnt6nivB60gKqgp1HoGAkyvAEIPg8iFl
NLkglmHD8KoQ7+dlKUEZ5D9r28mlq5xdB0W2j0nzaKjOV5oq72dx8xFwrEFomj0t
RsAvWgpx4xFnxKBwimcgw/rKzodsCVgSWw078O3bAoGBAOyz3sgE9c6mv5jwg2bt
59rqGfHZizMRH0gxiC8P1Ui9dlZfnvdeBLUR/FD6XBSb450vRdcgG4AQ+HPECKkb
kECHkBb542ymb/GqgutNLPzQ8n0Q1zO92CSX5KKmY2B+lEfer5NSc2c9gY261YMH
AApeuDGbws/XXVLWnR1fGbH0
-----END PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDNtbUaMQLMdRz9ZJOouIAaqMI1xxHmxpVL7KjPZI9GGmjJ/T+o
GtfkFjS3OaCjOhOJF8TjAKJUP30Jz4Oun8Uzj2vgSll2hwii+muY6a/+DCSiP3nN
oDo8o2fU52YOnaHAmxfZmbeSlsZRlPGMOSRxyaBRvgSMvuo0eruxpC2K9QIDAQAB
AoGAaD85c/h6bpq7Aj7CBbLaWKhFI3OqwsTITB22vsM7SE+B4zsP02UnG1OVi3UM
zytTUxpUkKV1njQ+bYZYOVqGWF4Up8tTqUglHn0FTPok1AIemELWtz3sXvdSHC1T
lqvFBAZ9kibn13qGyVOiyCFaMwfOM/05RvV7p3jfUMTWnNECQQDs7yCJZ8Ol8MyH
TGZzvkjoN2zg1KwmTbSD1hkP6QAJtPdRuqFbjlEru0/PefgOXsWLRIa3/3v0qw2G
xGkV6AXTAkEA3kNbFisqUydjPnZIYv/P6SvPdUimHJEjXbAbfNfzS9dzszrOVJd2
XqGH7z5yzjoH3IyaIMW8GnubVzGDSjrHFwJAKSU5vELlygpwKkrNO+pelN0TLlQg
dSJnZ8GlZorq88SWcn37iX/EftivenNO7YftvEqxLoDSkOGnnrC7Iw/A+wJBAIEe
L/QY72WPJCBNJpAce/PA96vyoE1II3txqwZDjZspdpVQPDz4IFOpEwbxCFC1dYuy
Qnd3Z2cbF4r3wIWGz9ECQQCJGNhUNtY+Om1ELdqPcquxE2VRV/pucnvJSTKwyo2C
Rvm6H7kFDwPDuN23YnTOlTiho0zzCkclcIukhIVJ+dKz
-----END RSA PRIVATE KEY-----

View file

@ -1,8 +0,0 @@
@echo off
rem Set the release to load code on demand (interactive) instead of preloading (embedded).
rem set RELEASE_MODE=interactive
rem Set the release to work across nodes.
rem RELEASE_DISTRIBUTION must be "sname" (local), "name" (distributed) or "none".
rem set RELEASE_DISTRIBUTION=name
rem set RELEASE_NODE=<%= @release.name %>

View file

@ -1,20 +0,0 @@
#!/bin/sh
# # Sets and enables heart (recommended only in daemon mode)
# case $RELEASE_COMMAND in
# daemon*)
# HEART_COMMAND="$RELEASE_ROOT/bin/$RELEASE_NAME $RELEASE_COMMAND"
# export HEART_COMMAND
# export ELIXIR_ERL_OPTIONS="-heart"
# ;;
# *)
# ;;
# esac
# # Set the release to load code on demand (interactive) instead of preloading (embedded).
# export RELEASE_MODE=interactive
# # Set the release to work across nodes.
# # RELEASE_DISTRIBUTION must be "sname" (local), "name" (distributed) or "none".
# export RELEASE_DISTRIBUTION=name
# export RELEASE_NODE=<%= @release.name %>

View file

@ -1,12 +0,0 @@
## Customize flags given to the VM: https://www.erlang.org/doc/man/erl.html
## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here
## Increase number of concurrent ports/sockets
##+Q 65536
## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10
## Enable deployment without epmd
## (requires changing both vm.args and remote.vm.args)
##-start_epmd false -erl_epmd_port 6789 -dist_listen false

View file

@ -1,12 +0,0 @@
## Customize flags given to the VM: https://www.erlang.org/doc/man/erl.html
## -mode/-name/-sname/-setcookie are configured via env vars, do not set them here
## Increase number of concurrent ports/sockets
##+Q 65536
## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10
## Enable deployment without epmd
## (requires changing both vm.args and remote.vm.args)
##-start_epmd false -erl_epmd_port 6789false

View file

@ -1,29 +0,0 @@
defmodule HTTParrot.Base64HandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.Base64Handler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
test "halts with error" do
expect(:cowboy_req, :binding, [{[:value, :req1], "I="}])
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "proceeds with decoded base64 urlsafe" do
expect(:cowboy_req, :binding, [{[:value, :req1], "LytiYXNlNjQrLw"}])
assert malformed_request(:req1, :state) == {false, :req1, "/+base64+/"}
assert validate(:cowboy_req)
end
test "returns value from state" do
assert get_binary(:req, :decoded) == {:decoded, :req, :decoded}
end
end

View file

@ -4,40 +4,42 @@ defmodule HTTParrot.BasicAuthHandlerTest do
import HTTParrot.BasicAuthHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "is_authorized returns true if user and passwd match" do
expect(:cowboy_req, :binding, [{[:user, :req1], :user}, {[:passwd, :req1], :passwd}])
expect(:cowboy_req, :parse_header, [{["authorization", :req1], {:basic, :user, :passwd}}])
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:user, :passwd}}, :req4}}])
assert is_authorized(:req1, :state) == {true, :req1, :user}
assert is_authorized(:req1, :state) == {true, :req4, :user}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
test "is_authorized returns false if user and passwd doesnt match" do
expect(:cowboy_req, :binding, [{[:user, :req1], :user}, {[:passwd, :req1], :passwd}])
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:not_the_user, :passwd}}, :req4}}])
expect(:cowboy_req, :parse_header, [
{["authorization", :req1], {:basic, :not_the_user, :passwd}}
])
assert is_authorized(:req1, :state) == {{false, "Basic realm=\"Fake Realm\""}, :req4, :state}
assert is_authorized(:req1, :state) == {{false, "Basic realm=\"Fake Realm\""}, :req1, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
test "returns user and if it's authenticated" do
expect(JSX, :encode!, [{[[authenticated: true, user: :user]], :json}])
expect(JSEX, :encode!, [{[[authenticated: true, user: :user]], :json}])
assert get_json(:req1, :user) == {:json, :req1, nil}
assert validate(JSX)
assert validate JSEX
end
end

View file

@ -1,22 +0,0 @@
defmodule HTTParrot.CacheHandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.CacheHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
test "returns prettified json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
end
end

View file

@ -4,19 +4,22 @@ defmodule HTTParrot.CookiesHandlerTest do
import HTTParrot.CookiesHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "returns a JSON with all the cookies o/" do
expect(:cowboy_req, :parse_cookies, 1, k1: :v1)
expect(JSX, :encode!, [{[[cookies: [k1: :v1]]], :json}])
expect(:cowboy_req, :cookies, 1, {[k1: :v1], :req2})
expect(JSEX, :encode!, [{[[cookies: [k1: :v1]]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
end

View file

@ -1,30 +0,0 @@
defmodule HTTParrot.DeflateHandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.DeflateHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
test "returns prettified json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
expect(JSX, :prettify!, [{[:json], "json"}])
expect(:cowboy_req, :set_resp_header, 3, :req3)
opened_zlib = :zlib.open()
:zlib.deflateInit(opened_zlib)
body = :zlib.deflate(opened_zlib, "json", :finish)
:zlib.deflateEnd(opened_zlib)
assert get_json(:req1, :state) == {body, :req3, :state}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
assert validate(:cowboy_req)
end
end

View file

@ -4,52 +4,56 @@ defmodule HTTParrot.DelayedHandlerTest do
import HTTParrot.DelayedHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new HTTParrot.GeneralRequestInfo
new JSEX
new :cowboy_req
end
teardown do
unload HTTParrot.GeneralRequestInfo
unload JSEX
unload :cowboy_req
end
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "a2B="}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"a2B=", :req2}}])
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert malformed_request(:req1, :state) == {true, :req2, :state}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns false if it's an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"2", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 2}
assert malformed_request(:req1, :state) == {false, :req2, 2}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns 0 if 'n' is less than 0" do
expect(:cowboy_req, :binding, [{[:n, :req1], "-1"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"-1", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 0}
assert malformed_request(:req1, :state) == {false, :req2, 0}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns 10 if 'n' is greater than 10" do
expect(:cowboy_req, :binding, [{[:n, :req1], "20"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"20", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 10}
assert malformed_request(:req1, :state) == {false, :req2, 10}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "returns json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
expect(JSEX, :encode!, [{[:info], :json}])
assert get_json(:req1, 0) == {:json, :req2, 0}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
assert validate HTTParrot.GeneralRequestInfo
assert validate JSEX
end
end

View file

@ -4,24 +4,20 @@ defmodule HTTParrot.DeleteCookiesHandlerTest do
import HTTParrot.DeleteCookiesHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
teardown do
unload :cowboy_req
end
test "delete cookies and redirect to /cookies " do
expect(:cowboy_req, :set_resp_cookie, [
{["k1", "v1", :req1, %{path: "/", max_age: 0}], :req2},
{["k2", "v2", :req2, %{path: "/", max_age: 0}], :req3}
])
expect(:cowboy_req, :set_resp_cookie, [{[:k1, :v1, [path: "/", max_age: 0], :req1], :req2},
{[:k2, :v2, [path: "/", max_age: 0], :req2], :req3}])
expect(:cowboy_req, :reply, [{[302, [{"location", "/cookies"}], "Redirecting...", :req3], {:ok, :req4}}])
expect(:cowboy_req, :reply, [
{[302, %{"location" => "/cookies"}, "Redirecting...", :req3], :req4}
])
assert get_json(:req1, [k1: :v1, k2: :v2]) == {:halt, :req4, [k1: :v1, k2: :v2]}
assert get_json(:req1, [{"k1", "v1"}, {"k2", "v2"}]) ==
{:stop, :req4, [{"k1", "v1"}, {"k2", "v2"}]}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
end

View file

@ -4,22 +4,26 @@ defmodule HTTParrot.DeleteHandlerTest do
import HTTParrot.DeleteHandler
setup do
new(:cowboy_req)
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new HTTParrot.GeneralRequestInfo
new JSEX
end
teardown do
unload :cowboy_req
unload HTTParrot.GeneralRequestInfo
unload JSEX
end
test "returns prettified json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
expect(JSEX, :encode!, [{[:info], :json}])
expect(:cowboy_req, :set_resp_body, [{[:json, :req2], :req3}])
assert delete_resource(:req1, :state) == {true, :req3, :state}
assert validate(:cowboy_req)
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
assert validate :cowboy_req
assert validate HTTParrot.GeneralRequestInfo
assert validate JSEX
end
end

View file

@ -4,74 +4,40 @@ defmodule HTTParrot.GeneralRequestInfoTest do
import HTTParrot.GeneralRequestInfo
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
doctest HTTParrot.GeneralRequestInfo
teardown do
unload :cowboy_req
end
test "returns a list of args, headers, url and original ip" do
qs_vals = [{"a", "b"}]
headers = %{"header1" => "value 1", "header2" => "value 2"}
headers = [header1: "value 1", header2: "value 2"]
ip = {127, 1, 2, 3}
url = "http://localhost/get?a=b"
expect(:cowboy_req, :parse_qs, 1, qs_vals)
expect(:cowboy_req, :headers, 1, headers)
expect(:cowboy_req, :uri, 1, url)
expect(:cowboy_req, :peer, 1, {ip, 0})
expect(:cowboy_req, :qs_vals, 1, {qs_vals, :req2})
expect(:cowboy_req, :headers, 1, {headers, :req3})
expect(:cowboy_req, :url, 1, {url, :req4})
expect(:cowboy_req, :peer, 1, {{ip, :host}, :req5})
assert retrieve(:req1) ==
{[args: %{"a" => "b"}, headers: headers, url: url, origin: "127.1.2.3"], :req1}
assert retrieve(:req1) == {[args: qs_vals, headers: headers, url: url, origin: "127.1.2.3"], :req5}
assert validate(:cowboy_req)
end
test "returns a list of args with duplicated keys, headers, url and original ip" do
qs_vals = [{"a", "b"}, {"a", "c"}]
headers = %{"header1" => "value 1", "header2" => "value 2"}
ip = {127, 1, 2, 3}
url = "http://localhost/get?a=b&a=c"
expect(:cowboy_req, :parse_qs, 1, qs_vals)
expect(:cowboy_req, :headers, 1, headers)
expect(:cowboy_req, :uri, 1, url)
expect(:cowboy_req, :peer, 1, {ip, 0})
assert retrieve(:req1) ==
{[args: %{"a" => ["b", "c"]}, headers: headers, url: url, origin: "127.1.2.3"],
:req1}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "returns a list of empty args, headers, url and original ip" do
qs_vals = []
headers = %{"header1" => "value 1", "header2" => "value 2"}
headers = [header1: "value 1", header2: "value 2"]
ip = {127, 1, 2, 3}
url = "http://localhost/get"
expect(:cowboy_req, :parse_qs, 1, qs_vals)
expect(:cowboy_req, :headers, 1, headers)
expect(:cowboy_req, :uri, 1, url)
expect(:cowboy_req, :peer, 1, {ip, 0})
expect(:cowboy_req, :qs_vals, 1, {qs_vals, :req2})
expect(:cowboy_req, :headers, 1, {headers, :req3})
expect(:cowboy_req, :url, 1, {url, :req4})
expect(:cowboy_req, :peer, 1, {{ip, :host}, :req5})
assert retrieve(:req1) ==
{[args: %{}, headers: headers, url: url, origin: "127.1.2.3"], :req1}
assert retrieve(:req1) == {[args: [{}], headers: headers, url: url, origin: "127.1.2.3"], :req5}
assert validate(:cowboy_req)
end
test "returns empty origin when using unix sockets" do
qs_vals = [{"a", "b"}]
headers = %{"header1" => "value 1", "header2" => "value 2"}
url = "http://localhost/get?a=b"
expect(:cowboy_req, :parse_qs, 1, qs_vals)
expect(:cowboy_req, :headers, 1, headers)
expect(:cowboy_req, :uri, 1, url)
expect(:cowboy_req, :peer, 1, {{127, 0, 0, 1}, 0})
assert retrieve(:req1) ==
{[args: %{"a" => "b"}, headers: headers, url: url, origin: ""], :req1}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
end

View file

@ -4,19 +4,22 @@ defmodule HTTParrot.GetHandlerTest do
import HTTParrot.GetHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
new HTTParrot.GeneralRequestInfo
new JSEX
end
teardown do
unload HTTParrot.GeneralRequestInfo
unload JSEX
end
test "returns prettified json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
expect(JSEX, :encode!, [{[:info], :json}])
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
assert validate HTTParrot.GeneralRequestInfo
assert validate JSEX
end
end

View file

@ -1,27 +0,0 @@
defmodule HTTParrot.GzipHandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.GzipHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
test "returns prettified json with query values, headers, url and origin" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req2})
expect(JSX, :encode!, [{[:info], :json}])
expect(JSX, :prettify!, [{[:json], "json"}])
expect(:cowboy_req, :set_resp_header, 3, :req3)
body = :zlib.gzip("json")
assert get_json(:req1, :state) == {body, :req3, :state}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
assert validate(:cowboy_req)
end
end

View file

@ -4,19 +4,22 @@ defmodule HTTParrot.HeadersHandlerTest do
import HTTParrot.HeadersHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "returns prettified json with headers list" do
expect(:cowboy_req, :headers, 1, :headers)
expect(JSX, :encode!, [{[[headers: :headers]], :json}])
expect(:cowboy_req, :headers, 1, {:headers, :req2})
expect(JSEX, :encode!, [{[[headers: :headers]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
end

View file

@ -4,46 +4,42 @@ defmodule HTTParrot.HiddenBasicAuthHandlerTest do
import HTTParrot.HiddenBasicAuthHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "resource_exists returns true if user and passwd match" do
expect(:cowboy_req, :binding, [
{[:user, :req1], :user},
{[:passwd, :req1], :passwd}
])
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:user, :passwd}}, :req4}}])
expect(:cowboy_req, :parse_header, [
{["authorization", :req1], {:basic, :user, :passwd}}
])
assert resource_exists(:req1, :state) == {true, :req4, :user}
assert resource_exists(:req1, :state) == {true, :req1, :user}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
test "resource_exists returns false if user and passwd doesn't match" do
expect(:cowboy_req, :binding, [{[:user, :req1], :user}, {[:passwd, :req1], :passwd}])
test "resource_exists returns false if user and passwd doesnt match" do
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:not_the_user, :passwd}}, :req4}}])
expect(:cowboy_req, :parse_header, [
{["authorization", :req1], {:basic, :not_the_user, :passwd}}
])
assert resource_exists(:req1, :state) == {false, :req4, :state}
assert resource_exists(:req1, :state) == {false, :req1, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
test "returns user and if it's authenticated" do
expect(JSX, :encode!, [{[[authenticated: true, user: :user]], :json}])
expect(JSEX, :encode!, [{[[authenticated: true, user: :user]], :json}])
assert get_json(:req1, :user) == {:json, :req1, nil}
assert validate(JSX)
assert validate JSEX
end
end

View file

@ -4,18 +4,17 @@ defmodule HTTParrotTest do
import HTTParrot
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
teardown do
unload :cowboy_req
end
test "'prettify_json' prettifies body response if it's a JSON" do
expect(:cowboy_req, :reply, [
{[:status, %{"content-length" => ~c"14"}, "{\n \"a\": \"b\"\n}", :req1], :req2}
])
assert prettify_json(:status, %{"content-length" => ~c"12"}, "{\"a\":\"b\"}", :req1) == :req2
assert validate(:cowboy_req)
expect(:cowboy_req, :reply, [{[:status, [{"content-length", '14'}], "{\n \"a\": \"b\"\n}", :req1], {:ok, :req2}}])
assert prettify_json(:status, [{"content-length", '12'}], "{\"a\":\"b\"}", :req1) == :req2
assert validate :cowboy_req
end
test "'prettify_json' does nothing if body is not a JSON" do

View file

@ -4,44 +4,23 @@ defmodule HTTParrot.IPHandlerTest do
import HTTParrot.IPHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "returns prettified json with origin" do
ip = {127, 1, 2, 3}
expect(:cowboy_req, :peer, 1, {ip, 0})
expect(:cowboy_req, :parse_header, 2, :undefined)
expect(JSX, :encode!, [{[[origin: "127.1.2.3"]], :json}])
expect(:cowboy_req, :peer, 1, {{ip, :host}, :req2})
expect(JSEX, :encode!, [{[[origin: "127.1.2.3"]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(:cowboy_req)
assert validate(JSX)
end
test "returns prettified json with client ip when available" do
ip = {127, 1, 2, 3}
expect(:cowboy_req, :peer, 1, {ip, 0})
expect(:cowboy_req, :parse_header, 2, ["190.1.2.3"])
expect(JSX, :encode!, [{[[origin: "190.1.2.3"]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert validate(:cowboy_req)
assert validate(JSX)
end
test "returns empty when running over unix sockets" do
expect(:cowboy_req, :peer, 1, {{127, 0, 0, 1}, 0})
expect(:cowboy_req, :parse_header, 2, :undefined)
expect(JSX, :encode!, [{[[origin: ""]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
end

View file

@ -4,329 +4,74 @@ defmodule HTTParrot.PHandlerTest do
import HTTParrot.PHandler
setup do
new(HTTParrot.GeneralRequestInfo)
new(JSX)
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new HTTParrot.GeneralRequestInfo
new JSEX
new :cowboy_req
end
Enum.each(
[{"/post", "POST"}, {"/patch", "PATCH"}, {"/put", "PUT"}],
fn {path, verb} ->
teardown do
unload HTTParrot.GeneralRequestInfo
unload JSEX
unload :cowboy_req
end
Enum.each [{"/post", "POST"},
{"/patch", "PATCH"},
{"/put", "PUT"}], fn {path, verb} ->
test "allowed_methods return verb related to #{path}" do
expect(:cowboy_req, :path, 1, unquote(path))
expect(:cowboy_req, :path, 1, {unquote(path), :req2})
assert allowed_methods(:req1, :state) == {[unquote(verb)], :req1, :state}
assert allowed_methods(:req1, :state) == {[unquote(verb)], :req2, :state}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
end
)
test "returns json with general info and P[OST, ATCH, UT] form data" do
expect(:cowboy_req, :read_urlencoded_body, 1, {:ok, :body_qs, :req2})
expect(:cowboy_req, :body_qs, 1, {:ok, :body_qs, :req2})
expect(:cowboy_req, :set_resp_body, [{[:response, :req3], :req4}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req3})
expect(JSX, :encode!, [{[[:info, {:form, :body_qs}, {:data, ""}, {:json, nil}]], :response}])
expect(JSEX, :encode!, [{[[:info, {:form, :body_qs}, {:data, ""}, {:json, nil}]], :response}])
assert post_form(:req1, :state) == {true, :req4, nil}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate HTTParrot.GeneralRequestInfo
end
test "returns json with general info and P[OST, ATCH, UT] JSON body data" do
expect(:cowboy_req, :read_body, 1, {:ok, "body", :req2})
expect(:cowboy_req, :body, 1, {:ok, "body", :req2})
expect(:cowboy_req, :set_resp_body, [{[:response, :req3], :req4}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req3})
expect(JSX, :is_json?, 1, true)
expect(JSX, :decode!, 1, :decoded_json)
expect(JSX, :encode!, [
{[[:info, {:form, [{}]}, {:data, "body"}, {:json, :decoded_json}]], :response}
])
expect(JSEX, :is_json?, 1, true)
expect(JSEX, :decode!, 1, :decoded_json)
expect(JSEX, :encode!, [{[[:info, {:form, [{}]}, {:data, "body"}, {:json, :decoded_json}]], :response}])
assert post_binary(:req1, :state) == {true, :req4, nil}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate HTTParrot.GeneralRequestInfo
end
test "returns json with general info and P[OST, ATCH, UT] non-JSON body data" do
expect(:cowboy_req, :read_body, 1, {:ok, "body", :req2})
expect(:cowboy_req, :body, 1, {:ok, "body", :req2})
expect(:cowboy_req, :set_resp_body, [{[:response, :req3], :req4}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req3})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [{[[:info, {:form, [{}]}, {:data, "body"}, {:json, nil}]], :response}])
expect(JSEX, :is_json?, 1, false)
expect(JSEX, :encode!, [{[[:info, {:form, [{}]}, {:data, "body"}, {:json, nil}]], :response}])
assert post_binary(:req1, :state) == {true, :req4, nil}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate HTTParrot.GeneralRequestInfo
end
test "returns json with general info and P[OST, ATCH, UT] octet-stream body data" do
expect(:cowboy_req, :read_body, 1, {:ok, <<0xFFFF::16>>, :req2})
expect(:cowboy_req, :body, 1, {:ok, <<0xffff :: 16>>, :req2})
expect(:cowboy_req, :set_resp_body, [{[:response, :req3], :req4}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req3})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[
[
:info,
{:form, [{}]},
{:data, "data:application/octet-stream;base64,//8="},
{:json, nil}
]
], :response}
])
expect(JSEX, :is_json?, 1, false)
expect(JSEX, :encode!, [{[[:info, {:form, [{}]}, {:data, "data:application/octet-stream;base64,//8="}, {:json, nil}]], :response}])
assert post_binary(:req1, :state) == {true, :req4, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, UT, ATCH] non-JSON body data for a chunked request" do
first_chunk = "first chunk"
second_chunk = "second chunk"
expect(:cowboy_req, :read_body, fn req ->
case req do
:req1 ->
{:more, first_chunk, :req2}
:req2 ->
{:ok, second_chunk, :req3}
end
end)
expect(:cowboy_req, :set_resp_body, [{[:response, :req4], :req5}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req4})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[[:info, {:form, [{}]}, {:data, first_chunk <> second_chunk}, {:json, nil}]], :response}
])
assert post_binary(:req1, :state) == {true, :req5, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, UT, ATCH] octect-stream body data for a chunked request" do
first_chunk = "first chunk" <> <<0xFFFF::16>>
second_chunk = "second chunk"
expect(:cowboy_req, :read_body, fn req ->
case req do
:req1 ->
{:more, first_chunk, :req2}
:req2 ->
{:ok, second_chunk, :req3}
end
end)
expect(:cowboy_req, :set_resp_body, [{[:response, :req4], :req5}])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req4})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[
[
:info,
{:form, [{}]},
{:data,
"data:application/octet-stream;base64,#{Base.encode64(first_chunk <> second_chunk)}"},
{:json, nil}
]
], :response}
])
assert post_binary(:req1, :state) == {true, :req5, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, ATCH, UT] form data for multipart request (simple)" do
expect(:cowboy_req, :read_part, fn req ->
case req do
:req1 ->
{:ok, %{"content-disposition" => "form-data; name=\"key1\""}, :req2}
:req3 ->
{:done, :req4}
end
end)
expect(:cowboy_req, :read_part_body, [{[:req2], {:ok, "value1", :req3}}])
expect(:cowboy_req, :parse_header, [
{["content-type", :req4],
{:ok, {"multipart", "form-data", [{"boundary", "----WebKitFormBoundary8BEQxJvZANFsvRV9"}]},
:req5}}
])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req6})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[[:info, {:form, [{"key1", "value1"}]}, {:files, [{}]}, {:data, ""}, {:json, nil}]],
:response}
])
expect(:cowboy_req, :set_resp_body, [{[:response, :req6], :req7}])
assert post_multipart(:req1, :state) == {true, :req7, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, ATCH, UT] form data for multipart requests (multiple parts)" do
expect(:cowboy_req, :read_part, fn req ->
case req do
:req1 ->
{:ok, %{"content-disposition" => "form-data; name=\"key1\""}, :req2}
:req3 ->
{:ok, %{"content-disposition" => "form-data; name=\"key2\""}, :req4}
:req5 ->
{:done, :req6}
end
end)
expect(:cowboy_req, :read_part_body, fn req ->
case req do
:req2 -> {:ok, "value1", :req3}
:req4 -> {:ok, "value2", :req5}
end
end)
expect(:cowboy_req, :parse_header, [
{["content-type", :req6],
{:ok, {"multipart", "form-data", [{"boundary", "----WebKitFormBoundary8BEQxJvZANFsvRV9"}]},
:req7}}
])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req8})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[
[
:info,
{:form, [{"key1", "value1"}, {"key2", "value2"}]},
{:files, [{}]},
{:data, ""},
{:json, nil}
]
], :response}
])
expect(:cowboy_req, :set_resp_body, [{[:response, :req8], :req9}])
assert post_multipart(:req1, :state) == {true, :req9, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, UT, ATCH] file data (one file)" do
expect(:cowboy_req, :read_part, fn req ->
case req do
:req1 ->
{:ok,
%{
"content-disposition" => "form-data; name=\"file1\"; filename=\"testdata.txt\"",
"content-type" => "text/plain"
}, :req2}
:req3 ->
{:done, :req4}
end
end)
expect(:cowboy_req, :read_part_body, [
{[:req2], {:ok, "here is some cool\ntest data.", :req3}}
])
expect(:cowboy_req, :parse_header, [
{["content-type", :req4],
{:ok, {"multipart", "form-data", [{"boundary", "----WebKitFormBoundary8BEQxJvZANFsvRV9"}]},
:req5}}
])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req6})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[
[
:info,
{:form, [{}]},
{:files, [{"file1", "here is some cool\ntest data."}]},
{:data, ""},
{:json, nil}
]
], :response}
])
expect(:cowboy_req, :set_resp_body, [{[:response, :req6], :req7}])
assert post_multipart(:req1, :state) == {true, :req7, nil}
assert validate(HTTParrot.GeneralRequestInfo)
end
test "returns json with general info and P[OST, UT, ATCH] form data and file data (form-data plus one file)" do
expect(:cowboy_req, :read_part, fn req ->
case req do
:req1 ->
{:ok, %{"content-disposition" => "form-data; name=\"key1\""}, :req2}
:req3 ->
{:ok,
%{
"content-disposition" => "form-data; name=\"file1\"; filename=\"testdata.txt\"",
"content-type" => "text/plain"
}, :req4}
:req5 ->
{:done, :req6}
end
end)
expect(:cowboy_req, :read_part_body, fn req ->
case req do
:req2 -> {:ok, "value1", :req3}
:req4 -> {:ok, "here is some cool\ntest data", :req5}
end
end)
expect(:cowboy_req, :parse_header, [
{["content-type", :req6],
{:ok, {"multipart", "form-data", [{"boundary", "----WebKitFormBoundary8BEQxJvZANFsvRV9"}]},
:req7}}
])
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req8})
expect(JSX, :is_json?, 1, false)
expect(JSX, :encode!, [
{[
[
:info,
{:form, [{"key1", "value1"}]},
{:files, [{"file1", "here is some cool\ntest data"}]},
{:data, ""},
{:json, nil}
]
], :response}
])
expect(:cowboy_req, :set_resp_body, [{[:response, :req8], :req9}])
assert post_multipart(:req1, :state) == {true, :req9, nil}
assert validate(HTTParrot.GeneralRequestInfo)
assert validate HTTParrot.GeneralRequestInfo
end
end

View file

@ -4,48 +4,51 @@ defmodule HTTParrot.RedirectHandlerTest do
import HTTParrot.RedirectHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
test "malformed_request returns true if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "a2B="}])
teardown do
unload :cowboy_req
end
assert malformed_request(:req1, :state) == {true, :req1, :state}
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], {"a2B=", :req2}}])
assert validate(:cowboy_req)
assert malformed_request(:req1, :state) == {true, :req2, :state}
assert validate :cowboy_req
end
test "malformed_request returns false if it's an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"2", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 2}
assert malformed_request(:req1, :state) == {false, :req2, 2}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns 1 if 'n' is less than 1" do
expect(:cowboy_req, :binding, [{[:n, :req1], "0"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"0", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 1}
assert malformed_request(:req1, :state) == {false, :req2, 1}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "moved_permanently returns 'redirect/n-1' if n > 1" do
expect(:cowboy_req, :uri, 2, "host")
test "moved_temporarily returns 'redirect/n-1' if n > 1" do
expect(:cowboy_req, :host_url, 1, {"host", :req2})
assert moved_permanently(:req1, 4) == {{true, "host/redirect/3"}, :req1, nil}
assert moved_temporarily(:req1, 4) == {{true, "host/redirect/3"}, :req2, nil}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "moved_permanently returns '/get' if n = 1" do
expect(:cowboy_req, :uri, 2, "host")
test "moved_temporarily returns '/get' if n = 1" do
expect(:cowboy_req, :host_url, 1, {"host", :req2})
assert moved_permanently(:req1, 1) == {{true, "host/get"}, :req1, nil}
assert moved_temporarily(:req1, 1) == {{true, "host/get"}, :req2, nil}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
end

View file

@ -4,20 +4,22 @@ defmodule HTTParrot.RedirectToHandlerTest do
import HTTParrot.RedirectToHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
teardown do
unload :cowboy_req
end
test "malformed_request returns true if no 'url' is defined" do
expect(:cowboy_req, :match_qs, 2, %{url: :url})
assert malformed_request(:req1, :state) == {false, :req1, :url}
assert validate(:cowboy_req)
expect(:cowboy_req, :qs_val, [{["url", :req1, nil], {:url, :req2}}])
assert malformed_request(:req1, :state) == {false, :req2, :url}
assert validate :cowboy_req
end
test "malformed_request returns false if no 'url' is defined" do
expect(:cowboy_req, :match_qs, 2, %{url: nil})
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
expect(:cowboy_req, :qs_val, [{["url", :req1, nil], {nil, :req2}}])
assert malformed_request(:req1, :state) == {true, :req2, :state}
assert validate :cowboy_req
end
end

View file

@ -1,43 +0,0 @@
defmodule HTTParrot.RelativeRedirectHandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.RelativeRedirectHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "a2B="}])
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "malformed_request returns false if it's an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
assert malformed_request(:req1, :state) == {false, :req1, 2}
assert validate(:cowboy_req)
end
test "malformed_request returns 1 if 'n' is less than 1" do
expect(:cowboy_req, :binding, [{[:n, :req1], "0"}])
assert malformed_request(:req1, :state) == {false, :req1, 1}
assert validate(:cowboy_req)
end
test "moved_permanently returns 'redirect/n-1' if n > 1" do
assert moved_permanently(:req1, 4) == {{true, "/redirect/3"}, :req1, nil}
end
test "moved_permanently returns '/get' if n = 1" do
assert moved_permanently(:req1, 1) == {{true, "/get"}, :req1, nil}
end
end

View file

@ -1,13 +0,0 @@
defmodule HTTParrot.RequestStoreTest do
alias HTTParrot.RequestStore
use ExUnit.Case
test "save, retrieve, clear" do
request = %{req: 1}
RequestStore.clear(:test)
RequestStore.store(:test, request)
assert RequestStore.retrieve(:test) == [request]
RequestStore.clear(:test)
assert RequestStore.retrieve(:test) == []
end
end

View file

@ -1,39 +0,0 @@
defmodule HTTParrot.ResponseHeadersHandlerTest do
use ExUnit.Case
import :meck
import HTTParrot.ResponseHeadersHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
test "malformed_request returns true if query string is empty" do
expect(:cowboy_req, :parse_qs, 1, [])
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "malformed_request returns false if query string is not empty" do
expect(:cowboy_req, :parse_qs, 1, [{"foo", "bar"}])
assert malformed_request(:req1, :state) == {false, :req1, [{"foo", "bar"}]}
assert validate(:cowboy_req)
end
test "query string parameters are sent as headers" do
expect(:cowboy_req, :set_resp_header, [{[:k1, :v1, :req1], :req2}, {[:k2, :v2, :req2], :req3}])
expect(JSX, :encode!, [{[[k1: :v1, k2: :v2]], :json}])
assert get_json(:req1, k1: :v1, k2: :v2) == {:json, :req3, [k1: :v1, k2: :v2]}
assert validate(:cowboy_req)
assert validate(JSX)
end
end

View file

@ -1,17 +0,0 @@
defmodule HTTParrot.RetrieveRequestHandlerTests do
use ExUnit.Case
import :meck
import HTTParrot.RetrieveRequestHandler
setup do
HTTParrot.RequestStore.clear(:test)
on_exit(fn -> unload() end)
:ok
end
test "returns saved requests" do
expect(:cowboy_req, :binding, [:key, :req1], {:test, :req1})
HTTParrot.RequestStore.store(:test, :req1)
assert retrieve_stored(:req1, :state) == {"[\"req1\"]", :req1, :state}
end
end

View file

@ -4,72 +4,20 @@ defmodule HTTParrot.SetCookiesHandlerTest do
import HTTParrot.SetCookiesHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
test "malformed_request returns false if /name/value is sent" do
expect(:cowboy_req, :parse_qs, 1, [])
expect(:cowboy_req, :binding, [
{[:name, :req1, nil], "name"},
{[:value, :req1, nil], "value"}
])
assert malformed_request(:req1, :state) == {false, :req1, [{"name", "value"}]}
assert validate(:cowboy_req)
end
test "malformed_request returns query string values too if /name/value is sent" do
expect(:cowboy_req, :parse_qs, 1, [{"name", "value2"}])
expect(:cowboy_req, :binding, [
{[:name, :req1, nil], "name"},
{[:value, :req1, nil], "value"}
])
expect(:cowboy_req, :binding, [
{[:name, :req1, nil], "name"},
{[:value, :req1, nil], "value"}
])
assert malformed_request(:req1, :state) == {false, :req1, [{"name", "value"}]}
assert validate(:cowboy_req)
end
test "malformed_request returns false if query string values are sent" do
expect(:cowboy_req, :parse_qs, 1, [{"name", "value"}])
expect(:cowboy_req, :binding, [{[:name, :req1, nil], nil}])
assert malformed_request(:req1, :state) == {false, :req1, [{"name", "value"}]}
assert validate(:cowboy_req)
end
test "malformed_request returns true if query string values are not sent" do
expect(:cowboy_req, :parse_qs, 1, [])
expect(:cowboy_req, :binding, [{[:name, :req1, nil], nil}])
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
teardown do
unload :cowboy_req
end
test "redirect to /cookies " do
expect(:cowboy_req, :set_resp_cookie, [
{[:k1, :v1, :req1, %{path: "/"}], :req2},
{[:k2, :v2, :req2, %{path: "/"}], :req3}
])
expect(:cowboy_req, :set_resp_cookie, [{[:k1, :v1, [path: "/"], :req1], :req2},
{[:k2, :v2, [path: "/"], :req2], :req3}])
expect(:cowboy_req, :reply, [{[302, [{"location", "/cookies"}], "Redirecting...", :req3], {:ok, :req4}}])
expect(:cowboy_req, :reply, [
{[302, %{"location" => "/cookies"}, "Redirecting...", :req3], :req4}
])
assert get_json(:req1, [k1: :v1, k2: :v2]) == {:halt, :req4, [k1: :v1, k2: :v2]}
assert get_json(:req1, k1: :v1, k2: :v2) == {:stop, :req4, [k1: :v1, k2: :v2]}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
end

View file

@ -4,16 +4,18 @@ defmodule HTTParrot.StatusCodeHandlerTest do
import HTTParrot.StatusCodeHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
end
teardown do
unload :cowboy_req
end
test "reply with 'code' as status code" do
code = 123
expect(:cowboy_req, :binding, [{[:code, :req1], code}])
expect(:cowboy_req, :reply, [{[code, %{}, "", :req1], :req2}])
assert get_json(:req1, :state) == {:stop, :req2, :state}
assert validate(:cowboy_req)
expect(:cowboy_req, :binding, [{[:code, :req1], {code, :req2}}])
expect(:cowboy_req, :reply, [{[code, [], "", :req2], {:ok, :req3}}])
assert get_json(:req1, :state) == {:halt, :req3, :state}
assert validate :cowboy_req
end
end

View file

@ -1,26 +0,0 @@
defmodule HTTParrot.StoreRequestHandlerTests do
use ExUnit.Case
import :meck
import HTTParrot.StoreRequestHandler
setup do
HTTParrot.RequestStore.clear(:test)
on_exit(fn -> unload() end)
:ok
end
test "store a request" do
expect(:cowboy_req, :binding, [:key, :req1], :test)
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req1})
assert get(:req1, :state) == {~c'{"saved": "true"}', :req1, :state}
assert HTTParrot.RequestStore.retrieve(:test) == [:info]
end
test "store multiple requests" do
expect(:cowboy_req, :binding, [:key, :req1], :test)
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {:info, :req1})
assert get(:req1, :state) == {~c'{"saved": "true"}', :req1, :state}
assert get(:req2, :state) == {~c'{"saved": "true"}', :req1, :state}
assert HTTParrot.RequestStore.retrieve(:test) == [:info, :info]
end
end

View file

@ -1,116 +0,0 @@
defmodule HTTParrot.StreamBytesHandlerTest do
use ExUnit.Case, async: false
import :meck
import HTTParrot.StreamBytesHandler
setup do
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
test "malformed_request returns true if n is not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "a2B="}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "1234"}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "1024"}
end
end)
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "malformed_request returns false if n is an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "1234"}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "1024"}
end
end)
assert malformed_request(:req1, :state) == {false, :req1, {2, 1234, 1024}}
assert validate(:cowboy_req)
end
test "malformed_request returns true if seed is not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "a2B="}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "1024"}
end
end)
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "malformed_request returns false if seed is an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "7"}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "1024"}
end
end)
assert malformed_request(:req1, :state) == {false, :req1, {2, 7, 1024}}
assert validate(:cowboy_req)
end
test "malformed_request returns true if chunk_size is not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "1234"}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "a2B="}
end
end)
assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req)
end
test "malformed_request returns false if chunk_size is an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :match_qs, fn [{name, [], default}], req ->
case {name, req, default} do
{:seed, :req1, "1234"} -> %{seed: "1234"}
{:chunk_size, :req1, "1024"} -> %{chunk_size: "13"}
end
end)
assert malformed_request(:req1, :state) == {false, :req1, {2, 1234, 13}}
assert validate(:cowboy_req)
end
test "response must stream chunks" do
expect(:cowboy_req, :stream_reply, 3, :req2)
expect(:cowboy_req, :stream_body, 3, :ok)
assert {:stop, :req2, nil} = get_bytes(:req1, {9, 3, 4})
chunk1 = :meck.capture(1, :cowboy_req, :stream_body, :_, 1)
chunk2 = :meck.capture(2, :cowboy_req, :stream_body, :_, 1)
chunk3 = :meck.capture(3, :cowboy_req, :stream_body, :_, 1)
assert String.length(chunk1) == 4
assert String.length(chunk2) == 4
assert String.length(chunk3) == 1
end
end

View file

@ -1,57 +1,67 @@
defmodule HTTParrot.StreamHandlerTest do
use ExUnit.Case, async: false
use ExUnit.Case
import :meck
import HTTParrot.StreamHandler
setup do
new(:cowboy_req)
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new HTTParrot.GeneralRequestInfo
new JSEX
end
test "malformed_request returns true if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "a2B="}])
teardown do
unload :cowboy_req
unload HTTParrot.GeneralRequestInfo
unload JSEX
end
assert malformed_request(:req1, :state) == {true, :req1, :state}
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], {"a2B=", :req2}}])
assert validate(:cowboy_req)
assert malformed_request(:req1, :state) == {true, :req2, :state}
assert validate :cowboy_req
end
test "malformed_request returns false if it's an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], "2"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"2", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 2}
assert malformed_request(:req1, :state) == {false, :req2, 2}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns 1 if 'n' is less than 1" do
expect(:cowboy_req, :binding, [{[:n, :req1], "0"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"0", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 1}
assert malformed_request(:req1, :state) == {false, :req2, 1}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "malformed_request returns 100 if 'n' is greater than 100" do
expect(:cowboy_req, :binding, [{[:n, :req1], "200"}])
expect(:cowboy_req, :binding, [{[:n, :req1], {"200", :req2}}])
assert malformed_request(:req1, :state) == {false, :req1, 100}
assert malformed_request(:req1, :state) == {false, :req2, 100}
assert validate(:cowboy_req)
assert validate :cowboy_req
end
test "response must stream chunks" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req2})
expect(:cowboy_req, :stream_reply, 3, :req2)
expect(:cowboy_req, :stream_body, 3, :req2)
expect(JSX, :encode!, [{[[{:id, 0}, :info]], :json1}, {[[{:id, 1}, :info]], :json2}])
expect(JSEX, :encode!, [{[[{:id, 0}, :info]], :json1},
{[[{:id, 1}, :info]], :json2}])
assert {:stop, :req2, nil} = get_json(:req1, 2)
assert {{:chunked, func}, :req2, nil} = get_json(:req1, 2)
assert is_function(func)
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
send_func = fn(body) -> self <- {:chunk, body} end
func.(send_func)
assert_receive {:chunk, :json1}
assert_receive {:chunk, :json2}
assert validate HTTParrot.GeneralRequestInfo
assert validate JSEX
end
end

View file

@ -1 +1 @@
ExUnit.start()
ExUnit.start

View file

@ -4,19 +4,22 @@ defmodule HTTParrot.UserAgentHandlerTest do
import HTTParrot.UserAgentHandler
setup do
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
new :cowboy_req
new JSEX
end
teardown do
unload :cowboy_req
unload JSEX
end
test "returns prettified json with user agent" do
expect(:cowboy_req, :header, [{["user-agent", :req1, "null"], :user_agent}])
expect(JSX, :encode!, [{[[{"user-agent", :user_agent}]], :json}])
expect(:cowboy_req, :header, [{["user-agent", :req1, "null"], {:user_agent, :req2}}])
expect(JSEX, :encode!, [{[[{"user-agent", :user_agent}]], :json}])
assert get_json(:req1, :state) == {:json, :req1, :state}
assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate(:cowboy_req)
assert validate(JSX)
assert validate :cowboy_req
assert validate JSEX
end
end