Node.js HTTP rest server side by side with UDP on same port

We are going to build service which will be able to respond to both HTTP requests and UDP messages comming to the same port.

UDP might be used to broadcast messages which might be used to service discovery like things if all services are running in same network.

server.js

const http = require('http')
const url = require('url')
const dgram = require('dgram')
const { StringDecoder } = require('string_decoder')

const PORT = process.env.PORT || 3000

// http/tcp server
const server = http.createServer((req, res) => {
  const method = req.method.toLowerCase()
  const parsed = url.parse(req.url, true)
  const path = parsed.pathname.replace(/^\/+|\/+$/g, '')
  const query = parsed.query
  const headers = req.headers

  const decoder = new StringDecoder('utf-8')
  let body = ''
  req.on('data', (data) => (body += decoder.write(data)))
  req.on('end', () => {
    body += decoder.end()

    console.log({
      kind: 'HTTP_REQUEST',
      method,
      path,
      query,
      headers,
      body
    })

    // TODO: process request

    res.setHeader('Content-Type', 'application/json; charset=utf-8')
    res.writeHead(200)
    res.end(JSON.stringify({ success: true, message: 'ok' }))
  })
})

// udp server
const socket = dgram.createSocket(
  {
    type: 'udp4',
    reuseAddr: true // <- NOTE: we are asking OS to let us reuse port
  },
  (buffer, sender) => {
    const message = buffer.toString()
    console.log({
      kind: 'UDP_MESSAGE',
      message,
      sender
    })

    // demo: respond to sender
    socket.send(message.toUpperCase(), sender.port, sender.address, (error) => {
      if (error) {
        console.error(error)
      } else {
        console.log({
          kind: 'RESPOND',
          message: message.toUpperCase(),
          sender
        })
      }
    })
  }
)

// POI: bind two servers to same port
server.listen(PORT)
socket.bind(PORT)

And here is simple client which will send broadcast udp message to whole network and print response if any, after that it closes socket and exits.

client.js

const dgram = require('dgram')
const socket = dgram.createSocket('udp4')

const PORT = process.env.PORT || 3000

socket.bind()
socket.on('listening', () => {
  socket.setBroadcast(true)

  // 255.255.255.255 - boradcast for local network - RFC922
  socket.send('hi', PORT, '255.255.255.255', (err) => {
    console.log(err ? err : 'Sended')
    // socket.close();
  })

  socket.on('message', (buffer, sender) => {
    const message = buffer.toString()
    console.log(`Received message: ${message} from ${sender.address}`)
    socket.close()
  })
})

Now is you start server and try to run following:

$ node client.js
Sended
Received message: HI from 192.168.105.108
$ curl -X POST http://localhost:6000/foo?acme=42 -d '{"foo":"bar"}'
{"success":true,"message":"ok"}

On a server console you will see:

{ kind: 'UDP_MESSAGE',
  message: 'hi',
  sender:
   { address: '192.168.105.108',
     family: 'IPv4',
     port: 56939,
     size: 2 } }
{ kind: 'RESPOND',
  message: 'HI',
  sender:
   { address: '192.168.105.108',
     family: 'IPv4',
     port: 56939,
     size: 2 } }
{ kind: 'HTTP_REQUEST',
  method: 'post',
  path: 'foo',
  query: { acme: '42' },
  headers:
   { host: 'localhost:3000',
     'user-agent': 'curl/7.54.0',
     accept: '*/*',
     'content-length': '13',
     'content-type': 'application/x-www-form-urlencoded' },
  body: '{"foo":"bar"}' }