1

It must be a simple issue, but my knowledge of streams is limited.

HTTP/1 80 to HTTP/2 h2c proxy

script(not working):

const net = require('net');
const http = require('http');
const http2 = require('http2');
const socketPath = `/tmp/socket.test.${Date.now()}`;

// front http 80 server.
http.createServer((req, res) => {
    const socket = net.createConnection(socketPath)
    req.pipe(socket).pipe(res);
}).listen(80);

// private http2 socket server.
http2.createServer(function(socket) {
    socket.write(`Echo from http2 server\r\n`);
    socket.pipe(socket);
}).listen(socketPath);

HTTP/2 h2c to HTTP/2 h2c proxy

cli command to start request:

curl --http2-prior-knowledge -v http://localhost:3333/ --output -

script(not working):

const net = require('net');
const http = require('http');
const http2 = require('http2');
const socketPath = `/tmp/socket.test.${Date.now()}`;
const port = 3333;

const private = http2.createServer({allowHTTP1: true});
private.on('stream', (stream, headers) => {
    console.log('private http2 request');
    stream.end('HTTP/2');
});
private.listen(socketPath, () => console.log('private http2 server is listening', socketPath));

const public = http2.createServer({allowHTTP1: true});
public.on('stream', (stream, headers) => {
    console.log('public http2 request');
    const socket = net.connect(socketPath);
    stream.pipe(socket).pipe(stream);
});
public.listen(port, () => console.log('public http2 server is listening port', port));
Serg
  • 736
  • 1
  • 9
  • 20

2 Answers2

4

Finally, http2(h2c) to http2(h2c) (with unix socket) proxy works!

const net = require('net');
const http2 = require('http2');
const socketPath = `/tmp/socket.test.${Date.now()}`;
const port = 4444;

const priv = http2.createServer({});
priv.on('stream', (stream, headers) => {
    console.log('private http2 request');
    stream.end('HTTP/2');
});
priv.listen(socketPath, () => console.log('private http2 server is listening', socketPath));

const pub = http2.createServer({});
pub.on('stream', (stream, headers) => {
    const clientSession = http2.connect('http://0.0.0.0', {
        createConnection: () => net.connect({path: socketPath})
    });
    const req = clientSession.request({
      ':path': `/`,
    });
    req.pipe(stream).pipe(req);
});
pub.listen(port, () => console.log('public http2 server is listening port', port));
Serg
  • 736
  • 1
  • 9
  • 20
1

I am not a node expert, and I may have completely misunderstood what you are trying to do here, but I am really struggling to make sense of the question...

If you are trying to have Node act as a HTTP/2 proxy (so a client can connect via h2c to node and it passes on those details to another HTTP/2 aware server), then the way you are going about it seems... weird to say the least.

A proxy can be a Level 4 proxy (e.g. a TCP proxy), where it creates two TCP separate connections (one from client to proxy, and one from proxy to destination server) and sends the connects of those TCP packets between them without really inspecting or interfering with them, other then the TCP headers.

Alternative a proxy can be a Level 7 proxy (e.g. a HTTP proxy), where it creates two separate HTTP connections (one from client to proxy, and one from proxy to destination server) and sends HTTP messages between them, mapping the HTTP headers and details between them and sometimes changing details or even adding more headers (e.g. X-FORWARDED-FOR).

You seem to be trying to create some kind of hybrid between these two distinct and incompatible modes of working! You are hoping to create a HTTP or HTTP/2 server and then open a TCP socket and pass those TCP messages between them and hope this works? While this might possibly work over a simple protocol like HTTP/1 it is never going to work over HTTP/2!

For your first example a HTTP/1 instance is entirely different to HTTP/2. So to set these two up and expect them to work is flawed from the start. If one of your friends only spoke German and the other friend only spoke Spanish and you passed all the German messages, verbatim, unfiltered, and still in German to the Spanish speaker then would you expect the Spanish speaker to magically be able to understand them? Of course not! So you cannot connect HTTP/1 and HTTP/2 at a socket level - they are completely different protocols. You need your proxy to act like a translator between them.

In your second example I'm even more confused. I guess you are trying to create two HTTP/2 servers, and have the client connect to one, and then proxy requests over to the other? And presumably you would add some logic in there at some stage so only certain requests made it through otherwise this would be pointless. Regardless this will almost certainly not work. HTTP/2 is a complex protocol, in many ways more akin to TCP. So each packet needs to be given a unique stream id, and many other settings need to be negotiated between the two end points. So to assume that one HTTP/2 message will seamlessly translate to an identical HTTP/2 message on another HTTP/2 connection is extremely naive! Back to the language analogy, copying German messages verbatim to another German speaker who is perhaps hard of hearing but sitting closer to you, might work initially but as soon as one end fails to keep up, speaks a slightly different dialect, or asks you to repeat something they missed, the whole show comes tumbling down.

I would suggest you either want to make this a Level 4 proxy (so ignore HTTP and HTTP/2 and just use sockets) or you want this to be a HTTP proxy (in which case ingest each HTTP message, read it, and send a similar HTTP message on the downstream connection). You cannot have both.

I would also question why and if you need to do this? HTTP/2 is not universally supported, and gets most of it's gains between client and edge server (proxy in this case), so why do you feel the need to speak HTTP/2 all the way through? See this question for more details on that: HTTP/2 behind reverse proxy

Hope that helps and apologies if I have completely misunderstood your question or your intention!

Barry Pollard
  • 40,655
  • 7
  • 76
  • 92
  • There is the HTTP CONNECT method, that could allow the proxy to establish and expose a TCP connection to the server. This is a way to "abuse" the HTTP proxy to do much more than L7. – Teodor Todorov Jan 02 '19 at 20:59
  • That is true, and probably should have mentioned it, though that is not what the OP seems to want to do here (though I admit I'm still a little confused as to what exactly they do want to do!!). Also I'm not sure I'd call it "abuse" since that's the very purpose of CONNECT. – Barry Pollard Jan 03 '19 at 00:33