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

Compare commits

...

50 commits

Author SHA1 Message Date
edgurgel
cb6373be97 Update URLs to point to render URL 2024-06-26 21:54:55 +12:00
edgurgel
e716a1a327 Add render build script 2024-06-26 21:41:50 +12:00
edgurgel
4f87d8262a mix release.init 2024-06-26 21:37:33 +12:00
Eduardo Gurgel
ca31d47221
Merge pull request #40 from edgurgel/ssl-fix
SSL Fix
2024-06-25 21:30:51 +12:00
edgurgel
690dc6fc30 Bump version 2024-06-25 21:25:35 +12:00
edgurgel
5982706dbb Add .github actions 2024-06-25 21:23:15 +12:00
edgurgel
312b6723f3 mix format and replace certs 2024-06-25 21:23:13 +12:00
Nathan Youngman
d2607766b0 warning: negative steps are not supported in String.slice/2, pass 1..-1//1 instead 2024-06-25 21:22:32 +12:00
Nathan Youngman
6b30597487 update all dependencies 2024-06-25 21:22:30 +12:00
Nathan Youngman
71675f8d3d resolve compiler warnings 2024-06-25 21:21:16 +12:00
Nathan Youngman
aa7ca908f0 update travis ci 2024-06-25 21:21:16 +12:00
Eduardo Gurgel
d6d83a8ac6
Merge pull request #37 from kianmeng/misc-doc-changes
Misc doc changes
2024-06-22 08:01:21 +12:00
Kian-Meng, Ang
3b7d5d5eea Misc doc changes
Besides other documentation changes, this commit ensures the generated
HTML doc for HexDocs.pm will become the source of truth for this Elixir
library and leverage on latest features of ExDoc.
2021-08-30 23:08:45 +08:00
Eduardo
b09754cae3 Update earmark and ex_doc 2020-07-15 20:13:02 +12:00
Eduardo
c638ef7ef9 Bump version 1.3.0 2020-07-15 20:08:34 +12:00
Eduardo Gurgel
171d74bb75
Merge pull request #36 from qgadrian/update-deps
Bump deps
2020-07-14 08:54:14 +12:00
qgadrian
5b3409d5b1
Bump deps 2020-06-29 13:33:39 +02:00
Eduardo Gurgel
3666f9db39
Merge pull request #34 from edgurgel/dependabot/hex/con_cache-0.14.0
Bump con_cache from 0.13.1 to 0.14.0
2019-10-07 21:20:04 +13:00
dependabot-preview[bot]
44c68b2f4e
Bump con_cache from 0.13.1 to 0.14.0
Bumps [con_cache](https://github.com/sasa1977/con_cache) from 0.13.1 to 0.14.0.
- [Release notes](https://github.com/sasa1977/con_cache/releases)
- [Changelog](https://github.com/sasa1977/con_cache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sasa1977/con_cache/compare/0.13.1...0.14.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-07 08:17:13 +00:00
Eduardo Gurgel
deb4015277
Merge pull request #35 from edgurgel/dependabot/hex/cowboy-2.6.3
Bump cowboy from 2.5.0 to 2.6.3
2019-10-07 21:14:57 +13:00
dependabot-preview[bot]
d411887e09
Bump cowboy from 2.5.0 to 2.6.3
Bumps [cowboy](https://github.com/ninenines/cowboy) from 2.5.0 to 2.6.3.
- [Release notes](https://github.com/ninenines/cowboy/releases)
- [Commits](https://github.com/ninenines/cowboy/compare/2.5.0...2.6.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-07 03:34:21 +00:00
Eduardo Gurgel
eb91575330
Merge pull request #33 from edgurgel/dependabot/hex/ex_doc-0.21.2
Bump ex_doc from 0.19.1 to 0.21.2
2019-10-07 16:32:37 +13:00
dependabot-preview[bot]
5fe28208a3
Bump ex_doc from 0.19.1 to 0.21.2
Bumps [ex_doc](https://github.com/elixir-lang/ex_doc) from 0.19.1 to 0.21.2.
- [Release notes](https://github.com/elixir-lang/ex_doc/releases)
- [Changelog](https://github.com/elixir-lang/ex_doc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/elixir-lang/ex_doc/compare/v0.19.1...v0.21.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-10-06 20:41:17 +00:00
edgurgel
dec8d9a039 Release 2019-07-12 20:28:41 +12:00
Eduardo Gurgel
cccd19a0fe Bump Elixir requirement to 1.7 2019-06-30 14:45:44 +12:00
Eduardo Gurgel
1f4f716124 Fix Cowboy 2.5 upgrade issues 2019-06-30 14:44:33 +12:00
Eduardo Gurgel
545101d50c
Merge pull request #32 from alex-strizhakov/master
updating to cowboy 2.5.0
2019-06-30 13:47:08 +12:00
Alex S
755a9fa827 build matrix 2019-06-29 15:46:22 +03:00
Alex S
7f59106d14 removing otp 18 2019-06-29 15:37:00 +03:00
Alex S
dbee63e5ea try 18.3 2019-06-29 15:35:20 +03:00
Alex S
c43ac0dc7c try this one 2019-06-29 15:30:47 +03:00
Alex S
c9ce3f0bc4 otp 21 is not supported 2019-06-29 15:25:42 +03:00
Alex S
65d9502c74 try this one 2019-06-29 15:15:03 +03:00
Alex S
3d13a6a680 bumping otp versions 2019-06-29 15:05:06 +03:00
Alex S
8f179399de bumping ConCache version 2019-06-29 14:57:04 +03:00
Alex S
0d9c6f6854 bumping elixir version 2019-06-29 14:44:24 +03:00
Alex S
3f2bbe78df more fixes 2019-06-29 14:41:16 +03:00
Alex S
eedc9798bc more changes 2019-06-28 19:01:03 +03:00
Alex S
9821d9ce61 updating to cowboy 2.5.0 2019-06-28 09:34:36 +03:00
edgurgel
1bc15bf4c7 Update ex_doc 2018-11-05 08:55:41 +13:00
edgurgel
10e2633bf9 Bump version to 1.1.0 2018-11-05 08:51:42 +13:00
Eduardo Gurgel
8f4b1a4a89
Merge pull request #31 from idabmat/master
Allow to send accept header with delete request
2018-10-31 08:54:39 +13:00
Igor de Alcantara Barroso
e355751abf
Allow to send accept header with delete request 2018-10-30 15:58:31 +01:00
Eduardo Gurgel
84fa15df9b Bump version
[ci skip]
2018-01-07 16:33:45 +13:00
Eduardo Gurgel
f3ed8f8ba3
Merge pull request #30 from nashby/fix/deprecations
Fix deprecation warnings for String.strip and Integer.to_char_list.
2017-11-16 08:45:48 +13:00
Vasiliy Ermolovich
41367be61f Fix deprecation warnings for String.strip and Integer.to_char_list. 2017-11-15 11:07:19 +03:00
Eduardo Gurgel
03c68d85a6 Update hex info 2017-05-21 20:48:15 +12:00
Eduardo Gurgel
82600a98c8 Bump version to 1.0.1
[ci skip]
2017-05-21 20:44:54 +12:00
Eduardo Gurgel
fa901347cd Merge pull request #28 from hveiras/master
Fixing /ip to use client's ip when available
2017-05-21 20:44:23 +12:00
Hernan Veiras
9552b529c4 Fixing /ip to use client's ip when available 2017-05-20 16:34:31 -03:00
81 changed files with 1287 additions and 771 deletions

4
.formatter.exs Normal file
View file

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

54
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,54 @@
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

27
.gitignore vendored
View file

@ -1,5 +1,26 @@
/_build # The directory Mix will write compiled artifacts to.
/deps /_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.
erl_crash.dump erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez *.ez
/doc
# Ignore package tarball (built via "mix hex.build").
httparrot-*.tar
# Temporary files, for example, from tests.
/tmp/

View file

@ -1,12 +0,0 @@
language: elixir
notifications:
recipients:
- eduardo@gurgel.me
elixir:
- 1.4.2
otp_release:
- 18.0
- 18.1
- 19.0
sudo: false
script: mix test

38
Dockerfile Normal file
View file

@ -0,0 +1,38 @@
# 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,4 +1,6 @@
Copyright (c) 2013-2014 Eduardo Gurgel Pinho # The MIT License
Copyright (c) 2013 Eduardo Gurgel <eduardo@gurgel.me>
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View file

@ -1,49 +1,56 @@
# HTTParrot [![Build Status](https://travis-ci.org/edgurgel/httparrot.png?branch=master)](https://travis-ci.org/edgurgel/httparrot) # HTTParrot
HTTP server built on top of 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). [![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).
## Endpoints ## Endpoints
* / This page. * `/` This page.
* /ip Returns Origin IP. * `/ip` Returns Origin IP.
* /user-agent Returns user-agent. * `/user-agent` Returns user-agent.
* /headers Returns header dict. * `/headers` Returns header dict.
* /get Returns GET data. * `/get` Returns GET data.
* /post Returns POST data. * `/post` Returns POST data.
* /put Returns PUT data. * `/put` Returns PUT data.
* /patch Returns PATCH data. * `/patch` Returns PATCH data.
* /delete Returns DELETE data * `/delete` Returns DELETE data
* /gzip Returns gzip-encoded data. * `/gzip` Returns gzip-encoded data.
* /status/:code Returns given HTTP Status code. * `/status/:code` Returns given HTTP Status code.
* /response-headers?key=val Returns given response headers. * `/response-headers?key=val` Returns given response headers.
* /redirect/:n 301 Redirects n times. * `/redirect/:n` 301 Redirects n times.
* /redirect-to?url=foo 301 Redirects to the foo URL. * `/redirect-to?url=foo` 301 Redirects to the foo URL.
* /relative-redirect/:n 301 Relative redirects n times. * `/relative-redirect/:n` 301 Relative redirects n times.
* /cookies Returns cookie data. * `/cookies` Returns cookie data.
* /cookies/set?name=value Sets one or more simple cookies. * `/cookies/set?name=value` Sets one or more simple cookies.
* /cookies/set/name/value Sets one cookie . * `/cookies/set/name/value` Sets one cookie .
* /cookies/delete?name Deletes one or more simple cookies. * `/cookies/delete?name` Deletes one or more simple cookies.
* /basic-auth/:user/:passwd Challenges HTTPBasic Auth. * `/basic-auth/:user/:passwd` Challenges HTTPBasic Auth.
* /hidden-basic-auth/:user/:passwd 404'd BasicAuth. * `/hidden-basic-auth/:user/:passwd` 404'd BasicAuth.
* /digest-auth/:qop/:user/:passwd Challenges HTTP Digest Auth. * `/digest-auth/:qop/:user/:passwd` Challenges HTTP Digest Auth.
* /stream/:n Streams n100 lines. * `/stream/:n` Streams n100 lines.
* /delay/:n Delays responding for n10 seconds. * `/delay/:n` Delays responding for n10 seconds.
* /html Renders an HTML Page. * `/html` Renders an HTML Page.
* /robots.txt Returns some robots.txt rules. * `/robots.txt` Returns some robots.txt rules.
* /deny Denied by robots.txt file. * `/deny` Denied by robots.txt file.
* /cache Returns 200 unless an If-Modified-Since header is provided, when it returns a 304 Not Modified. * `/cache` Returns 200 unless an If-Modified-Since header is provided, when it returns a 304 Not Modified.
* /base64/:value Decodes base64url-encoded string. * `/base64/:value` Decodes base64url-encoded string.
* /image Return an image based on Accept header. * `/image` Return an image based on Accept header.
* /websocket Echo message received through websocket * `/websocket` Echo message received through websocket
## TODO ## TODO
* [ ] /deflate Returns deflate-encoded data. * [ ] `/deflate` Returns deflate-encoded data.
* [ ] /digest-auth/:qop/:user/:passwd Challenges HTTP Digest Auth. * [ ] `/digest-auth/:qop/:user/:passwd` Challenges HTTP Digest Auth.
## License ## Copyright and License
Copyright 2013-2016 Eduardo Gurgel <eduardo@gurgel.me> Copyright (c) 2013 Eduardo Gurgel <eduardo@gurgel.me>
This work is free. You can redistribute it and/or modify it under the This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the LICENSE file for more details. terms of the MIT License. See the [LICENSE.md](./LICENSE.md) file for more details.

10
build.sh Executable file
View file

@ -0,0 +1,10 @@
#!/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

16
config/config.exs Normal file
View file

@ -0,0 +1,16 @@
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")

16
config/runtime.exs Normal file
View file

@ -0,0 +1,16 @@
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

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

View file

@ -9,16 +9,16 @@ defmodule HTTParrot.Base64Handler do
end end
def malformed_request(req, state) do def malformed_request(req, state) do
{value, req} = :cowboy_req.binding(:value, req) value = :cowboy_req.binding(:value, req)
case decode(value) do case decode(value) do
{ :ok, result } -> {false, req, result} {:ok, result} -> {false, req, result}
:error -> {true, req, state} :error -> {true, req, state}
end end
end end
defp decode(value) do defp decode(value) do
pad(value) |> Base.url_decode64 pad(value) |> Base.url_decode64()
end end
defp pad(value) do defp pad(value) do

View file

@ -5,11 +5,12 @@ defmodule HTTParrot.BasicAuthHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def is_authorized(req, state) do def is_authorized(req, state) do
{user, req} = :cowboy_req.binding(:user, req) user = :cowboy_req.binding(:user, req)
{passwd, req} = :cowboy_req.binding(:passwd, req) passwd = :cowboy_req.binding(:passwd, req)
{:ok, auth, req} = :cowboy_req.parse_header("authorization", req) auth = :cowboy_req.parse_header("authorization", req)
case auth do case auth do
{"basic", {^user, ^passwd}} -> {true, req, user} {:basic, ^user, ^passwd} -> {true, req, user}
_ -> {{false, "Basic realm=\"Fake Realm\""}, req, state} _ -> {{false, "Basic realm=\"Fake Realm\""}, req, state}
end end
end end
@ -23,6 +24,6 @@ defmodule HTTParrot.BasicAuthHandler do
end end
defp response(user) do defp response(user) do
[authenticated: true, user: user] |> JSX.encode! [authenticated: true, user: user] |> JSX.encode!()
end end
end end

View file

@ -19,6 +19,6 @@ defmodule HTTParrot.CacheHandler do
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -9,12 +9,12 @@ defmodule HTTParrot.CookiesHandler do
end end
def get_json(req, state) do def get_json(req, state) do
{cookies, req} = :cowboy_req.cookies(req) cookies = :cowboy_req.parse_cookies(req)
cookies = if cookies == [], do: [{}], else: cookies cookies = if cookies == [], do: [{}], else: cookies
{response(cookies), req, state} {response(cookies), req, state}
end end
defp response(cookies) do defp response(cookies) do
[cookies: cookies] |> JSX.encode! [cookies: cookies] |> JSX.encode!()
end end
end end

View file

@ -3,8 +3,8 @@ defmodule HTTParrot.Cowboy do
methods = Keyword.get(opts, :methods, []) methods = Keyword.get(opts, :methods, [])
quote bind_quoted: [methods: methods] do quote bind_quoted: [methods: methods] do
def init(_transport, _req, _opts) do def init(req, state) do
{:upgrade, :protocol, :cowboy_rest} {:cowboy_rest, req, state}
end end
def allowed_methods(req, state) do def allowed_methods(req, state) do

View file

@ -10,18 +10,18 @@ defmodule HTTParrot.DeflateHandler do
end end
def get_json(req, state) do def get_json(req, state) do
zlib = :zlib.open zlib = :zlib.open()
:zlib.deflateInit(zlib) :zlib.deflateInit(zlib)
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_header("content-encoding", "deflate", req) req = :cowboy_req.set_resp_header("content-encoding", "deflate", req)
json = response(info) |> JSX.prettify! json = response(info) |> JSX.prettify!()
response = :zlib.deflate(zlib, json, :finish) response = :zlib.deflate(zlib, json, :finish)
:zlib.deflateEnd(zlib) :zlib.deflateEnd(zlib)
{response, req, state} {response, req, state}
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -3,9 +3,10 @@ defmodule HTTParrot.DelayedHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{n, req} = :cowboy_req.binding(:n, req) n = :cowboy_req.binding(:n, req)
try do try do
n = n |> String.to_integer |> min(10) |> max(0) n = n |> String.to_integer() |> min(10) |> max(0)
{false, req, n} {false, req, n}
rescue rescue
ArgumentError -> {true, req, state} ArgumentError -> {true, req, state}
@ -18,11 +19,11 @@ defmodule HTTParrot.DelayedHandler do
def get_json(req, n) do def get_json(req, n) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
:timer.sleep(n*1000) :timer.sleep(n * 1000)
{response(info), req, n} {response(info), req, n}
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -5,7 +5,7 @@ defmodule HTTParrot.DeleteCookiesHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{qs_vals, req} = :cowboy_req.qs_vals(req) qs_vals = :cowboy_req.parse_qs(req)
if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals} if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals}
end end
@ -14,15 +14,18 @@ defmodule HTTParrot.DeleteCookiesHandler do
end end
def get_json(req, qs_vals) do def get_json(req, qs_vals) do
req = Enum.reduce qs_vals, req, fn({name, value}, req) -> req =
delete_cookie(name, value, req) Enum.reduce(qs_vals, req, fn {name, value}, req ->
end delete_cookie(name, value, req)
{:ok, req} = :cowboy_req.reply(302, [{"location", "/cookies"}], "Redirecting...", req) end)
{:halt, req, qs_vals}
req = :cowboy_req.reply(302, %{"location" => "/cookies"}, "Redirecting...", req)
{:stop, req, qs_vals}
end end
defp delete_cookie(name, true, req), do: delete_cookie(name, "", req) defp delete_cookie(name, true, req), do: delete_cookie(name, "", req)
defp delete_cookie(name, value, req) do defp delete_cookie(name, value, req) do
:cowboy_req.set_resp_cookie(name, value, [path: "/", max_age: 0], req) :cowboy_req.set_resp_cookie(name, value, req, %{path: "/", max_age: 0})
end end
end end

View file

@ -5,6 +5,10 @@ defmodule HTTParrot.DeleteHandler do
alias HTTParrot.GeneralRequestInfo alias HTTParrot.GeneralRequestInfo
use HTTParrot.Cowboy, methods: ~w(DELETE) use HTTParrot.Cowboy, methods: ~w(DELETE)
def content_types_provided(req, state) do
{[{{"application", "json", []}, :get_json}], req, state}
end
def delete_resource(req, state) do def delete_resource(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_body(response(info), req) req = :cowboy_req.set_resp_body(response(info), req)
@ -12,6 +16,6 @@ defmodule HTTParrot.DeleteHandler do
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -18,7 +18,7 @@ defmodule HTTParrot.DenyHandler do
\ .-"` `"-. / \ .-"` `"-. /
'. .' '. .'
'-......-' '-......-'
YOU SHOUDN'T BE HERE YOU SHOULDN'T BE HERE
""" """
def get_plain(req, state) do def get_plain(req, state) do
{@body, req, state} {@body, req, state}

View file

@ -1,14 +1,16 @@
defmodule HTTParrot.GeneralRequestInfo do defmodule HTTParrot.GeneralRequestInfo do
def retrieve(req) do def retrieve(req) do
{args, req} = :cowboy_req.qs_vals(req) args = :cowboy_req.parse_qs(req)
{headers, req} = :cowboy_req.headers(req) headers = :cowboy_req.headers(req)
{url, req} = :cowboy_req.url(req) url = IO.iodata_to_binary(:cowboy_req.uri(req))
{{ip, port}, req} = :cowboy_req.peer(req) {ip, _port} = :cowboy_req.peer(req)
ip = case {ip, port} do ip =
{:local, _} -> "" case ip do
_ -> :inet_parse.ntoa(ip) |> to_string {127, 0, 0, 1} -> ""
end :local -> ""
_ -> :inet_parse.ntoa(ip) |> to_string
end
args = group_by_keys(args) args = group_by_keys(args)
@ -18,11 +20,12 @@ defmodule HTTParrot.GeneralRequestInfo do
@doc """ @doc """
Group by keys and if duplicated keys, aggregate them as a list Group by keys and if duplicated keys, aggregate them as a list
iex> group_by_keys([a: "v1", a: "v2", b: "v3", a: "v4"]) iex> group_by_keys([{"a", "v1"}, {"a", "v2"}, {"b", "v3"}, {"a", "v4"}])
%{a: ["v1", "v2", "v4"], b: "v3"} %{"a" => ["v1", "v2", "v4"], "b" => "v3"}
""" """
@spec group_by_keys(list) :: map @spec group_by_keys(list) :: map
def group_by_keys([]), do: %{} def group_by_keys([]), do: %{}
def group_by_keys(args) do def group_by_keys(args) do
args args
|> Enum.map(fn {k, v} -> %{k => v} end) |> Enum.map(fn {k, v} -> %{k => v} end)

View file

@ -15,6 +15,6 @@ defmodule HTTParrot.GetHandler do
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -12,11 +12,11 @@ defmodule HTTParrot.GzipHandler do
def get_json(req, state) do def get_json(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_header("content-encoding", "gzip", req) req = :cowboy_req.set_resp_header("content-encoding", "gzip", req)
response = response(info) |> JSX.prettify! |> :zlib.gzip response = response(info) |> JSX.prettify!() |> :zlib.gzip()
{response, req, state} {response, req, state}
end end
defp response(info) do defp response(info) do
info |> JSX.encode! info |> JSX.encode!()
end end
end end

View file

@ -6,11 +6,11 @@ defmodule HTTParrot.HeadersHandler do
end end
def get_json(req, state) do def get_json(req, state) do
{headers, req} = :cowboy_req.headers(req) headers = :cowboy_req.headers(req)
{response(headers), req, state} {response(headers), req, state}
end end
defp response(headers) do defp response(headers) do
[headers: headers] |> JSX.encode! [headers: headers] |> JSX.encode!()
end end
end end

View file

@ -8,11 +8,12 @@ defmodule HTTParrot.HiddenBasicAuthHandler do
This method should be `is_authorized`, but this handler will return 404 if the auth fails This method should be `is_authorized`, but this handler will return 404 if the auth fails
""" """
def resource_exists(req, state) do def resource_exists(req, state) do
{user, req} = :cowboy_req.binding(:user, req) user = :cowboy_req.binding(:user, req)
{passwd, req} = :cowboy_req.binding(:passwd, req) passwd = :cowboy_req.binding(:passwd, req)
{:ok, auth, req} = :cowboy_req.parse_header("authorization", req) auth = :cowboy_req.parse_header("authorization", req)
case auth do case auth do
{"basic", {^user, ^passwd}} -> {true, req, user} {:basic, ^user, ^passwd} -> {true, req, user}
_ -> {false, req, state} _ -> {false, req, state}
end end
end end
@ -26,6 +27,6 @@ defmodule HTTParrot.HiddenBasicAuthHandler do
end end
defp response(user) do defp response(user) do
[authenticated: true, user: user] |> JSX.encode! [authenticated: true, user: user] |> JSX.encode!()
end end
end end

View file

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

View file

@ -9,17 +9,32 @@ defmodule HTTParrot.IPHandler do
end end
def get_json(req, state) do def get_json(req, state) do
{{ip, _port}, req} = :cowboy_req.peer(req) {ip, _port} = :cowboy_req.peer(req)
forwarded_for = :cowboy_req.parse_header("x-forwarded-for", req)
{response(ip), req, state} case forwarded_for do
:undefined ->
{response(ip), req, state}
_ ->
{response(to_tuple(forwarded_for)), req, state}
end
end end
defp response(:local) do defp to_tuple(client_ip) do
[origin: ""] |> JSX.encode! 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 end
defp response(ip) do defp response(ip) do
ip = :inet_parse.ntoa(ip) |> to_string ip = :inet_parse.ntoa(ip) |> to_string
[origin: ip] |> JSX.encode! [origin: ip] |> JSX.encode!()
end end
end end

View file

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

View file

@ -5,9 +5,10 @@ defmodule HTTParrot.RedirectHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{n, req} = :cowboy_req.binding(:n, req) n = :cowboy_req.binding(:n, req)
try do try do
n = n |> String.to_integer |> max(1) n = n |> String.to_integer() |> max(1)
{false, req, n} {false, req, n}
rescue rescue
ArgumentError -> {true, req, state} ArgumentError -> {true, req, state}
@ -18,8 +19,12 @@ defmodule HTTParrot.RedirectHandler do
def previously_existed(req, state), do: {true, req, state} def previously_existed(req, state), do: {true, req, state}
def moved_permanently(req, n) do def moved_permanently(req, n) do
{host_url, req} = :cowboy_req.host_url(req) host_url =
url = if n > 1, do: "/redirect/#{n-1}", else: "/get" IO.iodata_to_binary(
:cowboy_req.uri(req, %{path: :undefined, qs: :undefined, fragment: :undefined})
)
url = if n > 1, do: "/redirect/#{n - 1}", else: "/get"
{{true, host_url <> url}, req, nil} {{true, host_url <> url}, req, nil}
end end
end end

View file

@ -5,7 +5,7 @@ defmodule HTTParrot.RedirectToHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{url, req} = :cowboy_req.qs_val("url", req, nil) %{url: url} = :cowboy_req.match_qs([{:url, [], nil}], req)
if url, do: {false, req, url}, else: {true, req, state} if url, do: {false, req, url}, else: {true, req, state}
end end

View file

@ -12,7 +12,7 @@ defmodule HTTParrot.RelativeRedirectHandler do
def previously_existed(req, state), do: {true, req, state} def previously_existed(req, state), do: {true, req, state}
def moved_permanently(req, n) do def moved_permanently(req, n) do
url = if n > 1, do: "/redirect/#{n-1}", else: "/get" url = if n > 1, do: "/redirect/#{n - 1}", else: "/get"
{{true, url}, req, nil} {{true, url}, req, nil}
end end
end end

View file

@ -1,6 +1,6 @@
defmodule HTTParrot.RequestStore do defmodule HTTParrot.RequestStore do
@moduledoc """ @moduledoc """
Used to store and retrived requests Used to store and retrieved requests
""" """
@doc """ @doc """
Store the requests to the key Store the requests to the key
@ -15,15 +15,18 @@ defmodule HTTParrot.RequestStore do
""" """
def retrieve(key) do def retrieve(key) do
entry = ConCache.get(:requests_registry, key) entry = ConCache.get(:requests_registry, key)
case entry do case entry do
nil -> [] nil ->
_ -> []
entry
_ ->
entry
end end
end end
@doc """ @doc """
Clear the saved data on the coresponding key Clear the saved data on the corresponding key
""" """
def clear(key) do def clear(key) do
ConCache.delete(:requests_registry, key) ConCache.delete(:requests_registry, key)

View file

@ -5,7 +5,8 @@ defmodule HTTParrot.ResponseHeadersHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{qs_vals, req} = :cowboy_req.qs_vals(req) qs_vals = :cowboy_req.parse_qs(req)
if not Enum.empty?(qs_vals) do if not Enum.empty?(qs_vals) do
{false, req, qs_vals} {false, req, qs_vals}
else else
@ -18,13 +19,15 @@ defmodule HTTParrot.ResponseHeadersHandler do
end end
def get_json(req, qs_vals) do def get_json(req, qs_vals) do
req = Enum.reduce qs_vals, req, fn({key, value}, req) -> req =
:cowboy_req.set_resp_header(key, value, req) Enum.reduce(qs_vals, req, fn {key, value}, req ->
end :cowboy_req.set_resp_header(key, value, req)
end)
{response(qs_vals), req, qs_vals} {response(qs_vals), req, qs_vals}
end end
defp response(qs_vals) do defp response(qs_vals) do
qs_vals |> JSX.encode! qs_vals |> JSX.encode!()
end end
end end

View file

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

View file

@ -5,10 +5,11 @@ defmodule HTTParrot.SetCookiesHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
def malformed_request(req, state) do def malformed_request(req, state) do
{qs_vals, req} = :cowboy_req.qs_vals(req) qs_vals = :cowboy_req.parse_qs(req)
{name, req} = :cowboy_req.binding(:name, req, nil) name = :cowboy_req.binding(:name, req, nil)
if name do if name do
{value, req} = :cowboy_req.binding(:value, req, nil) value = :cowboy_req.binding(:value, req, nil)
{false, req, List.keystore(qs_vals, name, 0, {name, value})} {false, req, List.keystore(qs_vals, name, 0, {name, value})}
else else
if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals} if Enum.empty?(qs_vals), do: {true, req, state}, else: {false, req, qs_vals}
@ -20,14 +21,16 @@ defmodule HTTParrot.SetCookiesHandler do
end end
def get_json(req, qs_vals) do def get_json(req, qs_vals) do
req = Enum.reduce qs_vals, req, fn({name, value}, req) -> req =
set_cookie(name, value, req) Enum.reduce(qs_vals, req, fn {name, value}, req ->
end set_cookie(name, value, req)
{:ok, req} = :cowboy_req.reply(302, [{"location", "/cookies"}], "Redirecting...", req) end)
{:halt, req, qs_vals}
req = :cowboy_req.reply(302, %{"location" => "/cookies"}, "Redirecting...", req)
{:stop, req, qs_vals}
end end
defp set_cookie(name, value, req) do defp set_cookie(name, value, req) do
:cowboy_req.set_resp_cookie(name, value, [path: "/"], req) :cowboy_req.set_resp_cookie(name, value, req, %{path: "/"})
end end
end end

View file

@ -9,8 +9,8 @@ defmodule HTTParrot.StatusCodeHandler do
end end
def get_json(req, state) do def get_json(req, state) do
{code, req} = :cowboy_req.binding(:code, req) code = :cowboy_req.binding(:code, req)
{:ok, req} = :cowboy_req.reply(code, [], "", req) req = :cowboy_req.reply(code, %{}, "", req)
{:halt, req, state} {:stop, req, state}
end end
end end

View file

@ -6,11 +6,13 @@ defmodule HTTParrot.StoreRequestHandler do
use HTTParrot.Cowboy, methods: ~w(GET POST PUT HEAD OPTIONS) use HTTParrot.Cowboy, methods: ~w(GET POST PUT HEAD OPTIONS)
def content_types_accepted(req, state) do def content_types_accepted(req, state) do
{[{{"application", "json", :*}, :post_binary}, {[
{{"application", "octet-stream", :*}, :post_binary}, {{"application", "json", :*}, :post_binary},
{{"text", "plain", :*}, :post_binary}, {{"application", "octet-stream", :*}, :post_binary},
{{"application", "x-www-form-urlencoded", :*}, :post_form}, {{"text", "plain", :*}, :post_binary},
{{"multipart", "form-data", :*}, :post_multipart}], req, state} {{"application", "x-www-form-urlencoded", :*}, :post_form},
{{"multipart", "form-data", :*}, :post_multipart}
], req, state}
end end
def content_types_provided(req, state) do def content_types_provided(req, state) do
@ -19,29 +21,30 @@ defmodule HTTParrot.StoreRequestHandler do
def get(req, state) do def get(req, state) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
{key, _} = :cowboy_req.binding(:key, req) key = :cowboy_req.binding(:key, req)
HTTParrot.RequestStore.store(key, info) HTTParrot.RequestStore.store(key, info)
{'{"saved": "true"}', req, state} {~c'{"saved": "true"}', req, state}
end end
def post_binary(req, _state) do def post_binary(req, _state) do
{:ok, body, req} = HTTParrot.PHandler.handle_binary(req) {:ok, body, req} = HTTParrot.PHandler.handle_binary(req)
if String.valid?(body) do if String.valid?(body) do
if JSX.is_json?(body) do if JSX.is_json?(body) do
save_post(req, [form: [{}], data: body, json: JSX.decode!(body)]) save_post(req, form: [{}], data: body, json: JSX.decode!(body))
else else
save_post(req, [form: [{}], data: body, json: nil]) save_post(req, form: [{}], data: body, json: nil)
end end
else else
# Octet-stream # Octet-stream
body = Base.encode64(body) body = Base.encode64(body)
save_post(req, [form: [{}], data: "data:application/octet-stream;base64," <> body, json: nil]) save_post(req, form: [{}], data: "data:application/octet-stream;base64," <> body, json: nil)
end end
end end
def post_form(req, _state) do def post_form(req, _state) do
{:ok, body, req} = :cowboy_req.body_qs(req) {:ok, body, req} = :cowboy_req.read_urlencoded_body(req)
save_post(req, [form: body, data: "", json: nil]) save_post(req, form: body, data: "", json: nil)
end end
def post_multipart(req, _state) do def post_multipart(req, _state) do
@ -50,17 +53,19 @@ defmodule HTTParrot.StoreRequestHandler do
file_parts = for file <- parts, elem(file, 0) == :file, do: {elem(file, 1), elem(file, 2)} 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)} 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), save_post(req,
files: HTTParrot.PHandler.normalize_list(file_parts), form: HTTParrot.PHandler.normalize_list(form_parts),
data: "", json: nil]) files: HTTParrot.PHandler.normalize_list(file_parts),
data: "",
json: nil
)
end end
def save_post(req, body) do def save_post(req, body) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
{key, req} = :cowboy_req.binding(:key, req) key = :cowboy_req.binding(:key, req)
HTTParrot.RequestStore.store(key, info ++ body) HTTParrot.RequestStore.store(key, info ++ body)
{:ok, req} = :cowboy_req.reply(200, [], '{"saved": "true"}', req) req = :cowboy_req.reply(200, %{}, "{\"saved\": \"true\"}", req)
{:halt, req, nil} {:stop, req, nil}
end end
end end

View file

@ -9,14 +9,14 @@ defmodule HTTParrot.StreamBytesHandler do
end end
def malformed_request(req, state) do def malformed_request(req, state) do
{n, req} = :cowboy_req.binding(:n, req) n = :cowboy_req.binding(:n, req)
{seed, req} = :cowboy_req.qs_val("seed", req, "1234") %{seed: seed} = :cowboy_req.match_qs([{:seed, [], "1234"}], req)
{chunk_size, req} = :cowboy_req.qs_val("chunk_size", req, "1024") %{chunk_size: chunk_size} = :cowboy_req.match_qs([{:chunk_size, [], "1024"}], req)
try do try do
n = n |> String.to_integer n = n |> String.to_integer()
seed = seed |> String.to_integer seed = seed |> String.to_integer()
chunk_size = chunk_size |> String.to_integer chunk_size = chunk_size |> String.to_integer()
{false, req, {n, seed, chunk_size}} {false, req, {n, seed, chunk_size}}
rescue rescue
ArgumentError -> {true, req, state} ArgumentError -> {true, req, state}
@ -26,17 +26,21 @@ defmodule HTTParrot.StreamBytesHandler do
def get_bytes(req, state) do def get_bytes(req, state) do
{n, seed, chunk_size} = state {n, seed, chunk_size} = state
:rand.seed(:exs64, {seed, seed, seed}) :rand.seed(:exs64, {seed, seed, seed})
{{:chunked, stream_response(n, chunk_size)}, req, nil} req = stream_response!(n, chunk_size, req)
{:stop, req, nil}
end end
defp stream_response(n, chunk_size) do defp stream_response!(n, chunk_size, req) do
fn(send_func) -> req = :cowboy_req.stream_reply(200, %{"content-type" => "application/octet-stream"}, req)
Stream.repeatedly(fn -> :rand.uniform(255) end)
|> Stream.take(n) Stream.repeatedly(fn -> :rand.uniform(255) end)
|> Enum.chunk(chunk_size, chunk_size, []) |> Stream.take(n)
|> Enum.each(fn chunk -> |> Enum.chunk_every(chunk_size, chunk_size, [])
send_func.(List.to_string(chunk)) |> Enum.each(fn chunk ->
end) :cowboy_req.stream_body(List.to_string(chunk), :nofin, req)
end end)
:cowboy_req.stream_body("", :fin, req)
req
end end
end end

View file

@ -9,9 +9,10 @@ defmodule HTTParrot.StreamHandler do
`n` must be an integer between 1-100 `n` must be an integer between 1-100
""" """
def malformed_request(req, state) do def malformed_request(req, state) do
{n, req} = :cowboy_req.binding(:n, req) n = :cowboy_req.binding(:n, req)
try do try do
n = n |> String.to_integer |> min(100) |> max(1) n = n |> String.to_integer() |> min(100) |> max(1)
{false, req, n} {false, req, n}
rescue rescue
ArgumentError -> {true, req, state} ArgumentError -> {true, req, state}
@ -24,14 +25,14 @@ defmodule HTTParrot.StreamHandler do
def get_json(req, n) do def get_json(req, n) do
{info, req} = GeneralRequestInfo.retrieve(req) {info, req} = GeneralRequestInfo.retrieve(req)
{{:chunked, stream_response(n, info)}, req, nil} req = :cowboy_req.stream_reply(200, %{"content-type" => "application/json"}, req)
end
defp stream_response(n, info) do Enum.each(0..(n - 1), fn i ->
fn(send_func) -> body = JSX.encode!([id: i] ++ info)
Enum.each 0..n-1, fn (i) -> :cowboy_req.stream_body(body, :nofin, req)
send_func.([id: i] ++ info |> JSX.encode!) end)
end
end :cowboy_req.stream_body("", :fin, req)
{:stop, req, nil}
end end
end end

View file

@ -9,12 +9,12 @@ defmodule HTTParrot.UserAgentHandler do
end end
def get_json(req, state) do def get_json(req, state) do
{user_agent, req} = :cowboy_req.header("user-agent", req, "null") user_agent = :cowboy_req.header("user-agent", req, "null")
{response(user_agent), req, state} {response(user_agent), req, state}
end end
defp response(user_agent) do defp response(user_agent) do
[{"user-agent", user_agent}] [{"user-agent", user_agent}]
|> JSX.encode! |> JSX.encode!()
end end
end end

View file

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

66
mix.exs
View file

@ -1,44 +1,62 @@
defmodule Httparrot.Mixfile do defmodule Httparrot.Mixfile do
use Mix.Project use Mix.Project
@description """ @source_url "https://github.com/edgurgel/httparrot"
HTTP Request & Response Server. An incomplete clone of http://httpbin.org @version "1.4.0"
"""
def project do def project do
[ app: :httparrot, [
version: "1.0.0", app: :httparrot,
elixir: "~> 1.4", version: @version,
elixir: "~> 1.16",
name: "HTTParrot", name: "HTTParrot",
description: @description,
package: package(), package: package(),
deps: deps() ] deps: deps(),
docs: docs()
]
end end
def application do def application do
[ applications: [ :compiler, [
:syntax_tools, extra_applications: [:logger],
:cowboy, mod: {HTTParrot, []}
:exjsx, ]
:con_cache ],
mod: { HTTParrot, [] },
env: [ http_port: 8080, ssl: true, https_port: 8433,
unix_socket: true, socket_path: "httparrot.sock"] ]
end end
defp deps do defp deps do
[ {:cowboy, "~> 1.1.2"}, [
{:cowboy, "~> 2.12"},
{:exjsx, "~> 3.0 or ~> 4.0"}, {:exjsx, "~> 3.0 or ~> 4.0"},
{:con_cache, "~> 0.12.0"}, {:con_cache, "~> 1.1"},
{:ex_doc, ">= 0.0.0", only: :dev}, {:earmark, "~> 1.0", only: :dev},
{:meck, "~> 0.8.2", only: :test } ] {:ex_doc, "~> 0.18", only: :dev},
{:meck, "~> 0.9", only: :test}
]
end end
defp package do defp package do
[ contributors: ["Eduardo Gurgel Pinho"], [
description: "https://github.com/edgurgel/httparrot",
maintainers: ["Eduardo Gurgel Pinho"],
licenses: ["MIT"], licenses: ["MIT"],
links: %{ "Github" => "https://github.com/edgurgel/httparrot", links: %{
"HTTParrot" => "http://httparrot.herokuapp.com", "Github" => @source_url,
"httpbin" => "http://httpbin.org" } ] "HTTParrot" => "https://httparrot.onrender.com/",
"httpbin" => "http://httpbin.org"
}
]
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 end
end end

View file

@ -1,10 +1,17 @@
%{"con_cache": {:hex, :con_cache, "0.12.0", "2d961aec219aa5a914473873f348f5a6088292dc69d5192a9d25f8a1e13e9905", [:mix], [{:exactor, "~> 2.2.0", [hex: :exactor, optional: false]}]}, %{
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]}, "con_cache": {:hex, :con_cache, "1.1.0", "45c7c6cd6dc216e47636232e8c683734b7fe293221fccd9454fa1757bc685044", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8655f2ae13a1e56c8aef304d250814c7ed929c12810f126fc423ecc8e871593b"},
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, "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"},
"earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []}, "cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"ex_doc": {:hex, :ex_doc, "0.13.0", "aa2f8fe4c6136a2f7cfc0a7e06805f82530e91df00e2bff4b4362002b43ada65", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [:mix], []}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, "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"},
"jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], []}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"},
"meck": {:hex, :meck, "0.8.2", "f15f7d513e14ec8c8dee9a95d4ae585b3e5a88bf0fa6a7573240d6ddb58a7236", [:make, :rebar], []}, "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"},
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []}} "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"},
}

View file

@ -56,7 +56,7 @@
<div class='mp'> <div class='mp'>
<h1>httparrot(1): HTTP Request &amp; Response Service</h1> <h1>httparrot(1): HTTP Request &amp; Response Service</h1>
<p>Freely hosted in <a href="http://httparrot.herokuapp.com">HTTP</a> &amp; <a href="https://httparrot.herokuapp.com">HTTPS</a> <p>Freely hosted in <a href="https://httparrot.onrender.com/">HTTPS</a>
<h2 id="ENDPOINTS">ENDPOINTS</h2> <h2 id="ENDPOINTS">ENDPOINTS</h2>
@ -104,34 +104,46 @@ scenarios. Additional endpoints are being considered (e.g. <code>/deflate</code>
<h2 id="EXAMPLES">EXAMPLES</h2> <h2 id="EXAMPLES">EXAMPLES</h2>
<h3 id="-curl-http-httparrot-org-ip">$ curl http://httparrot.herokuapp.com/ip</h3> <h3 id="-curl-http-httparrot-org-ip">$ curl https://httparrot.onrender.com/</h3>
<pre><code>{"origin": "24.127.96.129"} <pre><code>{"origin": "24.127.96.129"}
</code></pre> </code></pre>
<h3 id="-curl-http-httparrot-org-user-agent">$ curl http://httparrot.herokuapp.com/user-agent</h3> <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"} <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> </code></pre>
<h3 id="-curl-http-httparrot-org-get">$ curl http://httparrot.herokuapp.com/get</h3> <h3 id="-curl-http-httparrot-org-get">$ curl https://httparrot.onrender.com/get</h3>
<pre><code>{ <pre><code>
"args": {}, {
"headers": { "args": {},
"Accept": "*/*", "headers": {
"Connection": "close", "accept": "*/*",
"Content-Length": "", "accept-encoding": "gzip",
"Content-Type": "", "cdn-loop": "cloudflare; subreqs=1",
"Host": "httparrot.herokuapp.com", "cf-connecting-ip": "118.148.71.18",
"User-Agent": "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3" "cf-ew-via": "15",
}, "cf-ipcountry": "NZ",
"origin": "24.127.96.129", "cf-ray": "899c4ca20494508c-AKL",
"url": "http://httparrot.herokuapp.com/get" "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> </code></pre>
<h3 id="-curl-I-http-httparrot-org-status-418">$ curl -I http://httparrot.herokuapp.com/status/201</h3> <h3 id="-curl-I-http-httparrot-org-status-418">$ curl -I https://httparrot.onrender.com/status/201</h3>
<pre><code>HTTP/1.1 201 <pre><code>HTTP/1.1 201
Server: Cowboy Server: Cowboy

View file

@ -1,16 +1,27 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIICeDCCAeGgAwIBAgIJAOvpU0y2e5J4MA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV MIIEqjCCAxKgAwIBAgIRANXKM01Lqak+6N93VnHgkl4wDQYJKoZIhvcNAQELBQAw
BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczETMBEGA1UECgwKTmluZSBOaW5lczEPMA0G bTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSEwHwYDVQQLDBhlZHVh
A1UECwwGQ293Ym95MRAwDgYDVQQDDAdST09UIENBMB4XDTEzMDIyODA1MTAwMVoX cmRvQHBvcC1vcyAoRWR1YXJkbykxKDAmBgNVBAMMH21rY2VydCBlZHVhcmRvQHBv
DTMzMDIyMzA1MTAwMVowVTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVRleGFzMRMw cC1vcyAoRWR1YXJkbykwHhcNMjQwNjIzMDU1MzA0WhcNMzQwNjIzMDU1MzA0WjBt
EQYDVQQKDApOaW5lIE5pbmVzMQ8wDQYDVQQLDAZDb3dib3kxEDAOBgNVBAMMB1JP MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExITAfBgNVBAsMGGVkdWFy
T1QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMzmY7Us06yjyUbpqwPx ZG9AcG9wLW9zIChFZHVhcmRvKTEoMCYGA1UEAwwfbWtjZXJ0IGVkdWFyZG9AcG9w
Iv+xh/g3V7we07ClC9GEYnvr3OQvdA1jFEHccMBUUjRoQ8DPd6uSyK5UkixABs08 LW9zIChFZHVhcmRvKTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAK6p
Tt5B3VsnGKr0DIN+IO4SN2PkmBqIU/BN3KdcwN65YNr3iM0KsKWeFtAZdYx4CakX DThGWoUHwCVf8DqHDFthUTvNIKRJzyxArh4GF71dHehiLVBZQ9i/jA1nRASzsVcu
7REbO0wjK20AH3xSBn3uFGiBAgMBAAGjUDBOMB0GA1UdDgQWBBRKfZ8KF2jlLBDm UGyZT18N+XvCCeoPevHXOPjUJZtMkhPqlt55N9gzrniEsmRRQOPzIt8+BiFjJ5Jn
NL6IuEuGY0pdbzAfBgNVHSMEGDAWgBRKfZ8KF2jlLBDmNL6IuEuGY0pdbzAMBgNV RzRD9S+9AwNIaZKUjiQ1Oexxgvd7pbWy3S/IzXCyFsKIZqYcvvpBY6FroDSEZbrk
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG1I0kBxXiLkM1b7rl2zPLizREYg xMA4Fhvy2370nTrlKzuoVTGZ8WdHOsh7Ef+mCZp1QwTtCkIPHdCa3OMs5F7u4q8P
1m+ajb6rWzPOBg6TXjv58Be+H4tqoHIL/M/crixew5emftBkuAGjiKMhbIokjvan tB4QgaWnvEmRtDwsst/CCQr4nIrQzjDwCMS41x0DZwMrQ8lzSjnAHxayWoll9aJK
aPTCV8U6HHvNvz9c68HpESWbd+56cHqfsS5XCKp1OpW5tbL2UQYpFKMP4qmbv3Ea 38jaDviT9qvnIjjdMHN9jJ7fDBZeonrGqdGSs/754oxpSFAiErpBY6dPGYgzkjQM
pBfPPmSFMBb1i2AI 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==
-----END CERTIFICATE----- -----END CERTIFICATE-----

View file

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

View file

@ -1,15 +1,28 @@
-----BEGIN RSA PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
MIICXQIBAAKBgQDNtbUaMQLMdRz9ZJOouIAaqMI1xxHmxpVL7KjPZI9GGmjJ/T+o MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDHyV/bSyb0qRaH
GtfkFjS3OaCjOhOJF8TjAKJUP30Jz4Oun8Uzj2vgSll2hwii+muY6a/+DCSiP3nN +7p6EnhikOXHsMZp5sGiTclHqt1XjZz1+W6ld2LRS7u2ri62Tisa7qCi0iU8UN5H
oDo8o2fU52YOnaHAmxfZmbeSlsZRlPGMOSRxyaBRvgSMvuo0eruxpC2K9QIDAQAB GiSo0hoJSakhbaawborM8GnYt/4BHQRt1BIS+qIwvHl1TfIRp8Yp4fXPioyhuERY
AoGAaD85c/h6bpq7Aj7CBbLaWKhFI3OqwsTITB22vsM7SE+B4zsP02UnG1OVi3UM hZT0XRXzO64m8J4dEJ0GSx7VdDrhfqDI3WbmVgQQiUZ86PuusAV0KS/rdbl1R6mH
zytTUxpUkKV1njQ+bYZYOVqGWF4Up8tTqUglHn0FTPok1AIemELWtz3sXvdSHC1T 3KggX/9P3KFPHTI0ZR4I66pAQvkexJ0PocuGoiyOaUjfe4dYl31+vvZlOObFrBFx
lqvFBAZ9kibn13qGyVOiyCFaMwfOM/05RvV7p3jfUMTWnNECQQDs7yCJZ8Ol8MyH re1LiWNHpB5nDzMTnWWHk/h/WlMZra9Q/FcDXU7yOa5eR+WAvIM2WbU+hvS26g06
TGZzvkjoN2zg1KwmTbSD1hkP6QAJtPdRuqFbjlEru0/PefgOXsWLRIa3/3v0qw2G lrZmL6YBAgMBAAECggEAY2zlJnZdGa84hk4RfITKoornv5xK8hMj1EkP3Xm8E8Fn
xGkV6AXTAkEA3kNbFisqUydjPnZIYv/P6SvPdUimHJEjXbAbfNfzS9dzszrOVJd2 FsaeePxUEkK1VXGTz6hRLWMKUF1yqHS6Wfo5ukZtLKga4ob2SKGKs/kFRBQ09Yri
XqGH7z5yzjoH3IyaIMW8GnubVzGDSjrHFwJAKSU5vELlygpwKkrNO+pelN0TLlQg VGIQ6J8Qrl5tt6Il0QGEzf0k3rddy3GvmbpI6d+Vd+oAfmKK3X4SFzq1SpdAYk1H
dSJnZ8GlZorq88SWcn37iX/EftivenNO7YftvEqxLoDSkOGnnrC7Iw/A+wJBAIEe RcC+VLfu60v7yFMtk0IMKMvtXBNY4TPmvJFKv439mLDyIRA5Dsk8P6TZrf473PVJ
L/QY72WPJCBNJpAce/PA96vyoE1II3txqwZDjZspdpVQPDz4IFOpEwbxCFC1dYuy JpofVVIKtug8z++CFojhiQYX2bcyIxHWdbUer/snJq4UV+0pOjVYqtB+cVjToBSV
Qnd3Z2cbF4r3wIWGz9ECQQCJGNhUNtY+Om1ELdqPcquxE2VRV/pucnvJSTKwyo2C BGNcR+qe8Sg0HXVjpSpKUl3d3GOmCTjKeFzHbu4yTQKBgQD498zP1pW4jH1RVxo8
Rvm6H7kFDwPDuN23YnTOlTiho0zzCkclcIukhIVJ+dKz RVsZWKmZip7iD9MhrUakMW9G9pUrWzl6DG7Na8pnYi5TsZXXak/f5sWhhCPxkae0
-----END RSA PRIVATE KEY----- 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-----

8
rel/env.bat.eex Normal file
View file

@ -0,0 +1,8 @@
@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 %>

20
rel/env.sh.eex Normal file
View file

@ -0,0 +1,20 @@
#!/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 %>

12
rel/remote.vm.args.eex Normal file
View file

@ -0,0 +1,12 @@
## 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

12
rel/vm.args.eex Normal file
View file

@ -0,0 +1,12 @@
## 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

@ -4,22 +4,22 @@ defmodule HTTParrot.Base64HandlerTest do
import HTTParrot.Base64Handler import HTTParrot.Base64Handler
setup do setup do
new :cowboy_req new(:cowboy_req)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
test "halts with error" do test "halts with error" do
expect(:cowboy_req, :binding, [{[:value, :req1], {"I=", :req2}}]) expect(:cowboy_req, :binding, [{[:value, :req1], "I="}])
assert malformed_request(:req1, :state) == {true, :req2, :state} assert malformed_request(:req1, :state) == {true, :req1, :state}
assert validate(:cowboy_req) assert validate(:cowboy_req)
end end
test "proceeds with decoded base64 urlsafe" do test "proceeds with decoded base64 urlsafe" do
expect(:cowboy_req, :binding, [{[:value, :req1], {"LytiYXNlNjQrLw", :req2}}]) expect(:cowboy_req, :binding, [{[:value, :req1], "LytiYXNlNjQrLw"}])
assert malformed_request(:req1, :state) == {false, :req2, "/+base64+/"} assert malformed_request(:req1, :state) == {false, :req1, "/+base64+/"}
assert validate(:cowboy_req) assert validate(:cowboy_req)
end end

View file

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

View file

@ -4,9 +4,9 @@ defmodule HTTParrot.CacheHandlerTest do
import HTTParrot.CacheHandler import HTTParrot.CacheHandler
setup do setup do
new HTTParrot.GeneralRequestInfo new(HTTParrot.GeneralRequestInfo)
new JSX new(JSX)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
@ -16,7 +16,7 @@ defmodule HTTParrot.CacheHandlerTest do
assert get_json(:req1, :state) == {:json, :req2, :state} assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate HTTParrot.GeneralRequestInfo assert validate(HTTParrot.GeneralRequestInfo)
assert validate JSX assert validate(JSX)
end end
end end

View file

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

View file

@ -4,9 +4,9 @@ defmodule HTTParrot.DeflateHandlerTest do
import HTTParrot.DeflateHandler import HTTParrot.DeflateHandler
setup do setup do
new HTTParrot.GeneralRequestInfo new(HTTParrot.GeneralRequestInfo)
new JSX new(JSX)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
@ -16,15 +16,15 @@ defmodule HTTParrot.DeflateHandlerTest do
expect(JSX, :prettify!, [{[:json], "json"}]) expect(JSX, :prettify!, [{[:json], "json"}])
expect(:cowboy_req, :set_resp_header, 3, :req3) expect(:cowboy_req, :set_resp_header, 3, :req3)
opened_zlib = :zlib.open opened_zlib = :zlib.open()
:zlib.deflateInit(opened_zlib) :zlib.deflateInit(opened_zlib)
body = :zlib.deflate(opened_zlib, "json", :finish) body = :zlib.deflate(opened_zlib, "json", :finish)
:zlib.deflateEnd(opened_zlib) :zlib.deflateEnd(opened_zlib)
assert get_json(:req1, :state) == {body, :req3, :state} assert get_json(:req1, :state) == {body, :req3, :state}
assert validate HTTParrot.GeneralRequestInfo assert validate(HTTParrot.GeneralRequestInfo)
assert validate JSX assert validate(JSX)
assert validate :cowboy_req assert validate(:cowboy_req)
end end
end end

View file

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

View file

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

View file

@ -4,10 +4,10 @@ defmodule HTTParrot.DeleteHandlerTest do
import HTTParrot.DeleteHandler import HTTParrot.DeleteHandler
setup do setup do
new :cowboy_req new(:cowboy_req)
new HTTParrot.GeneralRequestInfo new(HTTParrot.GeneralRequestInfo)
new JSX new(JSX)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
@ -18,8 +18,8 @@ defmodule HTTParrot.DeleteHandlerTest do
assert delete_resource(:req1, :state) == {true, :req3, :state} assert delete_resource(:req1, :state) == {true, :req3, :state}
assert validate :cowboy_req assert validate(:cowboy_req)
assert validate HTTParrot.GeneralRequestInfo assert validate(HTTParrot.GeneralRequestInfo)
assert validate JSX assert validate(JSX)
end end
end end

View file

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

View file

@ -4,9 +4,9 @@ defmodule HTTParrot.GetHandlerTest do
import HTTParrot.GetHandler import HTTParrot.GetHandler
setup do setup do
new HTTParrot.GeneralRequestInfo new(HTTParrot.GeneralRequestInfo)
new JSX new(JSX)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
@ -16,7 +16,7 @@ defmodule HTTParrot.GetHandlerTest do
assert get_json(:req1, :state) == {:json, :req2, :state} assert get_json(:req1, :state) == {:json, :req2, :state}
assert validate HTTParrot.GeneralRequestInfo assert validate(HTTParrot.GeneralRequestInfo)
assert validate JSX assert validate(JSX)
end end
end end

View file

@ -4,9 +4,9 @@ defmodule HTTParrot.GzipHandlerTest do
import HTTParrot.GzipHandler import HTTParrot.GzipHandler
setup do setup do
new HTTParrot.GeneralRequestInfo new(HTTParrot.GeneralRequestInfo)
new JSX new(JSX)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end
@ -20,8 +20,8 @@ defmodule HTTParrot.GzipHandlerTest do
assert get_json(:req1, :state) == {body, :req3, :state} assert get_json(:req1, :state) == {body, :req3, :state}
assert validate HTTParrot.GeneralRequestInfo assert validate(HTTParrot.GeneralRequestInfo)
assert validate JSX assert validate(JSX)
assert validate :cowboy_req assert validate(:cowboy_req)
end end
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@ defmodule HTTParrot.RetrieveRequestHandlerTests do
setup do setup do
HTTParrot.RequestStore.clear(:test) HTTParrot.RequestStore.clear(:test)
on_exit fn -> unload() end on_exit(fn -> unload() end)
:ok :ok
end end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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