Skip to content

Commit

Permalink
fix(pkg/driver): unicode character escaping in url for .request command
Browse files Browse the repository at this point in the history
Use `URL constructor` inside `cy.request` to make sure the url is well
encoded when for both domain and pathname.

Fixes cypress-io#5274
  • Loading branch information
avallete committed Dec 5, 2019
1 parent d5c3b0b commit 5eca3e9
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 88 deletions.
14 changes: 9 additions & 5 deletions packages/driver/src/cy/commands/request.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,15 @@ module.exports = (Commands, Cypress, cy, state, config) ->
## Make sure the url unicode characters are properly escaped
## https://github.com/cypress-io/cypress/issues/5274
try
options.url = decodeURI(options.url)
catch URIError
# The url is not already encoded just silence the error
finally
options.url = encodeURI(options.url)
options.url = new URL(options.url).href
catch TypeError
# The URL object cannot be constructed because of URL failure
$utils.throwErrByPath("request.url_invalid", {
args: {
configFile: Cypress.config("configFile")
}
})


## if options.url isnt FQDN then we need to throw here
## if we made a request prior to a visit then it needs
Expand Down
201 changes: 118 additions & 83 deletions packages/driver/test/cypress/integration/issues/5274_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,121 @@ Promise = Cypress.Promise
RESPONSE_TIMEOUT = 22222

describe "issue 5274", ->
beforeEach ->
cy.stub(Cypress, "backend").callThrough()
Cypress.config("responseTimeout", RESPONSE_TIMEOUT)
Cypress.config("defaultCommandTimeout", RESPONSE_TIMEOUT)

@logs = []

cy.on "log:added", (attrs, log) =>
if attrs.name is "request"
@lastLog = log
@logs.push(log)

return null

it "should request url with ’ character in pathname", (done) ->
cy.request("http://localhost:1234/’")

cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

it "should request url with ’ escaped in pathname", (done) ->
cy.request(encodeURI('http://localhost:1234/’'))

cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

it "should visit url with Unicode in pathname from BMP to Astral Plane", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"

cy.request('http://localhost:1234/%')
cy.request('http://localhost:1234/’')
cy.request('http://localhost:1234/£')
cy.request('http://localhost:1234/Ȥ')
cy.request('http://localhost:1234/˵')
cy.request('http://localhost:1234/֍')
cy.request('http://localhost:1234/ץ')
cy.request('http://localhost:1234/ص')
cy.request('http://localhost:1234/ޥ')
cy.request('http://localhost:1234/ࠊ')
cy.request('http://localhost:1234/ࢨ')
cy.request('http://localhost:1234/⍸')
cy.request('http://localhost:1234/㇇')
cy.request('http://localhost:1234/ヸ')
cy.request('http://localhost:1234/ㇻ')
cy.request('http://localhost:1234/𓌶')
cy.request('http://localhost:1234/🜈')
cy.request('http://localhost:1234/🠋')
cy.request('http://localhost:1234/👩')
cy.request('http://localhost:1234/😀')
done()

it "should request url with any Unicode escaped character in pathname", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"

cy.request(encodeURI('http://localhost:1234/%'))
cy.request(encodeURI('http://localhost:1234/’'))
cy.request(encodeURI('http://localhost:1234/£'))
cy.request(encodeURI('http://localhost:1234/Ȥ'))
cy.request(encodeURI('http://localhost:1234/˵'))
cy.request(encodeURI('http://localhost:1234/֍'))
cy.request(encodeURI('http://localhost:1234/ץ'))
cy.request(encodeURI('http://localhost:1234/ص'))
cy.request(encodeURI('http://localhost:1234/ޥ'))
cy.request(encodeURI('http://localhost:1234/ࠊ'))
cy.request(encodeURI('http://localhost:1234/ࢨ'))
cy.request(encodeURI('http://localhost:1234/⍸'))
cy.request(encodeURI('http://localhost:1234/㇇'))
cy.request(encodeURI('http://localhost:1234/ヸ'))
cy.request(encodeURI('http://localhost:1234/ㇻ'))
cy.request(encodeURI('http://localhost:1234/𓌶'))
cy.request(encodeURI('http://localhost:1234/🜈'))
cy.request(encodeURI('http://localhost:1234/🠋'))
cy.request(encodeURI('http://localhost:1234/👩'))
cy.request(encodeURI('http://localhost:1234/😀'))
done()
describe "UNESCAPED_CHARACTERS error", ->
beforeEach ->
cy.stub(Cypress, "backend").callThrough()
Cypress.config("responseTimeout", RESPONSE_TIMEOUT)
Cypress.config("defaultCommandTimeout", RESPONSE_TIMEOUT)

return null

it "should request url with ’ character in pathname without UNESCAPED_CHARACTERS error", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

cy.request("http://localhost:1234/’")

it "should request url with ’ escaped in pathname without UNESCAPED_CHARACTERS error", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

cy.request(encodeURI('http://localhost:1234/’'))

it "should visit url with Unicode in pathname from BMP to Astral Plane without UNESCAPED_CHARACTERS error", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

cy.request('http://localhost:1234/😀')

it "should request url with any Unicode escaped character in pathname without UNESCAPED_CHARACTERS error", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() failed trying to load:"
expect(err.message).to.not.contain "ERR_UNESCAPED_CHARACTERS"
done()

cy.request(encodeURI('http://localhost:1234/😀'))

describe "Invalid URL error", ->
beforeEach ->
cy.stub(Cypress, "backend").callThrough()
Cypress.config("responseTimeout", RESPONSE_TIMEOUT)
Cypress.config("defaultCommandTimeout", RESPONSE_TIMEOUT)

return null

it "should throw an error when invalid url is provided", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.contain "cy.request() must be provided a fully qualified url"
done()

Cypress.config({ baseUrl: false })
cy.request('invalid://url.foo/bar')

describe "encoded url value", ->
beforeEach ->
cy.stub(Cypress, "backend").callThrough()
Cypress.config("responseTimeout", RESPONSE_TIMEOUT)
Cypress.config("defaultCommandTimeout", RESPONSE_TIMEOUT)
backend = Cypress.backend
.withArgs("http:request")
.resolves({ isOkStatusCode: true, status: 200 })

@expectOptionsToBe = (opts) ->
_.defaults(opts, {
failOnStatusCode: true
retryOnNetworkFailure: true
retryOnStatusCodeFailure: false
gzip: true
followRedirect: true
timeout: RESPONSE_TIMEOUT
method: "GET"
})

options = backend.firstCall.args[1]

_.each options, (value, key) ->
expect(options[key]).to.deep.eq(opts[key], "failed on property: (#{key})")
_.each opts, (value, key) ->
expect(opts[key]).to.deep.eq(options[key], "failed on property: (#{key})")

it "should request url with ’ character in pathname", ->
cy.request({ url: 'http://localhost:1234/’' }).then ->
@expectOptionsToBe({
url: "http://localhost:1234/%E2%80%99"
})

it "should request url with ’ escaped in pathname", ->
cy.request({ url: encodeURI('http://localhost:1234/’') }).then ->
@expectOptionsToBe({
url: "http://localhost:1234/%E2%80%99"
})

it "should visit url with Unicode in pathname from BMP to Astral Plane", ->
cy.request({ url: 'http://localhost:1234/😀' }).then ->
@expectOptionsToBe({
url: "http://localhost:1234/%F0%9F%98%80"
})

it "should request url with any Unicode escaped character in pathname", ->
cy.request({ url: encodeURI('http://localhost:1234/😀') }).then ->
@expectOptionsToBe({
url: "http://localhost:1234/%F0%9F%98%80"
})

it "should percent escape unicode in path and convert domain name properly", ->
cy.request({ url: 'http://localhost😀:1234/😀' }).then ->
@expectOptionsToBe({
url: "http://xn--localhost-ob26h:1234/%F0%9F%98%80"
})

it "should percent escape unicode in path and convert domain name properly with encodedURI", ->
cy.request({ url: encodeURI('http://localhost😀:1234/😀') }).then ->
@expectOptionsToBe({
url: "http://xn--localhost-ob26h:1234/%F0%9F%98%80"
})

0 comments on commit 5eca3e9

Please sign in to comment.