Search code examples
f#suave

Form data is not correct with Suave web server


I am trying to receive a confirmation from AWS' SNS system. It sends a message through a POST to a webserver and I'm receiving it using Suave.

When I get the message, the form field is truncated, I am receiving:

"{\n  \"Type\" : \"SubscriptionConfirmation\",\n  \"MessageId\" : \"13dd68fa-2720-419a-8d8a-edc9f4466ea3\",\n  \"Token\" : \"2336412f37fb687f5d51e6e2425e90ccf23f36200405c1942ea85f0874b382c47327c4bfd63203eace5240bb7b253428af362a04a61c8f98ab1718c679e9e27594529615adf5d86729374cce472768b91622851aced957c1dcddf21e3d82b48f5ff528c8dbc911179a3f26126a8d7f00\",\n  \"TopicArn\" : \"arn:aws:sns:ap-northeast-1:960544703730:igorbot\",\n  \"Message\" : \"You have chosen to subscribe to the topic arn:aws:sns:ap-northeast-1:960544703730:igorbot.\\nTo confirm the subscription, visit the SubscribeURL included in this message.\",\n  \"SubscribeURL\" : \"https://sns.ap-northeast-1.amazonaws.com/?Action",
    "ConfirmSubscription"

so it's an unfinished json...

but when I look at the rawForm field, I get the whole message:

{
  "Type" : "SubscriptionConfirmation",
  "MessageId" : "0645c009-f4fa-4fb7-9c94-127e98f5eb76",
  "Token" : "2336412f37fb687f5d51e6e2425e90ccf23f36200406641a9f63b753dad1d31c61da2ebeea8cdaeed2e3c04f701bd08e2d2c9cac65676979e43c1089a96779f9b57a0e0f072013333db51472ca43c1e6a0854cf3af6769d95c7911d74c9e2bec22db93de90e537d480070891ddaaa548",
  "TopicArn" : "arn:aws:sns:ap-northeast-1:960544703730:igorbot",
  "Message" : "You have chosen to subscribe to the topic arn:aws:sns:ap-northeast-1:960544703730:igorbot.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
  "SubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:ap-northeast-1:960544703730:igorbot&Token=2336412f37fb687f5d51e6e2425e90ccf23f36200406641a9f63b753dad1d31c61da2ebeea8cdaeed2e3c04f701bd08e2d2c9cac65676979e43c1089a96779f9b57a0e0f072013333db51472ca43c1e6a0854cf3af6769d95c7911d74c9e2bec22db93de90e537d480070891ddaaa548",
  "Timestamp" : "2021-03-13T20:36:21.918Z",
  "SignatureVersion" : "1",
  "Signature" : "FBnpuGtkZmzox+5ryo1/4k1hgwmoeuvcptQ2dOyyneShVHovmdemMqo9JFTzbBFelTN7FMojX/sIjFs2dZoQYeqEgsQW9WqCiEstDQu0toHn7KKxapzIoGfjfh6Rikfy8Liv88RRNLC2DLtxWW2JWr5Mmwkjtro/pm7vyJhfp5G4qcAB3gtBOtVm+XOAai6rY7obcMojkmMr4jDd9UqutV6imyYDCH+PvUCnc7aKg6p4EmZO33VlRibIPa5PiN1Sj/mmNhyoeR4pGu+0Jci+utvonXaYPgtlDuEyoVcgUQ6lki1xiclIRpDm4FvOL5tvUSq+Jdjz3prlNDNM8AuQpQ==",
  "SigningCertURL" : "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-010a507c1833636cd94bdb98bd93083a.pem"
}

At first I thought the output was truncated, but then I narrowed it down. Posting this line:

{
  "SubscribeURL" : "https://sns.ap-northeast-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:ap-northeast-1:960544703730:igorbot&Token=2336412f37fb687f5d51e6e2425e90ccf23f36200406641a9f63b753dad1d31c61da2ebeea8cdaeed2e3c04f701bd08e2d2c9cac65676979e43c1089a96779f9b57a0e0f072013333db51472ca43c1e6a0854cf3af6769d95c7911d74c9e2bec22db93de90e537d480070891ddaaa548"
}

will fail and get truncated at the first '=' sign. Then:

{
  "SubscribeURL" : "abc=3"
}

will cause Suave to fail. When sent as form data, it will not be converted to a string properly.

Is there any setting related to encoding, etc that would allow to prevent this? (since I can't ask AWS to escape all their '=' signs)


Solution

  • JSON and form data (application/x-www-form-urlencoded, to be specific) have different syntax. Form data looks like key1=value1&key2=value2, so Suave is breaking the string on & and = in order to parse it.

    You can parse JSON using mapJson instead. Something like this should work:

    open System.Runtime.Serialization
    open Suave
    open Suave.Json
    
    [<DataContract>]
    type Subscription =
        {
            [<field: DataMember(Name = "SubscribeURL")>]
            SubscribeUrl : string;
        }
    
    [<EntryPoint>]
    let main argv =
        let app = mapJson (fun sub -> sprintf "URL: %s" sub.SubscribeUrl)
        startWebServer defaultConfig app
        0