changed
CHANGELOG.md
|
@@ -1,3 +1,13 @@
|
1
|
+ # v0.5.0
|
2
|
+
|
3
|
+ * Enhancements
|
4
|
+ * Update to Elixir v0.14.0
|
5
|
+ * Update Cowboy adapter to v0.10.0
|
6
|
+ * Add `Plug.Conn.read_body/2`
|
7
|
+
|
8
|
+ * Backwards incompatible changes
|
9
|
+ * `Plug.Parsers` now expect `:length` instead of `:limit` and also accept `:read_length` and `:read_timeout`
|
10
|
+
|
1
11
|
# v0.4.4
|
2
12
|
|
3
13
|
* Enhancements
|
|
@@ -37,4 +47,4 @@
|
37
47
|
|
38
48
|
# v0.3.0
|
39
49
|
|
40
|
- * Definition of the Plug specification
|
|
\ No newline at end of file
|
50
|
+ * Definition of the Plug specification
|
changed
hex_metadata.config
|
@@ -22,4 +22,4 @@
|
22
22
|
{<<"licenses">>,[<<"Apache 2">>]}.
|
23
23
|
{<<"links">>,#{<<"Github">> => <<"https://github.com/elixir-lang/plug">>}}.
|
24
24
|
{<<"requirements">>,#{}}.
|
25
|
- {<<"version">>,<<"0.4.4">>}.
|
25
|
+ {<<"version">>,<<"0.5.0">>}.
|
changed
lib/plug/adapters/cowboy/conn.ex
|
@@ -2,15 +2,15 @@ defmodule Plug.Adapters.Cowboy.Conn do
|
2
2
|
@behaviour Plug.Conn.Adapter
|
3
3
|
@moduledoc false
|
4
4
|
|
5
|
- alias :cowboy_req, as: R
|
5
|
+ alias :cowboy_req, as: Request
|
6
6
|
|
7
7
|
def conn(req, transport) do
|
8
|
- {path, req} = R.path req
|
9
|
- {host, req} = R.host req
|
10
|
- {port, req} = R.port req
|
11
|
- {meth, req} = R.method req
|
12
|
- {hdrs, req} = R.headers req
|
13
|
- {qs, req} = R.qs req
|
8
|
+ {path, req} = Request.path req
|
9
|
+ {host, req} = Request.host req
|
10
|
+ {port, req} = Request.port req
|
11
|
+ {meth, req} = Request.method req
|
12
|
+ {hdrs, req} = Request.headers req
|
13
|
+ {qs, req} = Request.qs req
|
14
14
|
|
15
15
|
%Plug.Conn{
|
16
16
|
adapter: {__MODULE__, req},
|
|
@@ -25,7 +25,7 @@ defmodule Plug.Adapters.Cowboy.Conn do
|
25
25
|
end
|
26
26
|
|
27
27
|
def send_resp(req, status, headers, body) do
|
28
|
- {:ok, req} = R.reply(status, headers, body, req)
|
28
|
+ {:ok, req} = Request.reply(status, headers, body, req)
|
29
29
|
{:ok, nil, req}
|
30
30
|
end
|
31
31
|
|
|
@@ -33,31 +33,33 @@ defmodule Plug.Adapters.Cowboy.Conn do
|
33
33
|
%File.Stat{type: :regular, size: size} = File.stat!(path)
|
34
34
|
body_fun = fn(socket, transport) -> transport.sendfile(socket, path) end
|
35
35
|
|
36
|
- {:ok, req} = R.reply(status, headers, R.set_resp_body_fun(size, body_fun, req))
|
36
|
+ {:ok, req} = Request.reply(status, headers, Request.set_resp_body_fun(size, body_fun, req))
|
37
37
|
{:ok, nil, req}
|
38
38
|
end
|
39
39
|
|
40
40
|
def send_chunked(req, status, headers) do
|
41
|
- {:ok, req} = R.chunked_reply(status, headers, req)
|
41
|
+ {:ok, req} = Request.chunked_reply(status, headers, req)
|
42
42
|
{:ok, nil, req}
|
43
43
|
end
|
44
44
|
|
45
45
|
def chunk(req, body) do
|
46
|
- R.chunk(body, req)
|
46
|
+ Request.chunk(body, req)
|
47
47
|
end
|
48
48
|
|
49
|
- def stream_req_body(req, limit) do
|
50
|
- R.stream_body(limit, req)
|
49
|
+ def read_req_body(req, opts \\ []) do
|
50
|
+ Request.body(req, opts)
|
51
51
|
end
|
52
52
|
|
53
|
- def parse_req_multipart(req, limit, callback) do
|
54
|
- {:ok, limit, acc, req} = parse_multipart(R.multipart_data(req), limit, %{}, callback)
|
53
|
+ def parse_req_multipart(req, opts, callback) do
|
54
|
+ limit = Keyword.get(opts, :length, 8_000_000)
|
55
|
+ {:ok, limit, acc, req} = parse_multipart(Request.part(req), limit, opts, %{}, callback)
|
56
|
+
|
57
|
+ params = Enum.reduce(acc, %{}, &Plug.Conn.Query.decode_pair/2)
|
55
58
|
|
56
59
|
if limit > 0 do
|
57
|
- params = Enum.reduce(acc, %{}, &Plug.Conn.Query.decode_pair/2)
|
58
60
|
{:ok, params, req}
|
59
61
|
else
|
60
|
- {:too_large, req}
|
62
|
+ {:more, params, req}
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
|
@@ -73,53 +75,63 @@ defmodule Plug.Adapters.Cowboy.Conn do
|
73
75
|
|
74
76
|
## Multipart
|
75
77
|
|
76
|
- defp parse_multipart({:headers, headers, req}, limit, acc, callback) when limit >= 0 do
|
78
|
+ defp parse_multipart({:ok, headers, req}, limit, opts, acc, callback) when limit >= 0 do
|
77
79
|
case callback.(headers) do
|
78
80
|
{:binary, name} ->
|
79
|
- {:ok, limit, body, req} = parse_multipart_body(R.multipart_data(req), limit, "")
|
80
|
- parse_multipart(R.multipart_data(req), limit, Map.put(acc, name, body), callback)
|
81
|
+ {:ok, limit, body, req} = parse_multipart_body(Request.part_body(req, opts), limit, opts, "")
|
82
|
+ parse_multipart(Request.part(req), limit, opts, Map.put(acc, name, body), callback)
|
81
83
|
|
82
84
|
{:file, name, file, %Plug.Upload{} = uploaded} ->
|
83
|
- {:ok, limit, req} = parse_multipart_file(R.multipart_data(req), limit, file)
|
84
|
- parse_multipart(R.multipart_data(req), limit, Map.put(acc, name, uploaded), callback)
|
85
|
+ {:ok, limit, req} = parse_multipart_file(Request.part_body(req, opts), limit, opts, file)
|
86
|
+ parse_multipart(Request.part(req), limit, opts, Map.put(acc, name, uploaded), callback)
|
85
87
|
|
86
88
|
:skip ->
|
87
|
- {:ok, req} = R.multipart_skip(req)
|
88
|
- parse_multipart(R.multipart_data(req), limit, acc, callback)
|
89
|
+ {:ok, req} = Request.multipart_skip(req)
|
90
|
+ parse_multipart(Request.part(req), limit, opts, acc, callback)
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
92
|
- defp parse_multipart({:headers, _headers, req}, limit, acc, _callback) do
|
94
|
+ defp parse_multipart({:ok, _headers, req}, limit, _opts, acc, _callback) do
|
93
95
|
{:ok, limit, acc, req}
|
94
96
|
end
|
95
97
|
|
96
|
- defp parse_multipart({:eof, req}, limit, acc, _callback) do
|
98
|
+ defp parse_multipart({:done, req}, limit, _opts, acc, _callback) do
|
97
99
|
{:ok, limit, acc, req}
|
98
100
|
end
|
99
101
|
|
100
|
- defp parse_multipart_body({:body, tail, req}, limit, body) when limit >= 0 do
|
101
|
- parse_multipart_body(R.multipart_data(req), limit - byte_size(tail), body <> tail)
|
102
|
+ defp parse_multipart_body({:more, tail, req}, limit, opts, body) when limit >= 0 do
|
103
|
+ parse_multipart_body(Request.part_body(req), limit - byte_size(tail), opts, body <> tail)
|
102
104
|
end
|
103
105
|
|
104
|
- defp parse_multipart_body({:body, _tail, req}, limit, body) do
|
106
|
+ defp parse_multipart_body({:more, _tail, req}, limit, _opts, body) do
|
105
107
|
{:ok, limit, body, req}
|
106
108
|
end
|
107
109
|
|
108
|
- defp parse_multipart_body({:end_of_part, req}, limit, body) do
|
110
|
+ defp parse_multipart_body({:ok, tail, req}, limit, _opts, body) when limit >= byte_size(tail) do
|
111
|
+ {:ok, limit, body <> tail, req}
|
112
|
+ end
|
113
|
+
|
114
|
+ defp parse_multipart_body({:ok, _tail, req}, limit, _opts, body) do
|
109
115
|
{:ok, limit, body, req}
|
110
116
|
end
|
111
117
|
|
112
|
- defp parse_multipart_file({:body, tail, req}, limit, file) when limit >= 0 do
|
118
|
+ defp parse_multipart_file({:more, tail, req}, limit, opts, file) when limit >= 0 do
|
113
119
|
:file.write(file, tail)
|
114
|
- parse_multipart_file(R.multipart_data(req), limit - byte_size(tail), file)
|
120
|
+ parse_multipart_file(Request.part_body(req, opts), limit - byte_size(tail), opts, file)
|
115
121
|
end
|
116
122
|
|
117
|
- defp parse_multipart_file({:body, _tail, req}, limit, file) do
|
123
|
+ defp parse_multipart_file({:more, _tail, req}, limit, _opts, file) do
|
118
124
|
:file.close(file)
|
119
125
|
{:ok, limit, req}
|
120
126
|
end
|
121
127
|
|
122
|
- defp parse_multipart_file({:end_of_part, req}, limit, file) do
|
128
|
+ defp parse_multipart_file({:ok, tail, req}, limit, _opts, file) when limit >= byte_size(tail) do
|
129
|
+ :file.write(file, tail)
|
130
|
+ :file.close(file)
|
131
|
+ {:ok, limit, req}
|
132
|
+ end
|
133
|
+
|
134
|
+ defp parse_multipart_file({:ok, _tail, req}, limit, _opts, file) do
|
123
135
|
:file.close(file)
|
124
136
|
{:ok, limit, req}
|
125
137
|
end
|
changed
lib/plug/adapters/test/conn.ex
|
@@ -44,13 +44,16 @@ defmodule Plug.Adapters.Test.Conn do
|
44
44
|
{:ok, body, %{state | chunks: body}}
|
45
45
|
end
|
46
46
|
|
47
|
- def stream_req_body(%{req_body: body} = state, _limit) when byte_size(body) == 0,
|
48
|
- do: {:done, state}
|
49
|
- def stream_req_body(%{req_body: body} = state, limit) do
|
50
|
- size = min(byte_size(body), limit)
|
47
|
+ def read_req_body(%{req_body: body} = state, opts \\ []) do
|
48
|
+ size = min(byte_size(body), Keyword.get(opts, :length, 8_000_000))
|
51
49
|
data = :binary.part(body, 0, size)
|
52
50
|
rest = :binary.part(body, size, byte_size(body) - size)
|
53
|
- {:ok, data, %{state | req_body: rest}}
|
51
|
+ tag =
|
52
|
+ case rest do
|
53
|
+ "" -> :ok
|
54
|
+ _ -> :more
|
55
|
+ end
|
56
|
+ {tag, data, %{state | req_body: rest}}
|
54
57
|
end
|
55
58
|
|
56
59
|
def parse_req_multipart(%{params: multipart} = state, _limit, _callback) do
|
changed
lib/plug/conn.ex
|
@@ -352,6 +352,48 @@ defmodule Plug.Conn do
|
352
352
|
conn
|
353
353
|
end
|
354
354
|
|
355
|
+ @doc """
|
356
|
+ Reads the request body.
|
357
|
+
|
358
|
+ This function reads a chunk of the request body. If there is more data to be
|
359
|
+ read, then `{:more, partial_body, conn}` is returned. Otherwise
|
360
|
+ `{:ok, body, conn}` is returned. In case of error reading the socket,
|
361
|
+ `{:error, reason}` is returned as per `:gen_tcp.recv/2`.
|
362
|
+
|
363
|
+ Because the request body can be of any size, reading the body will only
|
364
|
+ work once, as Plug will not cache the result of these operations. If you
|
365
|
+ need to access the body multiple times, it is your responsibility to store
|
366
|
+ it. Finally keep in mind some plugs like `Plug.Parsers` may read the body,
|
367
|
+ so the body may be unavailable after accessing such plugs.
|
368
|
+
|
369
|
+ This function is able to handle both chunked and identity transfer-encoding
|
370
|
+ by default.
|
371
|
+
|
372
|
+ ## Options
|
373
|
+
|
374
|
+ * `:length` - sets the max body length to read, defaults to 8,000,000 bytes;
|
375
|
+ * `:read_length` - set the amount of bytes to read at one time, defaults to 1,000,000 bytes;
|
376
|
+ * `:read_timeout` - set the timeout for each chunk received, defaults to 15ms;
|
377
|
+
|
378
|
+ ## Example
|
379
|
+
|
380
|
+ {:ok, body, conn} = Plug.Conn.read_body(conn, length: 1_000_000)
|
381
|
+
|
382
|
+ """
|
383
|
+ @spec read_body(t, Keyword.t) :: {:ok, binary, t} |
|
384
|
+ {:more, binary, t} |
|
385
|
+ {:error, binary}
|
386
|
+ def read_body(%Conn{adapter: {adapter, state}} = conn, opts \\ []) do
|
387
|
+ case adapter.read_req_body(state, opts) do
|
388
|
+ {:ok, data, state} ->
|
389
|
+ {:ok, data, %{conn | adapter: {adapter, state}}}
|
390
|
+ {:more, data, state} ->
|
391
|
+ {:more, data, %{conn | adapter: {adapter, state}}}
|
392
|
+ {:error, reason} ->
|
393
|
+ {:error, reason}
|
394
|
+ end
|
395
|
+ end
|
396
|
+
|
355
397
|
@doc """
|
356
398
|
Fetches cookies from the request headers.
|
357
399
|
"""
|
changed
lib/plug/conn/adapter.ex
|
@@ -71,13 +71,15 @@ defmodule Plug.Conn.Adapter do
|
71
71
|
:ok | {:ok, sent_body :: binary, payload} | {:error, term}
|
72
72
|
|
73
73
|
@doc """
|
74
|
- Streams the request body.
|
74
|
+ Reads the request body.
|
75
75
|
|
76
|
- An approximate limit of data to be read from the socket per stream
|
77
|
- can be passed as argument.
|
76
|
+ Read the docs in `Plug.Conn.read_body/2` for the supported
|
77
|
+ options and expected behaviour.
|
78
78
|
"""
|
79
|
- defcallback stream_req_body(payload, limit :: pos_integer) ::
|
80
|
- {:ok, data :: binary, payload} | {:done, payload}
|
79
|
+ defcallback read_req_body(payload, options :: Keyword.t) ::
|
80
|
+ {:ok, data :: binary, payload} |
|
81
|
+ {:more, data :: binary, payload} |
|
82
|
+ {:error, term}
|
81
83
|
|
82
84
|
@doc """
|
83
85
|
Parses a multipart request.
|
|
@@ -93,11 +95,11 @@ defmodule Plug.Conn.Adapter do
|
93
95
|
and contents should be written to the given `file`
|
94
96
|
* `:skip` - this multipart segment should be skipped
|
95
97
|
|
96
|
- This function can respond with one of the three following values:
|
98
|
+ This function may return a `:ok` or `:more` tuple. The first one is
|
99
|
+ returned when there is no more multipart data to be processed.
|
97
100
|
|
98
|
- * `{:ok, params, payload}` - the parameters are already processed as defined per `Conn.params`
|
99
|
- * `{:too_large, payload} - the request body goes over the given limit
|
101
|
+ For the supported options, please read `Plug.Conn.read_body/2` docs.
|
100
102
|
"""
|
101
|
- defcallback parse_req_multipart(payload, limit :: pos_integer, fun) ::
|
102
|
- {:ok, Conn.params, payload} | {:too_large, payload}
|
103
|
+ defcallback parse_req_multipart(payload, options :: Keyword.t, fun) ::
|
104
|
+ {:ok, Conn.params, payload} | {:more, Conn.params, payload}
|
103
105
|
end
|
changed
lib/plug/conn/unfetched.ex
|
@@ -5,14 +5,18 @@ defmodule Plug.Conn.Unfetched do
|
5
5
|
defstruct [:aspect]
|
6
6
|
|
7
7
|
defimpl Access do
|
8
|
- def get(%Plug.Conn.Unfetched{aspect: aspect}, key) do
|
8
|
+ def get(unfetched, key) do
|
9
|
+ raise_no_access(unfetched, key)
|
10
|
+ end
|
11
|
+
|
12
|
+ def get_and_update(unfetched, key, _value) do
|
13
|
+ raise_no_access(unfetched, key)
|
14
|
+ end
|
15
|
+
|
16
|
+ defp raise_no_access(%Plug.Conn.Unfetched{aspect: aspect}, key) do
|
9
17
|
raise ArgumentError, message:
|
10
18
|
"trying to access key #{inspect key} but they were not yet fetched. " <>
|
11
19
|
"Please call Plug.Conn.fetch_#{aspect} before accessing it"
|
12
20
|
end
|
13
|
-
|
14
|
- def access(unfetched, key) do
|
15
|
- get(unfetched, key)
|
16
|
- end
|
17
21
|
end
|
18
22
|
end
|
changed
lib/plug/parsers.ex
|
@@ -1,6 +1,6 @@
|
1
1
|
defmodule Plug.Parsers do
|
2
2
|
message = "the request is too large. If you are willing to process " <>
|
3
|
- "larger requests, please give a :limit to Plug.Parsers"
|
3
|
+ "larger requests, please give a :length to Plug.Parsers"
|
4
4
|
|
5
5
|
defmodule RequestTooLargeError do
|
6
6
|
@moduledoc """
|
|
@@ -10,9 +10,7 @@ defmodule Plug.Parsers do
|
10
10
|
defexception [:message]
|
11
11
|
|
12
12
|
defimpl Plug.Exception do
|
13
|
- def status(_exception) do
|
14
|
- 413
|
15
|
- end
|
13
|
+ def status(_exception), do: 413
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
|
@@ -24,9 +22,7 @@ defmodule Plug.Parsers do
|
24
22
|
defexception [:message]
|
25
23
|
|
26
24
|
defimpl Plug.Exception do
|
27
|
- def status(_exception) do
|
28
|
- 415
|
29
|
- end
|
25
|
+ def status(_exception), do: 415
|
30
26
|
end
|
31
27
|
end
|
32
28
|
|
|
@@ -39,8 +35,8 @@ defmodule Plug.Parsers do
|
39
35
|
These modules need to implement the behaviour
|
40
36
|
outlined in this module.
|
41
37
|
|
42
|
- * `:limit` - the request size limit we accept to parse.
|
43
|
- Defaults to 8,000,000 bytes.
|
38
|
+ All options supported by `Plug.Conn.read_body/2` are also
|
39
|
+ supported here.
|
44
40
|
|
45
41
|
## Examples
|
46
42
|
|
|
@@ -83,7 +79,7 @@ defmodule Plug.Parsers do
|
83
79
|
defcallback parse(Conn.t, type :: binary, subtype :: binary,
|
84
80
|
headers :: Keyword.t, opts :: Keyword.t) ::
|
85
81
|
{:ok, Conn.params, Conn.t} |
|
86
|
- {:too_large, Conn.t} |
|
82
|
+ {:error, :too_large, Conn.t} |
|
87
83
|
{:skip, Conn.t}
|
88
84
|
|
89
85
|
@behaviour Plug
|
|
@@ -92,7 +88,7 @@ defmodule Plug.Parsers do
|
92
88
|
parsers = Keyword.get(opts, :parsers) || raise_missing_parsers
|
93
89
|
opts
|
94
90
|
|> Keyword.put(:parsers, convert_parsers(parsers))
|
95
|
- |> Keyword.put_new(:limit, 8_000_000)
|
91
|
+ |> Keyword.put_new(:length, 8_000_000)
|
96
92
|
end
|
97
93
|
|
98
94
|
defp raise_missing_parsers do
|
|
@@ -129,7 +125,7 @@ defmodule Plug.Parsers do
|
129
125
|
%{conn | params: Map.merge(get, post)}
|
130
126
|
{:next, conn} ->
|
131
127
|
reduce(conn, t, type, subtype, headers, opts)
|
132
|
- {:too_large, _conn} ->
|
128
|
+ {:error, :too_large, _conn} ->
|
133
129
|
raise Plug.Parsers.RequestTooLargeError
|
134
130
|
end
|
135
131
|
end
|
changed
lib/plug/parsers/multipart.ex
|
@@ -4,13 +4,12 @@ defmodule Plug.Parsers.MULTIPART do
|
4
4
|
|
5
5
|
def parse(%Conn{} = conn, "multipart", subtype, _headers, opts) when subtype in ["form-data", "mixed"] do
|
6
6
|
{adapter, state} = conn.adapter
|
7
|
- limit = Keyword.fetch!(opts, :limit)
|
8
7
|
|
9
|
- case adapter.parse_req_multipart(state, limit, &handle_headers/1) do
|
8
|
+ case adapter.parse_req_multipart(state, opts, &handle_headers/1) do
|
10
9
|
{:ok, params, state} ->
|
11
10
|
{:ok, params, %{conn | adapter: {adapter, state}}}
|
12
|
- {:too_large, state} ->
|
13
|
- {:too_large, %{conn | adapter: {adapter, state}}}
|
11
|
+ {:more, _params, state} ->
|
12
|
+ {:error, :too_large, %{conn | adapter: {adapter, state}}}
|
14
13
|
end
|
15
14
|
end
|
changed
lib/plug/parsers/urlencoded.ex
|
@@ -3,30 +3,15 @@ defmodule Plug.Parsers.URLENCODED do
|
3
3
|
alias Plug.Conn
|
4
4
|
|
5
5
|
def parse(%Conn{} = conn, "application", "x-www-form-urlencoded", _headers, opts) do
|
6
|
- read_body(conn, Keyword.fetch!(opts, :limit))
|
6
|
+ case Conn.read_body(conn, opts) do
|
7
|
+ {:ok, body, conn} ->
|
8
|
+ {:ok, Plug.Conn.Query.decode(body), conn}
|
9
|
+ {:more, _data, conn} ->
|
10
|
+ {:error, :too_large, conn}
|
11
|
+ end
|
7
12
|
end
|
8
13
|
|
9
14
|
def parse(conn, _type, _subtype, _headers, _opts) do
|
10
15
|
{:next, conn}
|
11
16
|
end
|
12
|
-
|
13
|
- defp read_body(%Conn{adapter: {adapter, state}} = conn, limit) do
|
14
|
- case read_body({:ok, "", state}, "", limit, adapter) do
|
15
|
- {:too_large, state} ->
|
16
|
- {:too_large, %{conn | adapter: {adapter, state}}}
|
17
|
- {:ok, body, state} ->
|
18
|
- {:ok, Plug.Conn.Query.decode(body), %{conn | adapter: {adapter, state}}}
|
19
|
- end
|
20
|
- end
|
21
|
-
|
22
|
- defp read_body({:ok, buffer, state}, acc, limit, adapter) when limit >= 0,
|
23
|
- do: read_body(adapter.stream_req_body(state, 1_000_000),
|
24
|
- acc <> buffer, limit - byte_size(buffer), adapter)
|
25
|
- defp read_body({:ok, _, state}, _acc, _limit, _adapter),
|
26
|
- do: {:too_large, state}
|
27
|
-
|
28
|
- defp read_body({:done, state}, acc, limit, _adapter) when limit >= 0,
|
29
|
- do: {:ok, acc, state}
|
30
|
- defp read_body({:done, state}, _acc, _limit, _adapter),
|
31
|
- do: {:too_large, state}
|
32
17
|
end
|
changed
mix.exs
|
@@ -3,8 +3,8 @@ defmodule Plug.Mixfile do
|
3
3
|
|
4
4
|
def project do
|
5
5
|
[app: :plug,
|
6
|
- version: "0.4.4",
|
7
|
- elixir: "~> 0.13.3 or ~> 0.14.0-dev",
|
6
|
+ version: "0.5.0",
|
7
|
+ elixir: "~> 0.14.0",
|
8
8
|
deps: deps,
|
9
9
|
package: package,
|
10
10
|
description: "A specification and conveniences for composable " <>
|
|
@@ -19,7 +19,7 @@ defmodule Plug.Mixfile do
|
19
19
|
end
|
20
20
|
|
21
21
|
def deps do
|
22
|
- [{:cowboy, "~> 0.9", github: "extend/cowboy", optional: true},
|
22
|
+ [{:cowboy, "~> 0.10.0", github: "extend/cowboy", optional: true},
|
23
23
|
{:ex_doc, github: "elixir-lang/ex_doc", only: [:docs]},
|
24
24
|
{:hackney, github: "benoitc/hackney", only: [:test]}]
|
25
25
|
end
|