postmanlabs / postman-app-support

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
https://www.postman.com
5.79k stars 839 forks source link

+ (Plus) signs in Get Requests no longer working. #2517

Closed tobeliketree closed 6 years ago

tobeliketree commented 7 years ago
  1. Postman Version: 4.9.0
  2. App (Chrome app or Mac app): Mac App
  3. OS details: MacOs Sierra (10.12.1)
  4. Is the Interceptor on and enabled in the app: no
  5. Did you encounter this recently, or has this bug always been there: Recently
  6. Expected behavior: Email addresses that have + sign are no longer being sent / encoded correctly reporting errors.

I have an authentication request that uses an email address with a plus sign that was working previously until my app updated to v 4.9.0. Now my get request fails on the email address.

The email address I'm using looks like email+alias@email.com and I am using a {{email}} variable to insert this in the url. I am doing the same as well as the password and API key.

/authenticate?api_key={{api_key}}&email={{email}}&password={{password}}

This is the code that is being sent to the server via postman which is now failing.

GET /psapi/v3/mem/authenticate?api_key=DuMMyApiKeY&email=email+alias@email.com&password=abc123 Host: www.photoshelter.com Cache-Control: no-cache

I have tried manually encoding the url but nothing allows the + to work. I have since switched my test email address to use a non + email address format so I can continue working. It would be nice if this bug can be fixed in the next release as it's essential to my testing and was working until I updated my mac APP just today.

mhertogs commented 7 years ago

Second this - I also started being unable to send URL encoded '+' signs in GET requests.

Postman Version: 4.9.1 App (Chrome app or Mac app): Mac App OS details: MacOs Sierra (10.11.6) Is the Interceptor on and enabled in the app: no Did you encounter this recently, or has this bug always been there: Recently, after upgrade Expected behavior: Query params in GET request encoded as %2B are not modified incorrectly by Postman.

Example: GET /users/{{user}}/preference?phoneInQuestion=%2B19991114444

If I generate code to a curl request for instance, the url that is generated looks like: GET /users/{{user}}/preference?phoneInQuestion=%252B19991114444

It seems that Postman is url encoding the '%' and leaving everything else which is incorrect.

sdnts commented 7 years ago

I think this might be related to: https://github.com/postmanlabs/postman-app-support/issues/2538 We are looking into this.

rickmills commented 7 years ago

How do we downgrade for now @madebysid ? Really cant use postman at all with such a showstopper issue.

sdnts commented 7 years ago

@rickmills Could you drop a mail at help@getpostman.com?

kunagpal commented 7 years ago

Possible duplicate of #2468, related to #2510, #2508

thix1606 commented 7 years ago

Same problem here. Our system generates authentication keys that users must send in each request. In several API that key is sent as a GET parameter. After updating to 4.9.x the problem began. When the key has a plus sign (+) the requests do not work. Is there a solution or workaround for that yet?

robertkuhar commented 7 years ago

Same problem here. How do I downgrade or when can this get fixed? Its kind-of a big deal here as email addressed "tagged" with '+' (user.name+tag@example.com) are really popular on our service. There's not a test case for URL encoding in Postman?

tobeliketree commented 7 years ago

I'm the original poster and would love an update. This has been really disruptive to my work and I see that I'm not alone here. The status is....?

SamvelRaja commented 7 years ago

@tobeliketree @robertkuhar @mhertogs @rickmills The issue has been fixed in our latest canary(4.9.4-canary.1) release, can you guys switch to that version an check the same? On additional note we are using the whatwg's url parser algorithm.

SamvelRaja commented 7 years ago

@tobeliketree @robertkuhar @mhertogs @rickmills Just a nudge to check whether you guys had chance to test it out in canary build. OSX: https://dl.pstmn.io/download/channel/canary/osx_64 Windows 32-bit: https://dl.pstmn.io/download/channel/canary/windows_32 Windows 64-bit: https://dl.pstmn.io/download/channel/canary/windows_64 Linux 32-bit: https://dl.pstmn.io/download/channel/canary/linux_32 Linux 64-bit: https://dl.pstmn.io/download/channel/canary/linux_64

rickmills commented 7 years ago

Hi @SamvelRaja

Tested it out this morning and the Canary release does appear to fix the issue on macOS :)

Thanks for that 👍

robertkuhar commented 7 years ago

@SamvelRaja I confirmed that the canary does indeed allow me to manually encode the '+' in the GET URLs and generates an HTTP request that works when I click "Send". That is to say setting a query param like email=robert.kuhar%2Bprime@climate.com on the Postman URL (ex. http://example.com/user/details?email=robert.kuhar%2Bprime@climate.com) works like a champ.

It still strikes me as odd that the "cURL" from both the failed Postman URL (http://example.com/user/details?email=robert.kuhar+plus@climate.com) and the now respected "pre-http-encoded" Postman URL (http://example.com/user/details?email=robert.kuhar%2Bplus@climate.com) generate the exact same cURL and that cURL is correct.

In other words, a Postman URL of http://example.com/user/details?email=robert.kuhar+plus@climate.com will fail on Send but the cURL generated from it will succeed

curl -X GET -H "Accept: application/json" -H "x-http-request-id: GET_user_by_email" -H "x-http-caller-id: tcc-rkuhar.local" -H "Cache-Control: no-cache" -H "Postman-Token: eaaa3c49-2da9-7d89-ee5a-f92391399a23" "http://example.com/user/details?email=robert.kuhar%2Bplus@climate.com"

Maybe I shouldn't "look a gift horse in the mouth". Postman can now do common email aliases as query parameters and that alone is progress. Wahoo!

I tested this on OSX

SamvelRaja commented 7 years ago

@robertkuhar Thanks for testing and providing the information, the mismatch between the cURL and the postmanURL is intentional, I will look further into this and will update the thread accordingly.

mhertogs commented 7 years ago

@SamvelRaja The canary link you gave does solve my encoding problem when using Postman directly (I am using OSX). However, when I generate cURL requests, it seems you are URL encoding in the actual URL path as well which is messing up our REST resource paths.

Example: /v1/phones/+19998887777 becomes /v1/phones/%2B19998887777

According to RFC 1738: "Thus, only alphanumerics, the special characters "$-_.+!*'(),", and reserved characters used for their reserved purposes may be used unencoded within a URL."

I still think this fix is good enough to unblock a lot of people so I would still recommend moving forward with it, but there should probably some future consideration about how to treat special characters in a URL path without just encoding them all because there are valid URLs that leave these unencoded.

SamvelRaja commented 7 years ago

@mhertogs Thanks for your valuable feedback on the canary app. Right now i came to the fact that + needs to be encoded in the path of the URL in cURL code generator as %2B and it needs to be either encoded as %20 or left as it is as + in the query params. I will keep the issue open for further discussions and ideas from the users and community.

stianlagstad commented 7 years ago

I'm having some problems parsing dates in the ISO8601 format. My request looks like this: {{url}}?departure=2017-03-01T12:05:00+01:00 When I fire this off in the postman app, debugging my app shows the incoming string as "2017-03-01T12:05:00 01:00". This is not what I want - I want to keep the + sign.

But if I generate a cURL request I get this: curl -X GET "http://myUrl?departure=2017-03-01T12:05:00%2B01:00" and that works as expected. Debugging shows the incoming string as "2017-03-01T12:05:00+01:00".

Trying to change my request to look like this instead: {{url}}?departure=2017-03-01T12:05:00%2B01:00 didn't help. I get the same resulting string in my application: "2017-03-01T12:05:00 01:00".

SamvelRaja commented 7 years ago

@stianlagstad Can you provide the version of the app you face this? I am testing in the current 4.10.2 version of the Mac app and it sends the %2B as expected.

screen shot 2017-02-28 at 3 46 33 pm
stianlagstad commented 7 years ago

I'm getting the same as you for the url, but look at args.departure above. That contains a space, which is not what I want. I want args.departure to be "2017-03-01T12:05:00+01:00", not "2017-03-01T12:05:00 01:00".

Again: The generated cURL request works: curl -X GET "http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00"

Also:

You are up to date! Postman v4.10.2 is the latest version.

SamvelRaja commented 7 years ago

@stianlagstad My bad, Attached a wrong screen shot, yes i am getting the '+' in the args.departure, if i send the departure=2017-03-01T12:05:00%2B01:00 as query param.

screen shot 2017-02-28 at 4 43 02 pm

stianlagstad commented 7 years ago

Then there's a difference between the behaviour in your app and in mine. This is what I see: screenshot from 2017-02-28 12-21-09

Can I fix this somehow?

I'm using Postman v4.10.2 on Ubuntu 16.04.

SamvelRaja commented 7 years ago

@stianlagstad This seems really odd :( I would like you to open the app in debug mode to find the underlying request sent.

for that close the postman app if already opened, then you need to run the following command in your terminal

NODE_DEBUG=request /PATH_TO_THE_APP/Postman.app/Contents/MacOS/Postman

where PATH_TO_THE_APP would be either to your /Downloads/ folder or /Applications/folder.

This will open the postman app in debug mode. Once it is opened, send the same request. You can find the request data sent to your server something like below. screen shot 2017-03-01 at 2 04 22 pm

Kindly provide the screen shot of that to help us to debug much further.

stianlagstad commented 7 years ago

Like this?

➜  Postman NODE_DEBUG=request ./Postman
REQUEST { headers:
   { 'User-Agent': 'PostmanRuntime/3.0.11-hotfix.2',
     Accept: '*/*',
     Host: 'echo.getpostman.com' },
  url: 'http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00',
  method: 'GET',
  jar:
   RequestJar {
     _jar:
      CookieJar {
        rejectPublicSuffixes: false,
        enableLooseMode: true,
        store:
         { .. } } },
  gzip: true,
  useQuerystring: true,
  strictSSL: true,
  followRedirect: true,
  followAllRedirects: true,
  encoding: null,
  agentOptions: { keepAlive: true },
  request:
   PostmanRequest {
     description: PostmanPropertyDescription { content: undefined, type: 'text/plain' },
     url:
      PostmanUrl {
        auth: undefined,
        protocol: undefined,
        port: undefined,
        path: [Object],
        hash: undefined,
        host: [Object],
        query: [Object],
        variables: [Object] },
     method: 'GET',
     headers:
      PostmanPropertyList {
        members: [],
        reference: {},
        Type: [Object],
        _postman_listIndexKey: 'key',
        _postman_listIndexCaseInsensitive: true },
     body: PostmanRequestBody {},
     auth: PostmanRequestAuth {},
     _: { _postman_proxy: undefined } },
  certificateManager:
   callIntoRenderer {
     length: 0,
     models: [],
     _byId: {},
     getCertificateContents: [Function: callIntoRenderer],
     model: [Function: callIntoRenderer],
     initialize: [Function: callIntoRenderer],
     loadCertificates: [Function: callIntoRenderer],
     saveCertificates: [Function: callIntoRenderer],
     findCertificateByDomain: [Function: callIntoRenderer],
     addCertificate: [Function: callIntoRenderer],
     updateCertificate: [Function: callIntoRenderer],
     removeCertificate: [Function: callIntoRenderer],
     constructor: [Function: callIntoRenderer],
     on: [Function: callIntoRenderer],
     listenTo: [Function: callIntoRenderer],
     off: [Function: callIntoRenderer],
     stopListening: [Function: callIntoRenderer],
     once: [Function: callIntoRenderer],
     listenToOnce: [Function: callIntoRenderer],
     trigger: [Function: callIntoRenderer],
     bind: [Function: callIntoRenderer],
     unbind: [Function: callIntoRenderer],
     toJSON: [Function: callIntoRenderer],
     sync: [Function: callIntoRenderer],
     add: [Function: callIntoRenderer],
     remove: [Function: callIntoRenderer],
     set: [Function: callIntoRenderer],
     reset: [Function: callIntoRenderer],
     push: [Function: callIntoRenderer],
     pop: [Function: callIntoRenderer],
     unshift: [Function: callIntoRenderer],
     shift: [Function: callIntoRenderer],
     slice: [Function: callIntoRenderer],
     get: [Function: callIntoRenderer],
     has: [Function: callIntoRenderer],
     at: [Function: callIntoRenderer],
     where: [Function: callIntoRenderer],
     findWhere: [Function: callIntoRenderer],
     sort: [Function: callIntoRenderer],
     pluck: [Function: callIntoRenderer],
     fetch: [Function: callIntoRenderer],
     create: [Function: callIntoRenderer],
     parse: [Function: callIntoRenderer],
     clone: [Function: callIntoRenderer],
     modelId: [Function: callIntoRenderer],
     _reset: [Function: callIntoRenderer],
     _prepareModel: [Function: callIntoRenderer],
     _removeModels: [Function: callIntoRenderer],
     _isModel: [Function: callIntoRenderer],
     _addReference: [Function: callIntoRenderer],
     _removeReference: [Function: callIntoRenderer],
     _onModelEvent: [Function: callIntoRenderer],
     forEach: [Function: callIntoRenderer],
     each: [Function: callIntoRenderer],
     map: [Function: callIntoRenderer],
     collect: [Function: callIntoRenderer],
     reduce: [Function: callIntoRenderer],
     foldl: [Function: callIntoRenderer],
     inject: [Function: callIntoRenderer],
     reduceRight: [Function: callIntoRenderer],
     foldr: [Function: callIntoRenderer],
     find: [Function: callIntoRenderer],
     detect: [Function: callIntoRenderer],
     filter: [Function: callIntoRenderer],
     select: [Function: callIntoRenderer],
     reject: [Function: callIntoRenderer],
     every: [Function: callIntoRenderer],
     all: [Function: callIntoRenderer],
     some: [Function: callIntoRenderer],
     any: [Function: callIntoRenderer],
     include: [Function: callIntoRenderer],
     includes: [Function: callIntoRenderer],
     contains: [Function: callIntoRenderer],
     invoke: [Function: callIntoRenderer],
     max: [Function: callIntoRenderer],
     min: [Function: callIntoRenderer],
     toArray: [Function: callIntoRenderer],
     size: [Function: callIntoRenderer],
     first: [Function: callIntoRenderer],
     head: [Function: callIntoRenderer],
     take: [Function: callIntoRenderer],
     initial: [Function: callIntoRenderer],
     rest: [Function: callIntoRenderer],
     tail: [Function: callIntoRenderer],
     drop: [Function: callIntoRenderer],
     last: [Function: callIntoRenderer],
     without: [Function: callIntoRenderer],
     difference: [Function: callIntoRenderer],
     indexOf: [Function: callIntoRenderer],
     shuffle: [Function: callIntoRenderer],
     lastIndexOf: [Function: callIntoRenderer],
     isEmpty: [Function: callIntoRenderer],
     chain: [Function: callIntoRenderer],
     sample: [Function: callIntoRenderer],
     partition: [Function: callIntoRenderer],
     groupBy: [Function: callIntoRenderer],
     countBy: [Function: callIntoRenderer],
     sortBy: [Function: callIntoRenderer],
     indexBy: [Function: callIntoRenderer],
     findIndex: [Function: callIntoRenderer],
     findLastIndex: [Function: callIntoRenderer] },
  proxyManager: { getProxyConfiguration: [Function: callIntoRenderer] },
  callback: [Function] }
REQUEST make request http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00
REQUEST onRequestResponse http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00 200 { server: 'nginx/1.10.1',
  date: 'Wed, 01 Mar 2017 09:05:31 GMT',
  'content-type': 'application/json; charset=utf-8',
  'transfer-encoding': 'chunked',
  'access-control-allow-origin': '',
  'access-control-allow-credentials': '',
  'access-control-allow-methods': '',
  'access-control-allow-headers': '',
  'access-control-expose-headers': '',
  etag: 'W/"1af-k/bY8OyRv+bjQuNBJouEVA"',
  vary: 'Accept-Encoding',
  connection: 'Keep-Alive',
  'content-encoding': 'gzip',
  age: '0' }
REQUEST reading response's body
REQUEST finish init function http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00
REQUEST response end http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00 200 { server: 'nginx/1.10.1',
  date: 'Wed, 01 Mar 2017 09:05:31 GMT',
  'content-type': 'application/json; charset=utf-8',
  'transfer-encoding': 'chunked',
  'access-control-allow-origin': '',
  'access-control-allow-credentials': '',
  'access-control-allow-methods': '',
  'access-control-allow-headers': '',
  'access-control-expose-headers': '',
  etag: 'W/"1af-k/bY8OyRv+bjQuNBJouEVA"',
  vary: 'Accept-Encoding',
  connection: 'Keep-Alive',
  'content-encoding': 'gzip',
  age: '0' }
REQUEST end event http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00
REQUEST has body http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00 431
REQUEST emitting complete http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00

I removed what was in jar.RequestJar._jar.CookieJar.store, because that looked like my own unrelated data.

What I see in postman is the same as before: screenshot from 2017-03-01 10-12-39

stianlagstad commented 7 years ago

It must have to do with how the Postman app sends the request, because if I do the cURL-call it works as expected:

➜  Postman curl -X GET "http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00"
{"args":{"departure":"2017-03-01T12:05:00+01:00"},"headers":{"host":"echo.getpostman.com","user-agent":"curl/7.47.0","accept":"*/*","cache-control":"max-stale=0","x-bluecoat-via":"cc52d5680b6e1cd0"},"url":"http://echo.getpostman.com/get?departure=2017-03-01T12:05:00%2B01:00"}%

The response there is what I want.

SamvelRaja commented 7 years ago

@stianlagstad The log you shared ensures that postman actually sending the %2B as it is to the server 😢 I am still wondering why it is showing differently for you in the response UI. can you make sure that app sends the request in the format as you expected even to your server as well ? through your app server logs. And cURL, yeah app should behave in the same way.

stianlagstad commented 7 years ago

I'm not sure if that is right. There's clearly a difference between what reaches my server when I send the request from within the postman app (there's a space in the date), versus if I generate cURL and send the request through the command line (%2B instead of a space in the date). I'd stretch it as far as to wonder whether the logs that you asked me to check are correct? If the same error is occuring there, then the logs might look normal.

I'm not sure how to investigate this further :(

deinlandel commented 6 years ago

This IS a bug and it is still present in most recent version of Postman (there is no way to view version info from Postman interface, so sorry for not providing exact version).

Get parameter expired with value 2018-04-15 22:29:59 +0300 results in expired=2018-04-15 22:29:59 0300. Because plus sign isn't escaped and is parsed as a space by server.

udidol commented 6 years ago

I second this, we also have problems with it

SamvelRaja commented 6 years ago

We are following a master thread #4555 to track all issues related to Url encoding. Please follow that issue for updates.

RetroAsmDev commented 4 years ago

The "+" symbol should is still not encoded in URL queries as "%2B" in Postman ver. Version 7.11.0 on win32 6.1.7601 / x64

jiurman commented 1 year ago

2023, not encoded.

Investo-cat commented 9 months ago

2023 Oct, still happening

pktparticle commented 4 months ago

22 March 2024, still not working!