Skip to content

Commit

Permalink
Refactor proxy into own package, implement middleware pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
flotwig committed Sep 16, 2019
1 parent 446ea62 commit 177c15a
Show file tree
Hide file tree
Showing 42 changed files with 1,387 additions and 804 deletions.
1 change: 1 addition & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ jobs:
- run: npm run all test -- --package https-proxy
- run: npm run all test -- --package launcher
- run: npm run all test -- --package network
- run: npm run all test -- --package proxy
# how to pass Mocha reporter through zunder?
- run: npm run all test -- --package reporter
- run: npm run all test -- --package runner
Expand Down
16 changes: 6 additions & 10 deletions packages/network/lib/blacklist.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const _ = require('lodash')
const minimatch = require('minimatch')
const uri = require('./uri')
import _ from 'lodash'
import minimatch from 'minimatch'
import { stripProtocolAndDefaultPorts } from './uri'

const matches = function (urlToCheck, blacklistHosts) {
//# normalize into flat array
export function matches (urlToCheck, blacklistHosts) {
// normalize into flat array
blacklistHosts = [].concat(blacklistHosts)

urlToCheck = uri.stripProtocolAndDefaultPorts(urlToCheck)
urlToCheck = stripProtocolAndDefaultPorts(urlToCheck)

// use minimatch against the url
// to see if any match
Expand All @@ -16,7 +16,3 @@ const matches = function (urlToCheck, blacklistHosts) {

return _.find(blacklistHosts, matchUrl)
}

module.exports = {
matches,
}
112 changes: 60 additions & 52 deletions packages/network/lib/cors.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,76 @@
const _ = require('lodash')
const url = require('url')
const uri = require('./uri')
const debug = require('debug')('cypress:server:cors')
const parseDomain = require('parse-domain')
import _ from 'lodash'
import * as uri from './uri'
import debugModule from 'debug'
import parseDomain, { ParsedDomain } from 'parse-domain'

const debug = debugModule('cypress:network:cors')

const ipAddressRe = /^[\d\.]+$/

module.exports = {
parseUrlIntoDomainTldPort (str) {
let { hostname, port, protocol } = url.parse(str)
type ParsedHost = {
port?: string
tld?: string
domain?: string
}

if (port == null) {
port = protocol === 'https:' ? '443' : '80'
}
export function parseUrlIntoDomainTldPort (str) {
let { hostname, port, protocol } = uri.parse(str)

if (!hostname) {
hostname = ''
}

if (!port) {
port = protocol === 'https:' ? '443' : '80'
}

let parsed = parseDomain(hostname, {
privateTlds: true, // use the public suffix
customTlds: ipAddressRe,
})

// if we couldn't get a parsed domain
if (!parsed) {
// then just fall back to a dumb check
// based on assumptions that the tld
// is the last segment after the final
// '.' and that the domain is the segment
// before that
const segments = hostname.split('.')

parsed = {
tld: segments[segments.length - 1] || '',
domain: segments[segments.length - 2] || '',
}
let parsed : Partial<ParsedDomain> | null = parseDomain(hostname, {
privateTlds: true, // use the public suffix
customTlds: ipAddressRe,
})

// if we couldn't get a parsed domain
if (!parsed) {
// then just fall back to a dumb check
// based on assumptions that the tld
// is the last segment after the final
// '.' and that the domain is the segment
// before that
const segments = hostname.split('.')

parsed = {
tld: segments[segments.length - 1] || '',
domain: segments[segments.length - 2] || '',
}
}

const obj = {}
const obj: ParsedHost = {}

obj.port = port
obj.tld = parsed.tld
obj.domain = parsed.domain
// obj.protocol = protocol
obj.port = port
obj.tld = parsed.tld
obj.domain = parsed.domain

debug('Parsed URL %o', obj)
debug('Parsed URL %o', obj)

return obj
},
return obj
}

urlMatchesOriginPolicyProps (urlStr, props) {
// take a shortcut here in the case
// where remoteHostAndPort is null
if (!props) {
return false
}
export function urlMatchesOriginPolicyProps (urlStr, props) {
// take a shortcut here in the case
// where remoteHostAndPort is null
if (!props) {
return false
}

const parsedUrl = this.parseUrlIntoDomainTldPort(urlStr)
const parsedUrl = parseUrlIntoDomainTldPort(urlStr)

// does the parsedUrl match the parsedHost?
return _.isEqual(parsedUrl, props)
},
// does the parsedUrl match the parsedHost?
return _.isEqual(parsedUrl, props)
}

urlMatchesOriginProtectionSpace (urlStr, origin) {
const normalizedUrl = uri.addDefaultPort(urlStr).format()
const normalizedOrigin = uri.addDefaultPort(origin).format()
export function urlMatchesOriginProtectionSpace (urlStr, origin) {
const normalizedUrl = uri.addDefaultPort(urlStr).format()
const normalizedOrigin = uri.addDefaultPort(origin).format()

return _.startsWith(normalizedUrl, normalizedOrigin)
},
return _.startsWith(normalizedUrl, normalizedOrigin)
}
8 changes: 7 additions & 1 deletion packages/network/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import agent from './agent'
import * as connect from './connect'
import { allowDestroy } from './allow-destroy'
import * as blacklist from './blacklist'
import * as connect from './connect'
import * as cors from './cors'
import * as uri from './uri'

export {
agent,
allowDestroy,
blacklist,
connect,
cors,
uri,
}
42 changes: 13 additions & 29 deletions packages/server/lib/util/uri.js → packages/network/lib/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// node's url formatting algorithm (which acts pretty unexpectedly)
// - https://nodejs.org/api/url.html#url_url_format_urlobject

const _ = require('lodash')
const url = require('url')
import _ from 'lodash'
import url from 'url'

// yup, protocol contains a: ':' colon
// at the end of it (-______________-)
Expand All @@ -25,9 +25,9 @@ const parseClone = (urlObject) => {
return url.parse(_.clone(urlObject))
}

const parse = url.parse
export const parse = url.parse

const stripProtocolAndDefaultPorts = function (urlToCheck) {
export function stripProtocolAndDefaultPorts (urlToCheck) {
// grab host which is 'hostname:port' only
const { host, hostname, port } = url.parse(urlToCheck)

Expand All @@ -41,20 +41,18 @@ const stripProtocolAndDefaultPorts = function (urlToCheck) {
return host
}

const removePort = (urlObject) => {
export function removePort (urlObject) {
const parsed = parseClone(urlObject)

// set host to null else
// url.format(...) will ignore
// the port property
// set host to undefined else url.format(...) will ignore the port property
// https://nodejs.org/api/url.html#url_url_format_urlobject
parsed.host = null
parsed.port = null
parsed.host = undefined
parsed.port = undefined

return parsed
}

const removeDefaultPort = function (urlToCheck) {
export function removeDefaultPort (urlToCheck) {
let parsed = parseClone(urlToCheck)

if (portIsDefault(parsed.port)) {
Expand All @@ -64,33 +62,19 @@ const removeDefaultPort = function (urlToCheck) {
return parsed
}

const addDefaultPort = function (urlToCheck) {
export function addDefaultPort (urlToCheck) {
const parsed = parseClone(urlToCheck)

if (!parsed.port) {
// unset host...
// see above for reasoning
parsed.host = null
parsed.port = DEFAULT_PROTOCOL_PORTS[parsed.protocol]
parsed.host = undefined
parsed.port = DEFAULT_PROTOCOL_PORTS[parsed.protocol || 'http:']
}

return parsed
}

const getPath = (urlToCheck) => {
export function getPath (urlToCheck) {
return url.parse(urlToCheck).path
}

module.exports = {
parse,

getPath,

removePort,

addDefaultPort,

removeDefaultPort,

stripProtocolAndDefaultPorts,
}
1 change: 1 addition & 0 deletions packages/network/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"bluebird": "3.5.3",
"debug": "4.1.1",
"lodash": "4.17.15",
"parse-domain": "2.0.0",
"proxy-from-env": "1.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/network/test/mocha.opts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
test/unit
test/integration
--compilers ts:@packages/ts/register
--compilers coffee:@packages/coffee/register,ts:@packages/ts/register
--timeout 10000
--recursive
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require("../spec_helper")

blacklist = require("#{root}lib/util/blacklist")
{ blacklist } = require("../..")
{ expect } = require("chai")

hosts = [
"*.google.com"
Expand All @@ -21,7 +20,7 @@ matchesArray = (url, val) ->
matchesHost = (url, host) ->
expect(blacklist.matches(url, hosts)).to.eq(host)

describe "lib/util/blacklist", ->
describe "lib/blacklist", ->
it "handles hosts, ports, wildcards", ->
matchesArray("https://mail.google.com/foo", true)
matchesArray("https://shop.apple.com/bar", true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
require("../spec_helper")
{ cors } = require("../..")
{ expect } = require("chai")

cors = require("#{root}lib/util/cors")

describe "lib/util/cors", ->
describe "lib/cors", ->
context ".parseUrlIntoDomainTldPort", ->
beforeEach ->
@isEq = (url, obj) ->
Expand Down
5 changes: 5 additions & 0 deletions packages/proxy/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if (process.env.CYPRESS_ENV !== 'production') {
require('@packages/ts/register')
}

module.exports = require('./lib')
48 changes: 48 additions & 0 deletions packages/proxy/lib/http/error-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import _ from 'lodash'
import debugModule from 'debug'
import { HttpMiddleware } from '.'
import { Readable } from 'stream'
import { Request } from 'request'

const debug = debugModule('cypress:proxy:http:error-middleware')

type ErrorMiddleware = HttpMiddleware<{
error: Error
incomingResStream?: Readable
outgoingReq?: Request
}>

const LogError : ErrorMiddleware = function () {
debug('error proxying request %o', _.pick(this, 'error', 'req', 'res', 'incomingRes', 'outgoingReq', 'incomingResStream'))
this.next()
}

export const AbortRequest : ErrorMiddleware = function () {
if (this.outgoingReq) {
debug('aborting outgoingReq')
this.outgoingReq.abort()
}

this.next()
}

export const UnpipeResponse : ErrorMiddleware = function () {
if (this.incomingResStream) {
debug('unpiping resStream from response')
this.incomingResStream.unpipe()
}

this.next()
}

export const DestroyResponse : ErrorMiddleware = function () {
this.res.destroy()
this.end()
}

export default {
LogError,
AbortRequest,
UnpipeResponse,
DestroyResponse,
}
Loading

0 comments on commit 177c15a

Please sign in to comment.