From 978a4251b3513f52955bf50d247a6ff2168032b7 Mon Sep 17 00:00:00 2001 From: suika <872957+suikabreaker@users.noreply.github.com> Date: Wed, 12 Jan 2022 16:10:43 +0800 Subject: [PATCH 1/3] feature: lf_line_break --- lib/resty/upload.lua | 39 ++++++++++++++++++++++++---- t/sanity.t | 61 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/lib/resty/upload.lua b/lib/resty/upload.lua index 7a6c805..6a02599 100644 --- a/lib/resty/upload.lua +++ b/lib/resty/upload.lua @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/t/sanity.t b/t/sanity.t index 9fac009..01f65e9 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -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] From 249d0dbc713ea05dadb22e61ebe4daa094cc2bb3 Mon Sep 17 00:00:00 2001 From: suika-kong Date: Fri, 8 Apr 2022 16:31:32 +0800 Subject: [PATCH 2/3] add documentation for it --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 0fb69b4..23bb88c 100644 --- a/README.markdown +++ b/README.markdown @@ -166,7 +166,7 @@ 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. @@ -174,6 +174,8 @@ local form, err = upload:new(self, chunk_size, max_line_size, preserve_body) 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 ====== From 1d3d781921afb7183d22df0a3bc0f645404b586b Mon Sep 17 00:00:00 2001 From: Suika <872957+suikabreaker@users.noreply.github.com> Date: Fri, 8 Apr 2022 16:38:39 +0800 Subject: [PATCH 3/3] missing empty line --- t/sanity.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/sanity.t b/t/sanity.t index 01f65e9..551f4c1 100644 --- a/t/sanity.t +++ b/t/sanity.t @@ -831,3 +831,4 @@ read: ["eof"] read: ["eof"] --- no_error_log [error] +