Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,16 @@ Usage

```lua
local upload = require "resty.upload"
local form, err = upload:new(self, chunk_size, max_line_size, preserve_body)
local form, err = upload:new(self, chunk_size, max_line_size, preserve_body, lf_line_break)
```
`chunk_size` defaults to 4096. It is the size used to read data from the socket.

`max_line_size` defaults to 512. It is the size limit to read the chunked body header.

By Default, `lua-resty-upload` will consume the request body. For proxy mode this means upstream will not see the body. When `preserve_body` is set to true, the request body will be preserved. Note that this option is not free. When enabled, it will double the memory usage of `resty.upload`.

By Default, `lua-resty-upload` forbids using only LF(`'\n'`) as linebreak in multipart boundary. To be compatible with LF line break, set `lf_line_break` to true.

Author
======

Expand Down
39 changes: 34 additions & 5 deletions lib/resty/upload.lua
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,26 @@ local function get_boundary()
return match(header, ";%s*boundary=([^\",;]+)")
end

local function read_line_construct(sock, lf_line_break)
local line_end = "\r\n"
if lf_line_break then
line_end = "\n"
end
local read_line, err = sock:receiveuntil(line_end)
if lf_line_break and read_line then
return function (arg)
local ret, err_inner = read_line(arg)
if ret and ret:sub(-1,-1) == '\r' then
ret = ret:sub(1,-2)
end
return ret, err_inner
end
else
return read_line, err
end
end

function _M.new(self, chunk_size, max_line_size, preserve_body)
function _M.new(self, chunk_size, max_line_size, preserve_body, lf_line_break)
local boundary = get_boundary()

-- print("boundary: ", boundary)
Expand All @@ -121,7 +139,7 @@ function _M.new(self, chunk_size, max_line_size, preserve_body)
return nil, err
end

local read_line, err = sock:receiveuntil("\r\n")
local read_line, err = read_line_construct(sock, lf_line_break)
if not read_line then
return nil, err
end
Expand All @@ -134,7 +152,8 @@ function _M.new(self, chunk_size, max_line_size, preserve_body)
read_line = read_line,
boundary = boundary,
state = STATE_BEGIN,
preserve_body = preserve_body
preserve_body = preserve_body,
lf_line_break = lf_line_break
}, mt)
end

Expand Down Expand Up @@ -202,7 +221,17 @@ local function read_body_part(self)
if not chunk then
local sock = self.sock

local data = sock:receive(2)
local data, err = sock:receive(1)

-- partial lookahead: -- or CRLF?
if data == "-" or data == "\r" then
local next
next, err = sock:receive(1)
if next then
data = data .. next
end
end

if data == "--" then
local ok, err = discard_rest(self)
if not ok then
Expand All @@ -213,7 +242,7 @@ local function read_body_part(self)
return "part_end"
end

if data ~= "\r\n" then
if data ~= "\r\n" and not (data == "\n" and self.lf_line_break) then
local ok, err = discard_line(self)
if not ok then
return nil, nil, err
Expand Down
61 changes: 61 additions & 0 deletions t/sanity.t
Original file line number Diff line number Diff line change
Expand Up @@ -770,3 +770,64 @@ remain body length: 336

--- no_error_log
[error]



=== TEST 13: LF line break
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua '
local upload = require "resty.upload"
local ljson = require "ljson"

local form = upload:new(5, nil, nil, true)

form:set_timeout(1000) -- 1 sec

while true do
local typ, res, err = form:read()
if not typ then
ngx.say("failed to read: ", err)
return
end

ngx.say("read: ", ljson.encode({typ, res}))

if typ == "eof" then
break
end
end

local typ, res, err = form:read()
ngx.say("read: ", ljson.encode({typ, res}))
';
}
--- more_headers
Content-Type: multipart/form-data; boundary=---------------------------820127721219505131303151179
--- request eval
qq{POST /t\n-----------------------------820127721219505131303151179\r
Content-Disposition: form-data; name="file1"; filename="a.txt"\r
Content-Type: text/plain\r
\r
Hello, world\n-----------------------------820127721219505131303151179
Content-Disposition: form-data; name="test"\r
\r
value\r
\r\n-----------------------------820127721219505131303151179--\r
}
--- response_body
read: ["header",["Content-Disposition","form-data; name=\"file1\"; filename=\"a.txt\"","Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""]]
read: ["header",["Content-Type","text/plain","Content-Type: text/plain"]]
read: ["body","Hello"]
read: ["body",", wor"]
read: ["body","ld"]
read: ["part_end"]
read: ["header",["Content-Disposition","form-data; name=\"test\"","Content-Disposition: form-data; name=\"test\""]]
read: ["body","value"]
read: ["body","\r\n"]
read: ["part_end"]
read: ["eof"]
read: ["eof"]
--- no_error_log
[error]