rexyai / RestRserve

R web API framework for building high-performance microservices and app backends
https://restrserve.org
276 stars 32 forks source link

JMeter failing for multipart/form body #137

Closed rplati closed 4 years ago

rplati commented 4 years ago

Hi, first of all thanks for your work on this package! I got it working and it looks very promising.

I was unable to test my API with JMeter due to tests failing when the API accepts multipart/form data.

I suspect this might be because the boundary that JMeter uses that does not start with double dash signs (--). It seems parse_multipart_body cannot handle that. When I submit the same call with Postman (boundary starts with --), everything works.

Partial JMeter log:

2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "POST /processData HTTP/1.1[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Connection: keep-alive[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Length: 708[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Type: multipart/form-data; boundary=ZI6zx6lhyfOL4qenewl6bZazXipXjA6nR31oGZ6[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Host: 127.0.0.1:5015[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "User-Agent: Apache-HttpClient/4.5.10 (Java/1.8.0_201)[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "--ZI6zx6lhyfOL4qenewl6bZazXipXjA6nR31oGZ6[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Disposition: form-data; name="rules"; filename="rules.txt"[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Type: text/plain[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Transfer-Encoding: binary[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "descriptionCleaned = str_trim(description),[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "volume = ifelse(str_trim(description) == 'prism', round(baseArea * height, 2), ifelse(str_trim(description) == 'pyramid', round(baseArea * height / 3, 2), NA))"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "--ZI6zx6lhyfOL4qenewl6bZazXipXjA6nR31oGZ6[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Disposition: form-data; name="dta"; filename="geo.csv"[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Type: text/csv[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "Content-Transfer-Encoding: binary[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> ""description","baseArea","height"[\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> ""  X",93.3,12.21[\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> ""pyramid",32.9,7.38[\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> ""  prism ",51.1,26.98[\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> ""pyramid",45.7,9.15[\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.h.wire: http-outgoing-44 >> "--ZI6zx6lhyfOL4qenewl6bZazXipXjA6nR31oGZ6--[\r][\n]"
2020-04-06 09:27:03,663 DEBUG o.a.j.p.h.s.HTTPHC4Impl: Sent 945 bytes
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "HTTP/1.1 500 Evaluation error[\r][\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "Connection: close[\r][\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "Content-type: text/plain[\r][\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "[\r][\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "Error in parse_multipart_body(body, paste0("--", boundary)) : [\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.wire: http-outgoing-44 << "  Boundary string not found.[\n]"
2020-04-06 09:27:03,669 DEBUG o.a.h.headers: http-outgoing-44 << HTTP/1.1 500 Evaluation error
2020-04-06 09:27:03,669 DEBUG o.a.h.headers: http-outgoing-44 << Connection: close
2020-04-06 09:27:03,669 DEBUG o.a.h.headers: http-outgoing-44 << Content-type: text/plain
artemklevtsov commented 4 years ago

Seems Rserve convert the boundary string to lower case when write the body content-type atrribute. Example:

curl -v \
     -X POST \
     -H "Content-Type: multipart/form-data; boundary=AAAAA" \
     -d $'--AAAAA\r\nContent-Disposition: form-data; name="example"\r\n\r\ntest\r\n--AAAAA--\r\n' \
     http://127.0.0.1:8001

From the Rserve:

HEADERS:
Request-Method: POST
Host: 127.0.0.1:8001
User-Agent: curl/7.69.1
Accept: */*
Content-Type: multipart/form-data; boundary=AAAAA
Content-Length: 76
QUERY:
 NULL
BODY:
 raw [1:76] 2d 2d 41 41 ...
 - attr(*, "content-type")= chr "multipart/form-data; boundary=aaaaa"

JMeter use uppercase symbols in the boundary. parse_multipart_body tries to exact match it.

But Content-Type header is ok. So we should use it.

artemklevtsov commented 4 years ago

With inst/examples/echo/app.R from the #139:

curl -v -H "Content-Type: multipart/form-data; boundary=AAAAA" -d $'--AAAAA\r\nContent-Disposition: form-data; name="example"\r\n\r\ntest\r\n--AAAAA--\r\n' http://127.0.0.1:8080/echo
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /echo HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.69.1
> Accept: */*
> Content-Type: multipart/form-data; boundary=AAAAA
> Content-Length: 76
>
* upload completely sent off: 76 out of 76 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-type: application/json
< Server: RestRserve/0.2.1
< Content-length: 322
<
* Connection #0 to host 127.0.0.1 left intact
{"path":"/echo","headers":{"content-length":"76","host":"127.0.0.1:8080","user-agent":"curl/7.69.1","accept":"*/*","content-type":"multipart/form-data; boundary=AAAAA"},"parameters":{"query":{},"body":{"example":"test"},"path":[]},"body":{"attributes":{"content-type":"multipart/form-data; boundary=aaaaa"},"rtpye":"raw"}}
artemklevtsov commented 4 years ago

@rplati, can you test again with dev branch? To install use the following command:

remotes::install_github("rexyai/RestRserve@dev")
rplati commented 4 years ago

@artemklevtsov, I can confirm that the issue has been resolved, thank you!

dselivanov commented 4 years ago

@rplati on CRAN now