SuaveIO / suave

Suave is a simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition.
https://suave.io
Other
1.32k stars 198 forks source link

not decoding properly strings with an '=' sign in the form field, with a POST. Prevents use of Suave. #751

Closed thomasd3 closed 3 years ago

thomasd3 commented 3 years ago

If you create a POST message and put this string in the form field:

{
  "SubscribeURL" : "abc=3"
}

Suave's form field will contain a truncated version, while the rawForm will have the full version.

for more details, see: https://stackoverflow.com/questions/66618622/form-data-is-not-correct-with-suave-web-server

ademar commented 3 years ago

Hi, If your intent is to post JSON data you should use the bytes in rawForm.

thomasd3 commented 3 years ago

I'm using AWS' SNS system, so AWS will send a JSON message first, then after that it is whatever gets posted by the client on the other side, which is not necessarily JSON, but there will be symbols like & and = in the form field.

So is Suave always trying to decode what is in the form? in that case, we can't use that field for 'free/random' strings but need to get the bytes from the raw field, correct?

ademar commented 3 years ago

I do not follow exactly the issue and suspect there is some misunderstanding.

Either a JSON payload is posted to the server and you will get it via rawForm ; or a x-www-form-urlencoded encoded payload which you will get via the form dictionary.

If you place JSON data inside a form field it still should work because the browser will url encode the JSON data. If you are posting programmatically you SHOULD url encode the fields data otherwise it wouldn't be valid http.

Perhaps sharing a sample of your code will help clarify the issue.

thomasd3 commented 3 years ago

The pipeline is the following: there are a few clients posting data to AWS' SNS system (it's just a fast queue) and AWS will forward that data as a POST command wherever we tell it. The source data is a few strings with numerical values next to them (like x 30; y 17) with occasionally some text messages as well so it's not JSON. But some of the text strings will have '=' in them for example.

The issue is that AWS will also send JSON formatted messages to confirm subscription / un-subscription and the only way to know that the upcoming message is from AWS, vs. a client, is because AWS will set a header value indicating the origin of the payload.

So the data is a mix of Json / non Json data based on the presence of a value in the headers.

For this reason I can't say that the form data will decode as a Json, or use the custom decoder because it depends on the header value. What surprised me is that I expected that the form field would contain 'whatever' was sent, but it looks like it is processed.

ademar commented 3 years ago

it depends on the header value.

You can look at the headers and decide how to proceed.

let app : WebPart =
  context(fun ctx ->
    match ctx.request.header "content-encoding" with
    | Choice1Of2 "application/json" ->
      // do something with ctx.request.rawForm
    | Choice1Of2 "application/x-www-form-urlencoded" ->
      // use the request.form dictionary
    | _ ->
      failwith "Unsupported content enconding"
  )

Also to note that Suave only parses on demand if you access the form dictionary. This could be improved.

thomasd3 commented 3 years ago

Thanks, this is clear now. I think the issue stems from the fact that I didn't realize that the form field was a dictionary at the beginning and I stayed stuck on that issue. It's my first time using Suave, so there is a lot to ingest at once :D