Сбросить всю HTTP-связь как необработанные данные в nodejs

Интересно, возможно ли сбросить весь запрос HTTP-запроса +, когда он перейдет через провод.

Я не хочу получать метод, информацию о пути, строку запроса, заголовки, куки, тело и т.д. Я мог бы теоретически собирать необработанные данные, но тогда мне не нужна библиотека HTTP, не так ли?

Более того, я хочу сбрасывать точно байты, которые проходят по проводу.

Я хочу, чтобы необработанные данные, как на этом изображении

http raw data

взято с этой страницы.

Я использую текущий 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