web-dev-qa-db-fra.com

Vider l'intégralité de la communication HTTP sous forme de données brutes dans nodejs

Je me demande s'il est possible de vider la totalité de la requête + réponse HTTP au fur et à mesure qu'elle passe par le fil.

Je ne veux pas obtenir la méthode, les informations sur le chemin, la chaîne de requête, les en-têtes, les cookies, le corps et autres. Je pourrais en théorie assembler les données brutes moi-même, mais je n'aurais alors pas besoin de bibliothèque HTTP, non?

De plus, je veux vider exactement les octets qui passent sur le fil .

Je veux les données brutes comme dans cette image

http raw data

tiré de cette page .

J'utilise le courant node.js en tant que client HTTP avec request. C'est du HTTP simple (pas de HTTPS).

Installer un proxy dans node.js serait une option, mais je n'insiste pas sur une bibliothèque. Je pourrais imaginer encapsuler les fonctions de lecture et d'écriture du socket, mais je ne vois pas comment accéder au socket utilisé.

18
maaartinus

Le module Request renvoie des objets natifs augmentés. La valeur de retour est un objet http.ClientRequest Augmenté (sorte de), et le rappel est fourni un http.IncomingMessage Augmenté comme deuxième argument. Vous pouvez utiliser les différentes propriétés pour reconstruire la réponse, mais vous ne pouvez pas l'obtenir directement à partir d'ici. L'API http native qui Node fournit des résumés loin de la réponse brute.

(Les documents pour IncomingMessage et ClientRequest sont ici: https://nodejs.org/api/http.html ).

Plus intéressant, ce sont deux abstractions sur net.Socket. Si vous utilisez l'API native http, vous pouvez l'écouter Socket avant d'envoyer un ClientRequest (avec .end). Cela vous donnera un Buffer contenant la réponse 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();

Il ne semble pas que cela vous permet d'espionner la demande sortante, mais cela fonctionne très bien pour la réponse.

En remontant la chaîne d'abstraction, cela fonctionne très bien avec Request. Je vais sauter l'extrait de code, car il est presque identique à la fois à la précédente et à la prochaine.

Pour obtenir la demande, nous pouvons fouiller dans les internes d'une prise pour voir s'il y a quelque chose que nous pouvons abuser. Object.keys(socket) renvoie le tableau suivant:

[
   "connecting",
   "_hadError",
   "_handle",
   "_parent",
   "_Host",
   "_readableState",
   "readable",
   "domain",
   "_events",
   "_eventsCount",
   "_maxListeners",
   "_writableState",
   "writable",
   "allowHalfOpen",
   "destroyed",
   "_bytesDispatched",
   "_sockname",
   "_pendingData",
   "_pendingEncoding",
   "server",
   "_server",
   "parser",
   "_httpMessage"
]

Et, en effet, si nous nous penchons sur l'aspect suspect _pendingData, Nous pouvons afficher la demande avant qu'elle ne soit envoyée:

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()); });
});
6
shiftweave

Cela renverra les en-têtes de demande envoyés comme réponse

 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"))
0
Kayslay

http.request A également la possibilité de passer votre propre connexion (createConnection). Vous pouvez utiliser cette option pour fournir votre propre connexion créée, qui est "piped ()" à un flux de transformation de l'enregistreur.

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();

Quelques notes

J'ai essayé d'implémenter le flux PassThrough au lieu du Transform. Cela m'a donné une erreur d'analyse HTTP lorsque j'ai canalisé le flux PassThrough vers la connexion. Je ne sais pas pourquoi être honnête.

Il est important d'inclure la flush: () => {}, de la documentation

Cela sera appelé lorsqu'il n'y aura plus de données écrites à consommer, mais avant que l'événement "end" ne soit émis, signalant la fin du flux lisible.

https://nodejs.org/api/stream.html#stream_transform_flush_callback

0
janfabian