parley-messaging / ios-library

Parley iOS app library
MIT License
2 stars 2 forks source link

Issue with encoding HTTP body and content type #32

Closed igashev closed 2 years ago

igashev commented 2 years ago

Hi,

We recently started experiencing issues when contacting parley's backend service. First of all I want to point out that we are using latest iOS SDK 3.6.0 of Parley and that we haven't made any changes to our setup for 6+ months. From our investigation we ended up finding that the encoding of the HTTP body and/or the content type of the request might be the problem.

In all examples below I have modified/deleted all the sensitive data.

Request Header:
Method: POST,
  Connection: keep-alive
  Accept: */*
  Accept-Encoding: br; q=1.0
  Accept-Encoding: gzip; q=0.9
  Accept-Encoding: deflate; q=0.8
  Accept-Language: en-BG; q=1.0
  Accept-Language: bg-BG; q=0.9
  User-Agent: (iOS 15.1.0)
  User-Agent: Alamofire/5.4.0
  X-Original-URL: /clientApi/v1.6/devices
  Content-Length: 261
  Content-Type: application/x-www-form-urlencoded; charset=utf-8
}

Request Body:  
pushEnabled=1&pushToken=M1puTE8fgyWiAI2NQbbOiRoSB6Vvun4bLa2XT&pushType=6&type=2&userAdditionalInformation%5Bname%5D=Bob&version=3.1.3

Response Body: 
{"notifications":[{"type":"error","message":"invalid_pushType"}],"status":"ERROR","metadata":{"values":{"url":"devices","pushEnabled":"1","pushToken":"M1puTE8fgyWiAI2NQbbOiRoSB6Vvun4bLa2XT","pushType":"6","type":"2","userAdditionalInformation":{"name":"Bob"},"version":"3.1.3"},"method":"post","duration":0.006}}

Response Status:  BadRequest

Duration: 98 ms

In this request to register a device you can see that the service returns invalid_pushType but the push type is set to 6 which is a valid value. For some reason this endpoint suddenly stopped accepting this data.

We got informed that we might need to change the content type to application/json. We did that and then we received this.

Request Header:
Method: POST
{
  Connection: keep-alive
  Accept: */*
  Accept-Encoding: br; q=1.0
  Accept-Encoding: gzip; q=0.9
  Accept-Encoding: deflate; q=0.8
  Accept-Language: en-BG; q=1.0
  Accept-Language: bg-BG; q=0.9
  User-Agent: (iOS 15.1.0)
  User-Agent: Alamofire/5.4.0
  X-Original-URL: /clientApi/v1.6/devices
  Content-Length: 261
  Content-Type: application/json
}

Request Body:  
pushEnabled=1&pushToken=4P6WAxBBHVQkM1puTE8fgyWiAI2NQbbOiRoSB6Vvun4bLa2XT&pushType=6&type=2&userAdditionalInformation%5Bname%5D=Bob&version=3.1.3

Response Body: 
{"data":{},"notifications":[{"type":"success","message":"device_successfully_subscribed"}],"status":"SUCCESS","metadata":{"values":{"url":"devices"},"method":"post","duration":0.008}}

Response Status:  OK

Duration: 146 ms

You can clearly see that the content type is now application/json but the request body is still urlencoded. Interestingly, this request succeeded.

Then we tried another one which is to send a message.

Request Header:
Method: POST
{
  Connection: keep-alive
  Accept: */*
  Accept-Encoding: br; q=1.0
  Accept-Encoding: gzip; q=0.9
  Accept-Encoding: deflate; q=0.8
  Accept-Language: en-BG; q=1.0
  Accept-Language: bg-BG; q=0.9
  User-Agent: (iOS 15.1.0)
  User-Agent: Alamofire/5.4.0
  X-Original-URL: /clientApi/v1.6/messages
  Content-Length: 88
  Content-Type: application/json
}

Request Body:  
message=Hnbg&status=1&time=1660557400&typeId=1&uuid=600FE2C5-E046-462B-ABD4-2584CEAF577D

Response Body: 
{"notifications":[{"type":"error","message":"missing_message_or_image"}],"status":"ERROR","metadata":{"values":{"url":"messages"},"method":"post","duration":0.011}}

Response Status:  BadRequest

Duration: 41 ms

We again received an error that a message is missing but it is clearly there, it is just not json encoded but again urlencoded. Looks like to us that the backend service just fails to decode the data because it is not in a correct format.

We pinpointed the problem to this line of code. https://github.com/parley-messaging/ios-library/blob/10644447983836f1499db5f7fc1cd331af181a9e/Source/Data/Remotes/ParleyRemote.swift#L78

Alamofire's request function has a default parameter for encoding. https://github.com/Alamofire/Alamofire/blob/8dd85aee02e39dd280c75eef88ffdb86eed4b07b/Source/Session.swift#L284 This parameter's default value is URLEncoding.default. This means that the SDK always converts requests' bodies into url encoded if one does not pass for example JSONEncoding.default to the parameter.

alexkok commented 2 years ago

Thanks for the detailed report @igashev!

Yes, the body was being url-encoded by Alamfire, while the content-type headers making this inconsistent (and modifying the header wouldn't resolve it).

The backend probably couldn't convert the body back which was causing the error, although we couldn't reproduce the case where this causes the backend error. Does the backend error happen each time or 'sometimes'? -- Either way, the above PR will fix this inconsistency, which should fix this issue overall.

This change will be included in the next version, likely released later today.

alexkok commented 2 years ago

Fixed in version 3.6.1 🚀