0

Take a look at the following http echo server example: https://repl.it/repls/SlipperyQuickwittedEvent

const http = require('http')

const PORT = 6000
const server = http.createServer((req, res) => {
  req.pipe(res)
})
server.listen(PORT)
server.on('listening', () => {
  const req = http.request(
    {
      port: PORT
    },
    res => {
      console.log(res.statusCode)
      res.pipe(process.stdout)
      res.on('end', () => {
        server.close()
      })
    }
  )
  req.end('abc')
})

I expect it to print 200 and abc on the console. Instead, it prints 400 and nothing else.

What am I missing?

Samuel
  • 1,271
  • 1
  • 15
  • 29
  • What exactly are you attempting to accomplish? Set up a web server and ping it once it's listening? – esqew Oct 16 '18 at 02:11
  • I am happy the code is pretty self-explanatory. Yes, once the server is listening, send some data to it through a request and expect the returned data to be the same as the one sent. – Samuel Oct 16 '18 at 02:19

2 Answers2

1

The default HTTP request method for request is GET. The http server will return 400 Bad Request if data is sent on with a GET request.

I wonder if there is a way of realizing this by debugging. I tried hooking to 'error' events on server and all req and res with no success.

Samuel
  • 1,271
  • 1
  • 15
  • 29
1

Believe it or not, it appears your confusion stems from a fundamental misunderstanding regarding HTTP GET requests.

It seems that Node's built in http conforms to the HTTP/1.1 spec, section 9.3 which boils down to the fact that GET requests should not contain a message body, and whatever is sent along in the body of said GET request should be disregarded. (SO Source)

Edit: This behavior has been documented as an issue on the official GitHub repository for NodeJS.

In the thread, it was noted that the underlying HTTP engine will still accept and parse HTTP GET requests that have a proper Content-Length header associated. Since you haven't accounted for that with your current code, the server will send a 400 Bad Request response code because, as far as the server is concerned, sending a request body along with a GET request without a Content-Length header is, in fact, a malformed request.

Change your http.request constructor to send the Content-Length header with the GET request, and the underlying engine will accept and parse the request body with the request.

const http = require('http');

const PORT = 6000;
const server = http.createServer((req, res) => {
    req.pipe(res);
});
server.on('clientError', (err, socket) => {
  console.log(err);
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(PORT);

const responseString = "abc";

server.on('listening', () => {
    const req = http.request(
        {
            port: PORT,
            headers: {
              'Content-Length': responseString.length
            }
        },
        res => {
            console.log(res.statusCode);
            res.pipe(process.stdout);
            res.on('end', () => {
                server.close();
            });
        }
    );
    req.end(responseString);
});

Repl.it fork


To address your comment regarding capturing this during debugging, you could use this handler (straight from the documentation, with minimal modification to add console logging of the error object) to get a bit more information about the reasoning behind the 400 Bad Request response:

server.on('clientError', (err, socket) => {
  console.log(err);
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

The console will then provide a bit more context surrounding the 400 response:

node v9.7.1 linux/amd64

{ Error: Parse Error
  bytesParsed: 59,
  code: 'HPE_INVALID_METHOD',
  rawPacket: <Buffer 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 3a 36 30 30 30 0d 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 ... > }
400
Community
  • 1
  • 1
esqew
  • 42,425
  • 27
  • 92
  • 132