-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AUTH-1236] Optionally support accepting HTTPS connections in addition to HTTP #472
Changes from all commits
630d4fd
bc9c83f
f6cd3fa
d2d5414
2065f74
1085149
9ae103c
81211d4
6a153cf
086e0b2
bd70d65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import assertNever from "assert-never"; | ||
import chalk from "chalk"; | ||
import fs from "fs"; | ||
import http from "http"; | ||
import https from "https"; | ||
import net from "net"; | ||
import { ensureBuffer } from "./buffer"; | ||
import { findNextRecordToReplay, findRecordMatches } from "./matcher"; | ||
import { Mode } from "./modes"; | ||
|
@@ -12,7 +15,7 @@ import { TapeRecord } from "./tape"; | |
* A server that proxies or replays requests depending on the mode. | ||
*/ | ||
export class RecordReplayServer { | ||
private server: http.Server; | ||
private server: net.Server; | ||
private persistence: Persistence; | ||
|
||
private mode: Mode; | ||
|
@@ -34,6 +37,9 @@ export class RecordReplayServer { | |
enableLogging?: boolean; | ||
redactHeaders?: string[]; | ||
preventConditionalRequests?: boolean; | ||
httpsCA?: string; | ||
httpsKey?: string; | ||
httpsCert?: string; | ||
}) { | ||
this.currentTapeRecords = []; | ||
this.mode = options.initialMode; | ||
|
@@ -46,7 +52,10 @@ export class RecordReplayServer { | |
this.preventConditionalRequests = options.preventConditionalRequests; | ||
this.loadTape(this.defaultTape); | ||
|
||
this.server = http.createServer(async (req, res) => { | ||
const handler = async ( | ||
req: http.IncomingMessage, | ||
res: http.ServerResponse | ||
) => { | ||
if (!req.url) { | ||
if (this.loggingEnabled) { | ||
console.error(chalk.red("Received a request without URL.")); | ||
|
@@ -97,6 +106,57 @@ export class RecordReplayServer { | |
res.statusCode = 500; | ||
res.end(); | ||
} | ||
}; | ||
|
||
const httpServer = http.createServer(handler); | ||
let httpsServer: https.Server | null = null; | ||
if (options.httpsKey && options.httpsCert) { | ||
const httpsOptions = { | ||
ca: options.httpsCA ? fs.readFileSync(options.httpsCA) : undefined, | ||
key: fs.readFileSync(options.httpsKey), | ||
cert: fs.readFileSync(options.httpsCert), | ||
}; | ||
httpsServer = https.createServer(httpsOptions, handler); | ||
} | ||
|
||
// Copied from https://stackoverflow.com/a/42019773/16286019 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Why do we have the requirement to server Https and Https over the same port? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha, now that is an excellent question. The short answer is cypress. The slightly longer answer is during web testing, requests are made to proxay directly and via an nginx rewrite. The former (will shortly be) HTTPS and the later is HTTP. The later can't easily be made HTTPS due to docker-compose circular dependency reasons. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cheers for the context! I assume we can't point nginx -> proxay to a http port (using docker), and still have cypress -> proxay using the https port. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be possible, I'd have to check again. I was having issues with that earlier but I may have solved them now. |
||
this.server = net.createServer((socket) => { | ||
socket.once("data", (buffer) => { | ||
// Pause the socket | ||
socket.pause(); | ||
|
||
// Determine if this is an HTTP(s) request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Can we encapsulate this |
||
const byte = buffer[0]; | ||
|
||
let server; | ||
// First byte of HTTPS is a 22. | ||
if (byte === 22) { | ||
server = httpsServer; | ||
} else if (32 < byte && byte < 127) { | ||
server = httpServer; | ||
} else { | ||
console.error( | ||
chalk.red( | ||
`Unexpected starting byte of incoming request: ${byte}. Dropping request.` | ||
) | ||
); | ||
} | ||
timdawborn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (server) { | ||
// Push the buffer back onto the front of the data stream | ||
socket.unshift(buffer); | ||
|
||
// Emit the socket to the HTTP(s) server | ||
server.emit("connection", socket); | ||
} | ||
timdawborn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// As of NodeJS 10.x the socket must be | ||
// resumed asynchronously or the socket | ||
// connection hangs, potentially crashing | ||
// the process. Prior to NodeJS 10.x | ||
// the socket may be resumed synchronously. | ||
process.nextTick(() => socket.resume()); | ||
}); | ||
}); | ||
} | ||
|
||
|
@@ -125,6 +185,7 @@ export class RecordReplayServer { | |
request.path === "/__proxay" | ||
) { | ||
res.end("Proxay!"); | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bug fix. |
||
} | ||
|
||
// Sending a request to /__proxay/tape will pick a specific tape and/or a new mode. | ||
|
@@ -166,7 +227,12 @@ export class RecordReplayServer { | |
this.unloadTape(); | ||
res.end(`Unloaded tape`); | ||
} | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also a bug fix. |
||
} | ||
|
||
// If we got here, we don't know what to do. Return a 404. | ||
res.statusCode = 404; | ||
res.end(`Unhandled proxay request.\n\n${JSON.stringify(request)}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also a bug fix. Currently if a |
||
} | ||
|
||
private async fetchResponse(request: Request): Promise<TapeRecord | null> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤯