How to do it...

We'll provision our server for both GET and POST requests.

Let's start with GET requests, by requiring the core http module and loading form.html into memory, which we'll then serve via http.createServer:

const http = require('http') 
const fs = require('fs')
const path = require('path')
const form = fs.readFileSync(path.join(__dirname, 'public', 'form.html'))

http.createServer((req, res) => {
if (req.method === 'GET') {
get(res)
return
}
reject(405, 'Method Not Allowed', res)
}).listen(8080)

function get (res) {
res.writeHead(200, {'Content-Type': 'text/html'})
res.end(form)
}

function reject (code, msg, res) {
res.statusCode = code
res.end(msg)
}

We are synchronously loading form.html at initialization time instead of accessing the disk on each request. When creating servers in Node, initialization is the only time when it's a good idea to perform synchronous I/O.

If we navigate to http://localhost:8080, we'll be presented with a form.

But if we fill out the form and submit, we'll encounter a Method Not Allowed response. This is because the method attribute on our HTML form is set to POST. If the method is anything other than GET, our request handler (the function passed to http.createServer) will fall through to calling the reject function which sets the relevant status code and sends the supplied message via the res object.

Our next step is to implement POST request handling.

First we'll add the querystring module to our list of required dependencies at the top of the file. The top section of our server.js file should become:

const http = require('http') 
const fs = require('fs')
const path = require('path')
const form = fs.readFileSync(path.join(__dirname, 'public', 'form.html'))
const qs = require('querystring')

For safety, we'll want to define a maximum request size, which we'll use to guard against payload size based DoS attacks:

const maxData = 2 * 1024 * 1024 // 2mb 

Now we'll add a check for POST methods in the request handler, so our http.createServer calls looks like so:

http.createServer((req, res) => { 
if (req.method === 'GET') {
get(res)
return
}
if (req.method === 'POST') {
post(req, res)
return
}
reject(405, 'Method Not Allowed', res)
}).listen(8080)

Next, let's implement the post function that's called within the request handler:

function post (req, res) { 
if (
req.headers['content-type'] !== 'application/x-www-form-urlencoded'
) {
reject(415, 'Unsupported Media Type', res)
return
}
const size = parseInt(req.headers['content-length'], 10)
if (isNaN(size)) {
reject(400, 'Bad Request', res)
return
}
if (size > maxData) {
reject(413, 'Too Large', res)
return
}

const buffer = Buffer.allocUnsafe(size)
var pos = 0

req
.on('data', (chunk) => {
const offset = pos + chunk.length
if (offset > size) {
reject(413, 'Too Large', res)
return
}
chunk.copy(buffer, pos)
pos = offset
})
.on('end', () => {
if (pos !== size) {
reject(400, 'Bad Request', res)
return
}
const data = qs.parse(buffer.toString())
console.log('User Posted: ', data)
res.end('You Posted: ' + JSON.stringify(data))
})
}

Notice how we check the Content-Type and Content-Size headers sent by the browser. In particular, Content-Size is validated at several check-points; this is important for preventing various types of attack, from DoS attacks to leaking deallocated memory.

Once the form has been completed and submitted, the browser and terminal should present the data provided via the form.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset