Я хотел бы отправлять запросы HTTP / 2 на сервер через прокси, используя Node.js http2 библиотеку.
Я использую Charles v4.2.7 в качестве прокси, в целях тестирования, но Чарльз не может проксировать запрос. Charles показывает ошибки Malformed request URL "*"
, поскольку полученный запрос - PRI * HTTP/2.0
( HTTP / 2 Connection Preface ). Я могу успешно отправлять запросы HTTP / 2 через прокси Charles с помощью cURL (например, curl --http2 -x localhost:8888 https://cypher.codes
), поэтому я не думаю, что это проблема Charles, а проблема с моей реализацией Node.js.
Вот моя реализация клиента Node.js HTTP / 2, которая пытается отправить запрос GET на https://cypher.codes через мой прокси Charles, прослушивающий http://localhost: 8888:
const http2 = require('http2');
const client = http2.connect('http://localhost:8888');
client.on('error', (err) => console.error(err));
const req = client.request({
':scheme': 'https',
':method': 'GET',
':authority': 'cypher.codes',
':path': '/',
});
req.on('response', (headers, flags) => {
for (const name in headers) {
console.log(`${name}: ${headers[name]}`);
}
});
req.setEncoding('utf8');
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(`\n${data}`);
client.close();
});
req.end();
Вот ошибка Node.js, которую я получаю при запуске node proxy.js
(proxy.js
- это файл, содержащий приведенный выше код):
events.js:200
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP2_ERROR]: Protocol error
at Http2Session.onSessionInternalError (internal/http2/core.js:746:26)
Emitted 'error' event on ClientHttp2Stream instance at:
at emitErrorNT (internal/streams/destroy.js:92:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
at processTicksAndRejections (internal/process/task_queues.js:81:21) {
code: 'ERR_HTTP2_ERROR',
errno: -505
}
Я повторно выполняю указанный выше запрос cURL с подробным выводом, и похоже, что cURL сначала отправляет a CONNECT к прокси-серверу с использованием HTTP / 1 перед отправкой запроса GET с использованием HTTP / 2.
$ curl -v --http2 -x localhost:8888 https://cypher.codes
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8888 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to cypher.codes:443
> CONNECT cypher.codes:443 HTTP/1.1
> Host: cypher.codes:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection established
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=cypher.codes
* start date: Jun 21 04:38:35 2020 GMT
* expire date: Sep 19 04:38:35 2020 GMT
* subjectAltName: host "cypher.codes" matched cert's "cypher.codes"
* issuer: CN=Charles Proxy CA (8 Oct 2018, mcypher-mbp.local); OU=https://charlesproxy.com/ssl; O=XK72 Ltd; L=Auckland; ST=Auckland; C=NZ
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7ff50d00d600)
> GET / HTTP/2
> Host: cypher.codes
> User-Agent: curl/7.64.1
> Accept: */*
>
...
Я бы хотел попробовать сделать то же самое через Node.js (сначала отправив HTTP / 1 CONNECT запрос, а затем отправив мой запрос HTTP / 2 по тому же TCP-соединению), но я не уверен, как это сделать. Сам процесс создания клиента HTTP / 2 (например, http2.connect('http://localhost:8888');
) отправляет предисловие к соединению HTTP / 2. Я подумал о том, чтобы сначала создать соединение с использованием HTTP / 1 (например, используя библиотеку http
), а затем обновить его до HTTP / 2, но я не смог найти никаких примеров того, как это сделать.
Может кто-нибудь поможет мне отправить запрос HTTP / 2 через прокси, используя Node.js?
Обновление (2020-07-13): Я добился большего прогресса в том, чтобы сначала создать соединение с использованием HTTP / 1, отправить запрос CONNECT, а затем попытаться отправить запрос GET с использованием HTTP / 2 через та же розетка. Я вижу, что запрос CONNECT поступает в Charles, но не дополнительный запрос GET, который указывает на то, что я все еще делаю что-то не так, пытаясь использовать тот же сокет для запросов HTTP / 2. Вот мой обновленный код:
const http = require('http');
const http2 = require('http2');
const options = {
hostname: 'localhost',
port: 8888,
method: 'CONNECT',
path: 'cypher.codes:80',
headers: {
Host: 'cypher.codes:80',
'Proxy-Connection': 'Keep-Alive',
'Connection': 'Keep-Alive',
},
};
const connReq = http.request(options);
connReq.end();
connReq.on('connect', (_, socket) => {
const client = http2.connect('https://cypher.codes', {
createConnection: () => { return socket },
});
client.on('connect', () => console.log('http2 client connect success'));
client.on('error', (err) => console.error(`http2 client connect error: ${err}`));
const req = client.request({
':path': '/',
});
req.setEncoding('utf8');
req.on('response', (headers, flags) => {
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(data);
client.close();
});
});
req.end();
});