2

I have heard that Connection:Keep-Alive header will tell the server to keep the connection between client and server for a while to prevent the effort for each time client establish a request to server. I tried to add that to the request's header but it didn't work out as expected. The socket connection still close on each request.

Could you please help to explain why that happened? Did I missing something about Connection:Keep-Alive or did I implement it the wrong way?

Client:

const http = require("http");

const options = {
  port: 4000,
  headers: {
    connection: "keep-alive",
  },
};

function request() {
  console.log(`making a request`);

  const req = http.request(options, (res) => {
    console.log(`STATUS: ${res.statusCode}`);
    console.log(`HEADERS: ${JSON.stringify(res.headers)}`);

    res.setEncoding("utf8");
    res.on("data", (chunk) => {
      console.log(`BODY: ${chunk}`);
    });
    res.on("end", () => {
      console.log("No more data in response.");
    });
  });

  req.on("error", (e) => {
    console.error(`problem with request: ${e.message}`);
  });

  req.end();
}

setInterval(() => {
  request();
}, 3000);

Server:

const http = require("http");

const server = http.createServer((req, res) => {
  setTimeout(() => {
    res.end();
  }, 500);
});

server.on("connection", function (socket) {
  socket.id = Date.now();
  console.log(
    "A new connection was made by a client." + ` SOCKET ${socket.id}`
  );
  socket.on("end", function () {
    console.log(
      `SOCKET ${socket.id} END: other end of the socket sends a FIN packet`
    );
  });

  socket.on("timeout", function () {
    console.log(`SOCKET ${socket.id} TIMEOUT`);
  });

  socket.on("error", function (error) {
    console.log(`SOCKET ${socket.id} ERROR: ` + JSON.stringify(error));
  });

  socket.on("close", function (had_error) {
    console.log(`SOCKET ${socket.id} CLOSED. IT WAS ERROR: ` + had_error);
  });
});
server.on("clientError", (err, socket) => {
  socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
});

server.listen(4000);

And this is what I've got:

Client:

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:44 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:47 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

making a request
STATUS: 200
HEADERS: {"date":"Thu, 09 Apr 2020 13:05:50 GMT","connection":"keep-alive","content-length":"81"}
No more data in response.

and Server:

A new connection was made by a client. SOCKET 1586437543111
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437543111 END: other end of the socket sends a FIN packet
SOCKET 1586437543111 CLOSED. IT WAS ERROR: false

A new connection was made by a client. SOCKET 1586437546091
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437546091 END: other end of the socket sends a FIN packet
SOCKET 1586437546091 CLOSED. IT WAS ERROR: false

A new connection was made by a client. SOCKET 1586437549095
{ connection: 'keep-alive', host: 'localhost:1234' }
SOCKET 1586437549095 END: other end of the socket sends a FIN packet
SOCKET 1586437549095 CLOSED. IT WAS ERROR: false

NodeJS version: 10.15.0


There is one more thing makes me even more confuse is that when I use telnet localhost 1234 to make a request:

GET / HTTP/1.1
Connection: Keep-Alive
Host: localhost

Then the connection is not close, and no new connection being created as expected. Is that because telnet receive Connection: Keep-Alive and keep the connection by itself?

Toan Quoc Ho
  • 3,266
  • 1
  • 14
  • 22

2 Answers2

3

I have heard that Connection:Keep-Alive header will tell the server to keep the connection between client and server for a while to prevent the effort for each time client establish a request to server.

This is wrong. The client does not demand anything from the server, it just suggests something to the server.

Connection: keep-alive just tells the server that the client would be willing to keep the connection open in order to send more requests. And implicitly suggests that it would be nice if the server would do the same in order to reuse the existing connection for more requests. The server then can decide by its own if it keeps the connection open after the response was send or closes it immediately or closes it after some inactivity or whatever.

Of course, it is not enough that the client just sends the HTTP header (which is implicit with HTTP/1.1 anyway so no need to send it). It must actually keep the TCP connection open in order to send more requests on the same TCP connection. Your current implementation will not do this as can be seen from the servers log, i.e. the client is closing the connection first:

SOCKET 1586437549095 END: other end of the socket sends a FIN packet

In order to have real keep-alive on the client side you would use an agent which keeps the connection open through multiple http.request. See HTTP keep-alive in node.js for more.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • Thanks for your answer @Steffen Ullrich. So about the telnet, is my assumption is correct. Telnet keep connection because it saw `Connection: Keep-Alive` on the request. And it create a persistent connection and just tell the server about that? – Toan Quoc Ho Apr 09 '20 at 14:48
  • 1
    @ToanQuocHo: telnet keeps the connection open since this is what telnet does. Telnet has no idea of HTTP and thus will not interpret any data you send. It will only close the connection if you explicitly instruct it to do so or of course if the peer closes the connection. – Steffen Ullrich Apr 09 '20 at 14:52
  • I see. So about the connection mechanism it has nothing to do with `Connection: Keep-Alive` but client have to handle the mechanism first before send that information to the server right? Like use an agent to keep the connection. – Toan Quoc Ho Apr 09 '20 at 14:53
  • @ToanQuocHo: As I said: `Connection: keep-alive` just informs the server that the client would be willing to keep the connection open in order to send more requests. It does not do anything magical with the TCP connection. From the perspective of TCP this is just text without meaning. – Steffen Ullrich Apr 09 '20 at 15:00
  • Got it, Thank you @Steffen. – Toan Quoc Ho Apr 09 '20 at 15:05
2

I think websockets are a better solution than tradition http/https for your use case. The connection between client and server will stay alive on the websocket, until the client side is closed, or if you force close the connection.

Node.js websocket package

const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });

  ws.send('something');
});

Client side websockets

awrfisher
  • 196
  • 5
  • Yeah, you're right. But my experiment require client to be a HTTP client instead of websocket subscriber. And to understand more about the `Connection:keep-alive` I suppose to use http instead of websocket because websocket already handle that by itself. – Toan Quoc Ho Apr 09 '20 at 13:52
  • 1
    I see, http long polling would be what you would want then. However, with the way http works, the connection was not meant to be left open. https://dev.to/moz5691/websocket-vs-long-polling-http-412f Best of luck, – awrfisher Apr 09 '20 at 14:05