1
0
Fork 0
mirror of https://github.com/edgurgel/httparrot synced 2025-04-05 08:12:31 -04:00

Compare commits

...

65 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
Eduardo Gurgel
944b685306 Define HTTParrot.init/1 2017-03-16 08:28:23 +13:00
Eduardo Gurgel
97ed0cdfff Merge pull request #27 from steffenix/master
Update cowboy, make exjsx compatible with 3.0 or 4.0
2017-03-15 15:02:15 +13:00
steffel fenix
6b8c1f4927 update cowboy, relax version requirements 2017-03-15 09:51:29 +08:00
Eduardo Gurgel
e8d244330c Bump version to 1.0.0
[ci skip]
2017-03-14 21:25:50 +13:00
Eduardo Gurgel
7d167b02eb Merge pull request #26 from steffenix/master
fix elixir 1.4 warnings, update deps
2017-03-14 21:24:24 +13:00
steffel fenix
27aacc2580 fix elixir 1.4 warnings, update deps 2017-03-14 10:13:08 +08:00
Eduardo Gurgel
444046da94 Merge pull request #24 from jwinter/remove-alias-warning
Fix warning for unused alias for GeneralRequestInfo
2016-12-06 08:55:06 +13:00
Eduardo Gurgel
4969347c45 Merge pull request #25 from jwinter/deflate
Add /deflate support for deflate-encoded data
2016-12-06 08:54:04 +13:00
Joe Winter
66996f4e9a Add /deflate support for deflate-encoded data 2016-12-05 14:37:19 -05:00
Joe Winter
d5e24465e7 Fix warning for unused alias for GeneralRequestInfo 2016-12-02 11:26:09 -05:00
Eduardo Gurgel
9ded53016c Bump to version 0.5.0 2016-10-27 22:40:40 +13:00
Eduardo Gurgel
5713d04090 Merge pull request #23 from rhruiz/unix_socket_support
Adds support for starting HTTParrot over a unix socket
2016-10-27 21:36:39 +13:00
Ricardo Hermida Ruiz
b2e8461af5 Do not support unix sockets on windows 2016-10-26 20:04:19 -02:00
Ricardo Hermida Ruiz
cebf7ad9fa Adds support for starting HTTParrot over a unix socket 2016-10-26 14:14:58 -02:00
Steffel
bbbeb1c0bd Add store and retrieve endpoints (#22) 2016-10-08 20:18:49 +13:00
81 changed files with 1515 additions and 699 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
/deps
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
/doc
# Ignore package tarball (built via "mix hex.build").
httparrot-*.tar
# Temporary files, for example, from tests.
/tmp/

View file

@ -1,13 +0,0 @@
language: elixir
notifications:
recipients:
- eduardo@gurgel.me
elixir:
- 1.2.4
- 1.3.0
otp_release:
- 18.0
- 18.1
- 19.0
sudo: false
script: mix test --no-start

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,6 +5,10 @@ defmodule HTTParrot.DeleteHandler do
alias HTTParrot.GeneralRequestInfo
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
{info, req} = GeneralRequestInfo.retrieve(req)
req = :cowboy_req.set_resp_body(response(info), req)
@ -12,6 +16,6 @@ defmodule HTTParrot.DeleteHandler do
end
defp response(info) do
info |> JSX.encode!
info |> JSX.encode!()
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
{@body, req, state}

View file

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

View file

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

View file

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

View file

@ -6,11 +6,11 @@ defmodule HTTParrot.HeadersHandler do
end
def get_json(req, state) do
{headers, req} = :cowboy_req.headers(req)
headers = :cowboy_req.headers(req)
{response(headers), req, state}
end
defp response(headers) do
[headers: headers] |> JSX.encode!
[headers: headers] |> JSX.encode!()
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
"""
def resource_exists(req, state) do
{user, req} = :cowboy_req.binding(:user, req)
{passwd, req} = :cowboy_req.binding(:passwd, req)
{:ok, auth, req} = :cowboy_req.parse_header("authorization", req)
user = :cowboy_req.binding(:user, req)
passwd = :cowboy_req.binding(:passwd, req)
auth = :cowboy_req.parse_header("authorization", req)
case auth do
{"basic", {^user, ^passwd}} -> {true, req, user}
{:basic, ^user, ^passwd} -> {true, req, user}
_ -> {false, req, state}
end
end
@ -26,6 +27,6 @@ defmodule HTTParrot.HiddenBasicAuthHandler do
end
defp response(user) do
[authenticated: true, user: user] |> JSX.encode!
[authenticated: true, user: user] |> JSX.encode!()
end
end

View file

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

View file

@ -9,12 +9,32 @@ defmodule HTTParrot.IPHandler do
end
def get_json(req, state) do
{{ip, _port}, req} = :cowboy_req.peer(req)
{response(ip), req, state}
{ip, _port} = :cowboy_req.peer(req)
forwarded_for = :cowboy_req.parse_header("x-forwarded-for", req)
case forwarded_for do
:undefined ->
{response(ip), req, state}
_ ->
{response(to_tuple(forwarded_for)), req, state}
end
end
defp to_tuple(client_ip) do
client_ip
|> hd
|> String.split(".")
|> Enum.map(fn x -> String.to_integer(x) end)
|> List.to_tuple()
end
defp response({127, 0, 0, 1}) do
[origin: ""] |> JSX.encode!()
end
defp response(ip) do
ip = :inet_parse.ntoa(ip) |> to_string
[origin: ip] |> JSX.encode!
[origin: ip] |> JSX.encode!()
end
end

View file

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

View file

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

View file

@ -5,7 +5,7 @@ defmodule HTTParrot.RedirectToHandler do
use HTTParrot.Cowboy, methods: ~w(GET HEAD OPTIONS)
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}
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,12 +9,12 @@ defmodule HTTParrot.UserAgentHandler do
end
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}
end
defp response(user_agent) do
[{"user-agent", user_agent}]
|> JSX.encode!
|> JSX.encode!()
end
end

View file

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

67
mix.exs
View file

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

View file

@ -1,8 +1,17 @@
%{"cowboy": {:hex, :cowboy, "1.0.0", "d0b46a5e5f971c5543eb46f86fe76b9add567d12fb5262a852b8f27c64c0555d", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
"cowlib": {:hex, :cowlib, "1.0.0", "397d890d669e56d486b0b5329973ad1a07012412bc110d34a737698dd6941741", [:make], []},
"earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []},
"ex_doc": {:hex, :ex_doc, "0.13.0", "aa2f8fe4c6136a2f7cfc0a7e06805f82530e91df00e2bff4b4362002b43ada65", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]},
"exjsx": {:hex, :exjsx, "3.1.0", "d419162cb2d5be80070835c2c2b8c78c8c45907706c991d23f3750dae506d1e9", [:mix], [{:jsx, "~> 2.4.0", [hex: :jsx, optional: false]}]},
"jsx": {:hex, :jsx, "2.4.0", "fb83830ac15e981b6ce310b645324ceecd01b1e0847d23921c33df829de5d2db", [:mix], []},
"meck": {:hex, :meck, "0.8.2", "f15f7d513e14ec8c8dee9a95d4ae585b3e5a88bf0fa6a7573240d6ddb58a7236", [:make, :rebar], []},
"ranch": {:hex, :ranch, "1.0.0", "e3ac054e4265de77e2d2696dd7e1d6a67b7525aade13ba10c860e7f499a0d433", [:make], []}}
%{
"con_cache": {:hex, :con_cache, "1.1.0", "45c7c6cd6dc216e47636232e8c683734b7fe293221fccd9454fa1757bc685044", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8655f2ae13a1e56c8aef304d250814c7ed929c12810f126fc423ecc8e871593b"},
"cowboy": {:hex, :cowboy, "2.12.0", "f276d521a1ff88b2b9b4c54d0e753da6c66dd7be6c9fca3d9418b561828a3731", [:make, :rebar3], [{:cowlib, "2.13.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "8a7abe6d183372ceb21caa2709bec928ab2b72e18a3911aa1771639bef82651e"},
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"},
"exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"},
"jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
}

View file

@ -56,7 +56,7 @@
<div class='mp'>
<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>
@ -104,34 +104,46 @@ scenarios. Additional endpoints are being considered (e.g. <code>/deflate</code>
<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"}
</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"}
</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>{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Content-Length": "",
"Content-Type": "",
"Host": "httparrot.herokuapp.com",
"User-Agent": "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3"
},
"origin": "24.127.96.129",
"url": "http://httparrot.herokuapp.com/get"
}
<pre><code>
{
"args": {},
"headers": {
"accept": "*/*",
"accept-encoding": "gzip",
"cdn-loop": "cloudflare; subreqs=1",
"cf-connecting-ip": "118.148.71.18",
"cf-ew-via": "15",
"cf-ipcountry": "NZ",
"cf-ray": "899c4ca20494508c-AKL",
"cf-visitor": "{\"scheme\":\"https\"}",
"cf-worker": "onrender.com",
"host": "httparrot.onrender.com",
"render-proxy-ttl": "4",
"rndr-id": "1cf65e46-55f1-429d",
"true-client-ip": "118.148.71.18",
"user-agent": "curl/7.81.0",
"x-forwarded-for": "118.148.71.18, 10.213.36.192, 10.214.43.80",
"x-forwarded-proto": "https",
"x-request-start": "1719395492280333"
},
"url": "http://httparrot.onrender.com/get",
"origin": ""
}
</code></pre>
<h3 id="-curl-I-http-httparrot-org-status-418">$ curl -I 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
Server: Cowboy

View file

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

View file

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

View file

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

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
setup do
new :cowboy_req
on_exit fn -> unload end
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
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)
end
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)
end

View file

@ -4,32 +4,33 @@ defmodule HTTParrot.BasicAuthHandlerTest do
import HTTParrot.BasicAuthHandler
setup do
new :cowboy_req
new JSX
on_exit fn -> unload end
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
test "is_authorized returns true if user and passwd match" do
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:user, :passwd}}, :req4}}])
expect(:cowboy_req, :binding, [{[:user, :req1], :user}, {[:passwd, :req1], :passwd}])
expect(:cowboy_req, :parse_header, [{["authorization", :req1], {:basic, :user, :passwd}}])
assert is_authorized(:req1, :state) == {true, :req4, :user}
assert is_authorized(:req1, :state) == {true, :req1, :user}
assert validate :cowboy_req
assert validate JSX
assert validate(:cowboy_req)
assert validate(JSX)
end
test "is_authorized returns false if user and passwd doesnt match" do
expect(:cowboy_req, :binding, [{[:user, :req1], {:user, :req2}},
{[:passwd, :req2], {:passwd, :req3}}])
expect(:cowboy_req, :parse_header, [{["authorization", :req3], {:ok, {"basic", {:not_the_user, :passwd}}, :req4}}])
expect(:cowboy_req, :binding, [{[:user, :req1], :user}, {[:passwd, :req1], :passwd}])
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 validate JSX
assert is_authorized(:req1, :state) == {{false, "Basic realm=\"Fake Realm\""}, :req1, :state}
assert validate(:cowboy_req)
assert validate(JSX)
end
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 validate JSX
assert validate(JSX)
end
end

View file

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

View file

@ -4,19 +4,19 @@ defmodule HTTParrot.CookiesHandlerTest do
import HTTParrot.CookiesHandler
setup do
new :cowboy_req
new JSX
on_exit fn -> unload end
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
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}])
assert get_json(:req1, :state) == {:json, :req2, :state}
assert get_json(:req1, :state) == {:json, :req1, :state}
assert validate :cowboy_req
assert validate JSX
assert validate(:cowboy_req)
assert validate(JSX)
end
end

View file

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

View file

@ -4,43 +4,43 @@ defmodule HTTParrot.DelayedHandlerTest do
import HTTParrot.DelayedHandler
setup do
new HTTParrot.GeneralRequestInfo
new JSX
new :cowboy_req
on_exit fn -> unload end
new(HTTParrot.GeneralRequestInfo)
new(JSX)
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], {"a2B=", :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
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
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
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
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 validate HTTParrot.GeneralRequestInfo
assert validate JSX
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
end
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,19 +4,19 @@ defmodule HTTParrot.HeadersHandlerTest do
import HTTParrot.HeadersHandler
setup do
new :cowboy_req
new JSX
on_exit fn -> unload end
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
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}])
assert get_json(:req1, :state) == {:json, :req2, :state}
assert get_json(:req1, :state) == {:json, :req1, :state}
assert validate :cowboy_req
assert validate JSX
assert validate(:cowboy_req)
assert validate(JSX)
end
end

View file

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

View file

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

View file

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

View file

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

View file

@ -4,49 +4,48 @@ defmodule HTTParrot.RedirectHandlerTest do
import HTTParrot.RedirectHandler
setup do
new :cowboy_req
on_exit fn -> unload end
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
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
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
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
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
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

View file

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

View file

@ -4,33 +4,33 @@ defmodule HTTParrot.RelativeRedirectHandlerTest do
import HTTParrot.RelativeRedirectHandler
setup do
new :cowboy_req
on_exit fn -> unload end
new(:cowboy_req)
on_exit(fn -> unload() end)
:ok
end
test "malformed_request returns false if it's not an integer" do
expect(:cowboy_req, :binding, [{[:n, :req1], {"a2B=", :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
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
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
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
assert moved_permanently(:req1, 1) == {{true, "/get"}, :req1, nil}
end
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,60 +4,54 @@ defmodule HTTParrot.StreamHandlerTest do
import HTTParrot.StreamHandler
setup do
new :cowboy_req
new HTTParrot.GeneralRequestInfo
new JSX
on_exit fn -> unload end
new(:cowboy_req)
new(HTTParrot.GeneralRequestInfo)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
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
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
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
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
test "response must stream chunks" do
expect(HTTParrot.GeneralRequestInfo, :retrieve, 1, {[:info], :req2})
expect(JSX, :encode!, [{[[{:id, 0}, :info]], :json1},
{[[{:id, 1}, :info]], :json2}])
expect(:cowboy_req, :stream_reply, 3, :req2)
expect(:cowboy_req, :stream_body, 3, :req2)
expect(JSX, :encode!, [{[[{:id, 0}, :info]], :json1}, {[[{:id, 1}, :info]], :json2}])
assert {{:chunked, func}, :req2, nil} = get_json(:req1, 2)
assert is_function(func)
assert {:stop, :req2, nil} = get_json(:req1, 2)
send_func = fn(body) -> send(self, {:chunk, body}) end
func.(send_func)
assert_receive {:chunk, :json1}
assert_receive {:chunk, :json2}
assert validate HTTParrot.GeneralRequestInfo
assert validate JSX
assert validate(HTTParrot.GeneralRequestInfo)
assert validate(JSX)
end
end

View file

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

View file

@ -4,19 +4,19 @@ defmodule HTTParrot.UserAgentHandlerTest do
import HTTParrot.UserAgentHandler
setup do
new :cowboy_req
new JSX
on_exit fn -> unload end
new(:cowboy_req)
new(JSX)
on_exit(fn -> unload() end)
:ok
end
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}])
assert get_json(:req1, :state) == {:json, :req2, :state}
assert get_json(:req1, :state) == {:json, :req1, :state}
assert validate :cowboy_req
assert validate JSX
assert validate(:cowboy_req)
assert validate(JSX)
end
end