Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

CoffeeScript's basic HTTP server


May 09, 2021 CoffeeScript


Table of contents


Basic HTTP server

Problem

You want to create an HTTP server on the network. In this approach, we will gradually move from the smallest server to a function key value store.

Solution

We'll use node .js the HTTP library and create the simplest web server in Coffeescript.

Start 'hi'n'

We can start by importing .js the HTTP module. T his will include a creative server, a simple request handler that returns to the HTTP server. We can use this server to listen for TCP ports.

http = require 'http'
server = http.createServer (req, res) -> res.end 'hi\n'
server.listen 8000

To run this example, simply put it in a file and run it. Y ou can terminate it with ctrl-c. We can test it with the curl command, which can be used on most of the snix platforms:

$ curl -D - http://localhost:8000/
HTTP/1.1 200 OK
Connection: keep-alive
Transfer-Encoding: chunked

hi

What happened?

Let's give a little feedback on what's happening on the server. At this point, we can be friendly to the user and provide them with some HTTP header files.

http = require 'http'

server = http.createServer (req, res) ->
    console.log req.method, req.url
    data = 'hi\n'
    res.writeHead 200,
        'Content-Type':     'text/plain'
        'Content-Length':   data.length
    res.end data

server.listen 8000

Try accessing it again, but this time using a different URL path, such http://localhost:8000/coffee You'll see a server console like this:

$ coffee http-server.coffee 
GET /
GET /coffee
GET /user/1337

get something

What if our web server could hold some data? W e'll try to figure out a simple key value store in the element that is retrieved by the GET method request. Provides a critical path, the server will request a value returned, and if it does not exist, a 404 error.

http = require 'http'

store = # we'll use a simple object as our store
    foo:    'bar'
    coffee: 'script'

server = http.createServer (req, res) ->
    console.log req.method, req.url

    value = store[req.url[1..]]

    if not value
        res.writeHead 404
    else
        res.writeHead 200,
            'Content-Type': 'text/plain'
            'Content-Length': value.length + 1
        res.write value + '\n'

    res.end()

server.listen 8000

We can try a few urls and see how they respond:

$ curl -D - http://localhost:8000/coffee
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 7
Connection: keep-alive

script

$ curl -D - http://localhost:8000/oops
HTTP/1.1 404 Not Found
Connection: keep-alive
Transfer-Encoding: chunked

Use your header file

Text / plain is indefensible. W hat if we use application/json or text/xml? A t the same time, our store retrieval process can also be refactored with a little bit - how about throwing out some exceptions and handling them? Let's see what we can come up with:

http = require 'http'

# known mime types
[any, json, xml] = ['*/*', 'application/json', 'text/xml']

# gets a value from the db in format [value, contentType]
get = (store, key, format) ->
    value = store[key]
    throw 'Unknown key' if not value
    switch format
        when any, json then [JSON.stringify({ key: key, value: value }), json]
        when xml then ["<key>#{ key }</key>\n<value>#{ value }</value>", xml]
        else throw 'Unknown format'

store =
    foo:    'bar'
    coffee: 'script'

server = http.createServer (req, res) ->
    console.log req.method, req.url

    try
        key = req.url[1..]
        [value, contentType] = get store, key, req.headers.accept
        code = 200
    catch error
        contentType = 'text/plain'
        value = error
        code = 404

    res.writeHead code,
        'Content-Type': contentType
        'Content-Length': value.length + 1
    res.write value + '\n'
    res.end()

server.listen 8000

The server still returns a value that matches a given key and a 404 error if it does not exist. B ut it will respond in a JSON or XML structure based on the headerAccept. Take a look at it for yourself:

$ curl http://localhost:8000/
Unknown key

$ curl http://localhost:8000/coffee
{"key":"coffee","value":"script"}

$ curl -H "Accept: text/xml" http://localhost:8000/coffee
<key>coffee</key>
<value>script</value>

$ curl -H "Accept: image/png" http://localhost:8000/coffee
Unknown format

You need to get something back

Our final step is to provide the client with the ability to store data. We will keep RESTiness by listening to POST requests.

http = require 'http'

# known mime types
[any, json, xml] = ['*/*', 'application/json', 'text/xml']

# gets a value from the db in format [value, contentType]
get = (store, key, format) ->
    value = store[key]
    throw 'Unknown key' if not value
    switch format
        when any, json then [JSON.stringify({ key: key, value: value }), json]
        when xml then ["<key>#{ key }</key>\n<value>#{ value }</value>", xml]
        else throw 'Unknown format'

# puts a value in the db
put = (store, key, value) ->
    throw 'Invalid key' if not key or key is ''
    store[key] = value

store =
    foo:    'bar'
    coffee: 'script'

# helper function that responds to the client
respond = (res, code, contentType, data) ->
    res.writeHead code,
        'Content-Type': contentType
        'Content-Length': data.length
    res.write data
    res.end()

server = http.createServer (req, res) ->
    console.log req.method, req.url
    key = req.url[1..]
    contentType = 'text/plain'
    code = 404

    switch req.method
        when 'GET'
            try
                [value, contentType] = get store, key, req.headers.accept
                code = 200
            catch error
                value = error
            respond res, code, contentType, value + '\n'

        when 'POST'
            value = ''
            req.on 'data', (chunk) -> value += chunk
            req.on 'end', () ->
                try
                    put store, key, value
                    value = ''
                    code = 200
                catch error
                    value = error + '\n'
                respond res, code, contentType, value

server.listen 8000

Pay attention to how the data is received in a POST request. By attaching handlers to the events of the Data and End request objects, we were eventually able to buffer and save the data from the client.

$ curl -D - http://localhost:8000/cookie
HTTP/1.1 404 Not Found # ...
Unknown key

$ curl -D - -d "monster" http://localhost:8000/cookie
HTTP/1.1 200 OK # ...

$ curl -D - http://localhost:8000/cookie
HTTP/1.1 200 OK # ...
{"key":"cookie","value":"monster"}

Discuss

Give http.createServer a function (request, response) - I t returns a server object that we can use to listen to a port. L et the server interact with the request and response objects. Use server.listen 8000 to listen to port 8000.

For API and overall information on this issue, refer to .js https and https documentation pages. In addition, HTTP spec may come in use.

Practice

Create a layer between the server and the developer that allows the developer to do something similar:

server = layer.createServer
    'GET /': (req, res) ->
        ...
    'GET /page': (req, res) ->
        ...
    'PUT /image': (req, res) ->
        ...