Tiny Web Server

Author: Carl Sassenrath
Return to REBOL Cookbook

Here is a long time favorite REBOL program. This is a tiny little web server that takes only one page of code. This tiny server is handy if you quickly need an HTTP web site on Windows, Linux, BSD, and others, but don't really need anything too fancy (like a big httpd server such as Apache).

How good is it? It's good enough to serve the HTML pages and graphics from the REBOL.com web site. Will it do everything? No. It's just one page. It makes a good example, but here is a link to a five page REBOL web server by Cal Dixon that includes many other features, including CGI scripting and a simple form of active server pages (ASP).

Here is the entire code for the tiny web server. I'll explain each part below. To run the tiny web server, just copy this code to a file and run it using any version of REBOL (Core 2.5.6 or better is recommended). Put it in the same directory as your web files, or change the web-dir variable to tell it where the files are.

(Note that the code below can also be found on www.rebol.org under the title Micro Web Server.)

    REBOL [Title: "Tiny Web Server"]

    web-dir: %.   ; the path to where you store your web files

    listen-port: open/lines tcp://:80  ; port used for web connections

    errors: [
        400 "Forbidden" "No permission to access:"
        404 "Not Found" "File was not found:"

    send-error: function [err-num file] [err] [
        err: find errors err-num
        insert http-port join "HTTP/1.0 " [
            err-num " " err/2 "^/Content-type: text/html^/^/" 
            <HTML> <TITLE> err/2 </TITLE>
            "<BODY><H1>SERVER-ERROR</H1><P>REBOL Webserver Error:"
            err/3 " " file newline <P> </BODY> </HTML>

    send-page: func [data mime] [
        insert data rejoin ["HTTP/1.0 200 OK^/Content-type: " mime "^/^/"]
        write-io http-port data length? data

    buffer: make string! 1024  ; will auto-expand if needed

    forever [
        http-port: first wait listen-port
        clear buffer
        while [not empty? request: first http-port][
            repend buffer [request newline]
        repend buffer ["Address: " http-port/host newline] 
        print buffer
        file: "index.html"
        mime: "text/plain"
        parse buffer ["get" ["http" | "/ " | copy file to " "]]
        parse file [thru "." [
                "html" (mime: "text/html") |
                "gif"  (mime: "image/gif") |
                "jpg"  (mime: "image/jpeg")
        any [
            if not exists? web-dir/:file [send-error 404 file]
            if error? try [data: read/binary web-dir/:file] [send-error 400 file]
            send-page data mime
        close http-port

The web-dir variable holds the file path to your tiny web server pages. By default it uses the current directory (the dot means current directory), but you can set it to other locations if you wish.

The listen-port line opens a TCP port for web (HTTP) connections. This is the port where web browsers connect. Port 80 is the default for HTTP. If you are using port 80 for something else, you can use other ports. Some people like using 8000 or 8080.

The errors block is a list of HTTP error numbers and messages that the server will return. You can add more types of errors if you need them. But, be sure to follow the HTTP standard.

The send-error function looks up the error message for a given error number then generates an error page to send back to the browser. You can edit this function to customize your own error message.

The send-page function sends a page or other data (such as images) back to the browser on each request.

The buffer holds the request information which is printed out as connections are made.

The forever loop processes each HTTP request from a web browser. The first step is to wait for a connection on the listen-port. When a connection is made, the http-port variable is set to the TCP port connection and is then used to get the HTTP request from the browser and send the result back to the browser.

The while loop gathers the browser's request, a line at a time. The host name of the client (the browser computer) is added to the buffer string. It is just for your own information. If you want, you could use the remote-ip address instead of the host name.

The default file and default MIME type are specified in the file and mime variables.

Next, the parse function parses the HTTP header and copies the requested file name to a variable. This is a very simple method, but it will work fine for simple web server requests.

The next parse takes the file's suffix and uses it to lookup the MIME type for the file. This is returned to the web browser to tell it what to do with the data. For example, if the file is foo.html, then a text/html MIME type is returned. You can add other MIME types to this list.

The any block is used to check that the requested file exists, read the file and send it to the browser using the SEND-PAGE function described earlier. If an error occurs, the SEND-ERROR function is called to send the error back to the browser.

The close http-port makes sure that the connection from the browser is closed, now that the requested web data has been returned.

I've tested this tiny web server with the entire content of the www.REBOL.com web site, and it does great. It's quite fast.

Need a few more features like CGI processing? Then be sure to check out Cal Dixon's five page REBOL web server. Not as tiny, but it's nicely done and quite powerful.

2006 REBOL Technologies REBOL.com REBOL.net