Сбросить всю HTTP-связь как необработанные данные в nodejs
Интересно, возможно ли сбросить весь запрос HTTP-запроса +, когда он перейдет через провод.
Я не хочу получать метод, информацию о пути, строку запроса, заголовки, куки, тело и т.д. Я мог бы теоретически собирать необработанные данные, но тогда мне не нужна библиотека HTTP, не так ли?
Более того, я хочу сбрасывать точно байты, которые проходят по проводу.
Я хочу, чтобы необработанные данные, как на этом изображении
взято с этой страницы.
Я использую текущий node.js
как HTTP-клиент с request
. Это простой HTTP (без HTTPS).
Установка прокси в node.js была бы вариантом, но я не настаиваю на библиотеке. Я мог бы представить, что он должен выполнять функции чтения и записи сокетов, но я не вижу, как добраться до используемого сокета.
Ответы
Ответ 1
Модуль Request возвращает дополнительные объекты. Возвращаемое значение представляет собой расширенный объект http.ClientRequest
(вид), а обратный вызов предоставляется дополненным http.IncomingMessage
в качестве второго аргумента. Вы можете использовать различные свойства для восстановления ответа, но вы не можете получить его прямо отсюда. Собственный HTTP-API, который Node предоставляет абстракты от исходного ответа.
(Документы для IncomingMessage
и ClientRequest
находятся здесь: https://nodejs.org/api/http.html).
Более интересно, что это абстракции по сравнению с net.Socket
. Если вы используете собственный http
API, вы можете прослушать этот Socket
прежде чем отправлять ClientRequest
(с .end
). Это даст вам Buffer
содержащий HTTP-ответ.
let http = require("http");
let nativeRequest = http.get({
host: "google.com"
}); //get a ClientRequest object
nativeRequest.on('socket', function (socket) {
socket.on('data', function (data) { console.log(data.toString()); });
});
nativeRequest.end();
Это не похоже на то, что позволяет вам отслеживать исходящий запрос, но отлично подходит для ответа.
Возвращаясь к цепочке абстракции, это отлично работает с Request. Я пропущу фрагмент, потому что он почти идентичен предыдущему и предстоящему.
Чтобы получить запрос, мы можем сориентироваться во внутренней части Socket, чтобы увидеть, есть ли там что-то, что мы можем злоупотреблять. Object.keys(socket)
возвращает следующий массив:
[
"connecting",
"_hadError",
"_handle",
"_parent",
"_host",
"_readableState",
"readable",
"domain",
"_events",
"_eventsCount",
"_maxListeners",
"_writableState",
"writable",
"allowHalfOpen",
"destroyed",
"_bytesDispatched",
"_sockname",
"_pendingData",
"_pendingEncoding",
"server",
"_server",
"parser",
"_httpMessage"
]
И действительно, если мы ткнуть подозрительно выглядящую _pendingData
, мы можем просмотреть запрос до его отправки:
let request = require('request');
let req = request("http://google.com", function (e, r, d) {});
req.on('socket', function (socket) {
console.log("========\nRequest\n========")
console.log(JSON.stringify(socket._pendingData, null, 3));
console.log("========\nResponse\n========");
socket.on('data', function (data) { console.log(data.toString()); });
});
Ответ 2
Это вернет заголовки запроса, отправленные как ответ
const http = require("http")
function getRawHeader(req, res) {
const httpVersion = req.httpVersion
let str = '${req.method.toUpperCase()} ${req.url} HTTP/${httpVersion}\n'
for (let i = 0; i < req.rawHeaders.length; i = i + 2) {
str += '${req.rawHeaders[1]} : ${req.rawHeaders[i + 1]}\n'
console.log(i)
}
let written = false
req.on("readable", (chunk) => {
const data = req.read()
if (!written) {
res.write(str)
res.write("\n")
}
written = true
if (data) res.write(data)
})
}
http.createServer((req, res) => {
getRawHeader(req, res)
req.on("end", () =>res.end())
}).listen(7200, () => console.log("server f is running"))
Ответ 3
http.request
также имеет возможность передать ваше собственное соединение (createConnection
). Вы можете использовать эту опцию, чтобы предоставить свое собственное созданное соединение, которое является "piped()" для потока преобразования журнала.
const { Transform } = require('stream');
const http = require('http');
const agent = new http.Agent();
let nativeRequest = http.get({
host: 'google.com',
createConnection: options => {
let connection = agent.createConnection(options);
let logger = new Transform({
transform: (chunk, encoding, callback) => {
console.log(chunk.toString());
connection.write(chunk, encoding, callback);
},
flush: () => {},
});
connection.pipe(logger);
return logger;
},
});
nativeRequest.on('socket', function(socket) {
socket.on('data', function(data) {
console.log(data.toString());
});
});
nativeRequest.end();
Некоторые примечания
Я попытался реализовать поток PassThrough
вместо Transform
. Это дало мне ошибку разбора HTTP, когда я передал поток PassThrough в соединение. Я не знаю, почему быть честным.
Важно включить flush:() => {}
, из документации
Это будет вызываться, когда больше нет записанных данных, но до того, как произойдет событие "end", сигнализирующее о конце Readable stream.
https://nodejs.org/api/stream.html#stream_transform_flush_callback