From f5427d6c233108ed156c7d520c321142bfd36da4 Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Fri, 9 Apr 2021 23:41:11 +0100 Subject: [PATCH 1/6] chore: bootstrap http2client --- lib/core/session.js | 0 lib/http2client.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/core/session.js create mode 100644 lib/http2client.js diff --git a/lib/core/session.js b/lib/core/session.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/http2client.js b/lib/http2client.js new file mode 100644 index 00000000000..e69de29bb2d From 0e2c9ae12b70089b20ae4872ddfd7df9c56e8e00 Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Sat, 24 Apr 2021 23:54:38 +0100 Subject: [PATCH 2/6] feat: http2settings implementation --- lib/http2client.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lib/http2client.js b/lib/http2client.js index e69de29bb2d..cf6f9c7b9bf 100644 --- a/lib/http2client.js +++ b/lib/http2client.js @@ -0,0 +1,50 @@ +const FRAME_TYPES = [ + "DATA", // standard request / response payloads + "HEADERS", // starts stream, carries request headers + "PRIORITY", // advises the priority of a stream + "RST_STREAM", // immediately terminates a stream + "SETTINGS", // inform other end of endpoint config + "PUSH_PROMISE", // pre-warns peer of wanted streams + "PING", + "GOAWAY", // graceful version of RST_STREAM + "WINDOW_UPDATE", // defines flow-control + "CONTINUATION" // continue a sequence of headers +]; + +function parseHttp2Settings(settings) { + // under RFC7540 section 3.2.1, the settings must be a base64url encoded + // SETTINGS frame. 16 bit identifiers, 32 bit values. */ + let parsed = ""; + for (let i = 0; i < Object.keys(settings).length; i += 2) { + parsed += `${settings[i]}${settings[i + 1]}\r\n`; + } + + // base64url polyfill, courtesy of @panva + let encoded; + if (Buffer.isEncoding("base64url")) { + encoded = Buffer.from(parsed) + .toString("base64url") + .replace(/[=]+$/g, ""); + } else { + encoded = Buffer.from(parsed) + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/[=]+$/g, ""); + } + return encoded; +} + +function http2Connect(url, options) { + if (url.protocol === "https:") { + throw new Error("Invalid protocol - httpConnect upgrades http streams"); + } + throw new Error("UNIMPLEMENTED"); +} + +function https2Connect(url, options) { + if (url.protocol === "http:") { + throw new Error("Invalid protocol - https2Connect upgrades https streams"); + } + throw new Error("UNIMPLEMENTED"); +} From 764aa7bac9496cedf0c111cf46586b80478a429c Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Sun, 25 Apr 2021 00:30:38 +0100 Subject: [PATCH 3/6] feat: reference upgrade implementation --- lib/http2client.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/http2client.js b/lib/http2client.js index cf6f9c7b9bf..486a5532c15 100644 --- a/lib/http2client.js +++ b/lib/http2client.js @@ -1,3 +1,9 @@ +"use strict"; +/* eslint-disable no-unused-vars, max-len, id-length */ + +const assert = require("assert"); +const { kUrl, kSocket, kHTTP2Opts } = require("./core/symbols"); + const FRAME_TYPES = [ "DATA", // standard request / response payloads "HEADERS", // starts stream, carries request headers @@ -35,16 +41,27 @@ function parseHttp2Settings(settings) { return encoded; } -function http2Connect(url, options) { - if (url.protocol === "https:") { +function http2Connect(client) { + assert(!client[kSocket]); + const { protocol, port, hostname } = client[kUrl]; + if (protocol === "https:") { throw new Error("Invalid protocol - httpConnect upgrades http streams"); } - throw new Error("UNIMPLEMENTED"); + // TODO: allow ALT-SVC + client.upgrade({ + protocol: "h2c", + headers: { + "Connection": "Upgrade, HTTP2-Settings", + "Upgrade": "h2c", + "HTTP2-Settings": parseHttp2Settings(client[kHTTP2Opts]) + } + }); } -function https2Connect(url, options) { - if (url.protocol === "http:") { +function https2Connect(client) { + const { protocol, port, hostname } = client[kUrl]; + if (protocol === "http:") { throw new Error("Invalid protocol - https2Connect upgrades https streams"); } - throw new Error("UNIMPLEMENTED"); + // TODO: tls negotiation comes FIRST } From d46cff2978b6ae2f4f355169004e6b09bfa1df4a Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Sun, 25 Apr 2021 01:22:08 +0100 Subject: [PATCH 4/6] feat: support http2-settings connection field --- lib/client.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/client.js b/lib/client.js index d6ee7e2fa7a..63226de0197 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1289,7 +1289,11 @@ function write (client, request) { let header if (upgrade) { - header = `${method} ${path} HTTP/1.1\r\nconnection: upgrade\r\nupgrade: ${upgrade}\r\n` + let connectionString = "upgrade"; + if (headers.toLowerCase().includes("http2-settings")) { + connectionString += ", http2-settings"; + } + header = `${method} ${path} HTTP/1.1\r\nconnection: ${connectionString}\r\nupgrade: ${upgrade}\r\n` } else if (client[kPipelining]) { header = `${method} ${path} HTTP/1.1\r\nconnection: keep-alive\r\n` } else { From 71509fe7046fefdb34378741d219622714c2e6bf Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Sun, 25 Apr 2021 12:34:52 +0100 Subject: [PATCH 5/6] fix: handle upgrade paths --- lib/http2client.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/http2client.js b/lib/http2client.js index 486a5532c15..d9f4c741e20 100644 --- a/lib/http2client.js +++ b/lib/http2client.js @@ -43,19 +43,24 @@ function parseHttp2Settings(settings) { function http2Connect(client) { assert(!client[kSocket]); - const { protocol, port, hostname } = client[kUrl]; + const { protocol, port, hostname, pathname } = client[kUrl]; if (protocol === "https:") { throw new Error("Invalid protocol - httpConnect upgrades http streams"); } // TODO: allow ALT-SVC client.upgrade({ + path: pathname, protocol: "h2c", headers: { - "Connection": "Upgrade, HTTP2-Settings", - "Upgrade": "h2c", - "HTTP2-Settings": parseHttp2Settings(client[kHTTP2Opts]) + "HTTP2-Settings": parseHttp2Settings(client[kHTTP2Opts] || {}) } - }); + }) + .then((res) => { + // upgrade successful, validate response and use http2 + }) + .catch((err) => { + // continue as normal + }); } function https2Connect(client) { @@ -65,3 +70,5 @@ function https2Connect(client) { } // TODO: tls negotiation comes FIRST } + +module.exports = http2Connect; From 959b30014a67130835ec2a0c1159b7a69f37c429 Mon Sep 17 00:00:00 2001 From: Nytelife26 Date: Wed, 19 May 2021 23:09:49 +0100 Subject: [PATCH 6/6] chore: upload remaining --- lib/http2client.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/http2client.js b/lib/http2client.js index d9f4c741e20..5ac49b8e5fc 100644 --- a/lib/http2client.js +++ b/lib/http2client.js @@ -56,10 +56,15 @@ function http2Connect(client) { } }) .then((res) => { - // upgrade successful, validate response and use http2 + console.log("hi"); + console.log(res); }) .catch((err) => { - // continue as normal + if (err.code === "UND_ERR_SOCKET" && err.message === "bad upgrade") { + // do not upgrade + } else { + throw err; + } }); }