diff --git a/cli/package.json b/cli/package.json
index f2b9ef7223..0d8a64fc2b 100644
--- a/cli/package.json
+++ b/cli/package.json
@@ -100,4 +100,4 @@
"index.js",
"types/**/*.d.ts"
]
-}
+}
\ No newline at end of file
diff --git a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee
index 833b372811..16dc0f4155 100644
--- a/packages/driver/test/cypress/integration/commands/navigation_spec.coffee
+++ b/packages/driver/test/cypress/integration/commands/navigation_spec.coffee
@@ -541,6 +541,11 @@ describe "src/cy/commands/navigation", ->
.then ->
expect(backend).to.be.calledWithMatch("resolve:url", "http://localhost:3500/timeout", { auth })
+ ## https://github.com/cypress-io/cypress/issues/1727
+ it "can visit a page with undefined content type and html-shaped body", ->
+ cy
+ .visit("http://localhost:3500/undefined-content-type")
+
describe "when only hashes are changing", ->
it "short circuits the visit if the page will not refresh", ->
count = 0
diff --git a/packages/driver/test/support/server.coffee b/packages/driver/test/support/server.coffee
index a71def2745..e2641857c4 100644
--- a/packages/driver/test/support/server.coffee
+++ b/packages/driver/test/support/server.coffee
@@ -72,6 +72,9 @@ niv.install("react-dom@15.6.1")
res.setHeader('Content-Type', 'text/html; charset=utf-8,text/html')
res.end("
TestHello")
+ app.get '/undefined-content-type', (req, res) ->
+ res.end("some stuff that looks likehtml")
+
app.all '/dump-method', (req, res) ->
res.send("request method: #{req.method}")
diff --git a/packages/server/lib/controllers/proxy.coffee b/packages/server/lib/controllers/proxy.coffee
index 8616c393f6..93a6917d66 100644
--- a/packages/server/lib/controllers/proxy.coffee
+++ b/packages/server/lib/controllers/proxy.coffee
@@ -11,7 +11,6 @@ buffers = require("../util/buffers")
rewriter = require("../util/rewriter")
blacklist = require("../util/blacklist")
conditional = require("../util/conditional_stream")
-networkFailures = require("../util/network_failures")
REDIRECT_STATUS_CODES = [301, 302, 303, 307, 308]
NO_BODY_STATUS_CODES = [204, 304]
diff --git a/packages/server/lib/file_server.coffee b/packages/server/lib/file_server.coffee
index 3e4c9f47b1..7162c38ae4 100644
--- a/packages/server/lib/file_server.coffee
+++ b/packages/server/lib/file_server.coffee
@@ -29,6 +29,7 @@ onRequest = (req, res, fileServerFolder) ->
})
.on "error", (err) ->
res.setHeader("x-cypress-file-server-error", true)
+ res.setHeader("content-type", "text/html")
res.statusCode = err.status
res.end(networkFailures.get(file, err.status))
.pipe(res)
diff --git a/packages/server/lib/server.coffee b/packages/server/lib/server.coffee
index 99744679ce..9907dacb0e 100644
--- a/packages/server/lib/server.coffee
+++ b/packages/server/lib/server.coffee
@@ -2,11 +2,13 @@ _ = require("lodash")
exphbs = require("express-handlebars")
url = require("url")
http = require("http")
+concatStream = require("concat-stream")
cookie = require("cookie")
stream = require("stream")
express = require("express")
Promise = require("bluebird")
evilDns = require("evil-dns")
+isHtml = require("is-html")
httpProxy = require("http-proxy")
la = require("lazy-ass")
check = require("check-more-types")
@@ -34,6 +36,15 @@ fileServer = require("./file_server")
DEFAULT_DOMAIN_NAME = "localhost"
fullyQualifiedRe = /^https?:\/\//
+isResponseHtml = (contentType, responseBuffer) ->
+ if contentType
+ return contentType is "text/html"
+
+ if body = _.invoke(responseBuffer, 'toString')
+ return isHtml(body)
+
+ return false
+
setProxiedUrl = (req) ->
## bail if we've already proxied the url
return if req.proxiedUrl
@@ -418,11 +429,9 @@ class Server
isOk = statusIs2xxOrAllowedFailure()
contentType = headersUtil.getContentType(incomingRes)
- isHtml = contentType is "text/html"
details = {
isOkStatusCode: isOk
- isHtml
contentType
url: newUrl
status: incomingRes.statusCode
@@ -439,19 +448,22 @@ class Server
debug("setting details resolving url %o", details)
- ## this will allow us to listen to `str`'s `end` event by putting it in flowing mode
- responseBuffer = stream.PassThrough({
- ## buffer forever - node's default is only to buffer 16kB
- highWaterMark: Infinity
- })
-
- str.pipe(responseBuffer)
-
- str.on "end", =>
+ concatStr = concatStream (responseBuffer) =>
## buffer the entire response before resolving.
## this allows us to detect & reject ETIMEDOUT errors
## where the headers have been sent but the
## connection hangs before receiving a body.
+
+ if !_.get(responseBuffer, 'length')
+ ## concatStream can yield an empty array, which is
+ ## not a valid chunk
+ responseBuffer = undefined
+
+ ## if there is not a content-type, try to determine
+ ## if the response content is HTML-like
+ ## https://github.com/cypress-io/cypress/issues/1727
+ details.isHtml = isResponseHtml(contentType, responseBuffer)
+
debug("resolve:url response ended, setting buffer %o", { newUrl, details })
details.totalTime = new Date() - startTime
@@ -460,15 +472,21 @@ class Server
## frontend so that the driver can be in control of
## when the server should cache the request buffer
## and set the domain vs not
- if isOk and isHtml
+ if isOk and details.isHtml
## reset the domain to the new url if we're not
## handling a local file
@_onDomainSet(newUrl, options) if not handlingLocalFile
+ responseBufferStream = new stream.PassThrough({
+ highWaterMark: Number.MAX_SAFE_INTEGER
+ })
+
+ responseBufferStream.end(responseBuffer)
+
buffers.set({
url: newUrl
jar: jar
- stream: responseBuffer
+ stream: responseBufferStream
details: details
originalUrl: originalUrl
response: incomingRes
@@ -479,6 +497,8 @@ class Server
restorePreviousState()
resolve(details)
+
+ str.pipe(concatStr)
.catch(onReqError)
restorePreviousState = =>
diff --git a/packages/server/package.json b/packages/server/package.json
index 2f9a594d28..80ca991e46 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -143,6 +143,7 @@
"human-interval": "0.1.6",
"image-size": "0.7.4",
"is-fork-pr": "2.3.0",
+ "is-html": "2.0.0",
"jimp": "0.6.4",
"jsonlint": "1.6.3",
"konfig": "0.2.1",
diff --git a/packages/server/test/integration/server_spec.coffee b/packages/server/test/integration/server_spec.coffee
index b97b1a7128..096d3fc2bb 100644
--- a/packages/server/test/integration/server_spec.coffee
+++ b/packages/server/test/integration/server_spec.coffee
@@ -240,8 +240,8 @@ describe "Server", ->
.then (obj = {}) ->
expectToEqDetails(obj, {
isOkStatusCode: false
- isHtml: false
- contentType: undefined
+ isHtml: true
+ contentType: "text/html"
url: "http://localhost:2000/does-not-exist"
originalUrl: "/does-not-exist"
filePath: Fixtures.projectPath("no-server/dev/does-not-exist")
@@ -406,6 +406,44 @@ describe "Server", ->
cookies: []
})
+ it "yields isHtml true for HTML-shaped responses", ->
+ nock("http://example.com")
+ .get("/")
+ .reply(200, "foo")
+
+ @server._onResolveUrl("http://example.com", {}, @automationRequest)
+ .then (obj = {}) ->
+ expectToEqDetails(obj, {
+ isOkStatusCode: true
+ isHtml: true
+ contentType: undefined
+ url: "http://example.com/"
+ originalUrl: "http://example.com/"
+ status: 200
+ statusText: "OK"
+ redirects: []
+ cookies: []
+ })
+
+ it "yields isHtml false for non-HTML-shaped responses", ->
+ nock("http://example.com")
+ .get("/")
+ .reply(200, '{ foo: "bar" }')
+
+ @server._onResolveUrl("http://example.com", {}, @automationRequest)
+ .then (obj = {}) ->
+ expectToEqDetails(obj, {
+ isOkStatusCode: true
+ isHtml: false
+ contentType: undefined
+ url: "http://example.com/"
+ originalUrl: "http://example.com/"
+ status: 200
+ statusText: "OK"
+ redirects: []
+ cookies: []
+ })
+
it "can follow multiple http redirects", ->
nock("http://espn.com")
.get("/")