Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Care-free web app prototyping using files and scripts

Gem Version Build Status Coverage Status Code Climate


Oaf provides stupid-easy way of creating dynamic web applications by setting all best practices and security considerations aside until you are sure that you want to invest your time doing so.

Oaf was created as a weekend project to create a small, simple HTTP server program that uses script execution as its primary mechanism for generating responses.


Create an executable file named hello.GET:

echo "Hello, ${USER}!"

Start the server by running the oaf command, then make a request:

$ curl localhost:9000/hello
Hello, ryanuber!


Oaf is available on rubygems, which means you can do:

gem install oaf

Basic Usage

  oaf [options] [path]

    -p, --port PORT                  Listening port. Default=9000
        --default-response FILE      Path to default response file
        --version                    Show the version number
    -h, --help                       Show this message

Accepted files

Oaf will run ANY file with the executable bit set, be it shell, Python, Ruby, compiled binary, or whatever else you might have.

Oaf can also use plain text files.

How file permissions affect output

  • If the file in your request is executable, the output of its execution will be used as the return data.
  • If the file is NOT executable, then the contents of the file will be used as the return data.

Nested methods

You can create nested methods using simple directories. Example:

$ ls ./examples/

$ curl http://localhost:8000/examples/hello
Hello, world!

HTTP Methods

Files must carry the extension of the HTTP method used to invoke them. Oaf should support any HTTP method, including custom methods.

Headers and Status

You can indicate HTTP headers and status using stdout from your script.

cat <<EOF
Hello, world!
content-type: text/plain

Separated by 3 dashes on a line of their own (---), the very last block of output can contain headers and response status.

Getting request headers, query parameters, and body

Headers, query parameters, and request body are all passed to executables using the environment. To defeat overlap in variables, they are namespaced using a prefix. The environment variables are as follows:

  • Headers: oaf_header_*
  • Request parameters: oaf_param_*
  • Request body: oaf_request_body
  • Request path: oaf_request_path

Below is a quick example of a shell script which makes use of the request data:

echo "You passed the Accept header: $oaf_header_accept"
echo "You passed the 'message' value: $oaf_param_message"
echo "You passed the request body: $oaf_request_body"
echo "This method is located at: $oaf_request_path"

Headers query parameter names are converted to all-lowercase, and dashes are replaced with underscores. This is due to the way the environment works. For example, if you wanted to get at the Content-Type header, you could with the environment variable $oaf_header_content_type.

Catch-all methods

Catch-all's can be defined by naming a file inside of a directory, beginning and ending with underscores (_). So for example, test/_default_.GET will match: GET /test/anything, GET /test/blah, etc.

Request handling priority

Direct file matches are always prioritized over catch-all methods. Take the following directory structure as an example with the request for /hello:

├── hello
│   └── _default_.GET
└── hello.GET

In this case, the file hello.GET will be executed during a request to /hello. If hello.GET did not exist, then hello/_default_.GET would be executed instead. If neither existed, then the default response would be returned.

Default response file

If no files match a given request, Oaf must fall back to a default response to let the end user know that nothing was found. This is typically a 404 error page. Oaf gives you flexibility here by providing a --default-response command line option. The value of this argument should be the path to a file, which can be a script or flat text file, just like any other Oaf script, and supports the same format and backmatter to specify HTTP response headers, status code, etc.


Why are the headers and status at the bottom of the response? Because it is much easier to echo these last. Since Oaf reads response data directly from stdout/stderr, it is very easy for debug or error messages to interfere with them. By specifying the headers and status last, we minimize the chances of unexpected output damaging the response, as all processing is already complete.

Why the name Oaf? It's a bit of a clumsy and "oafish" approach at web application prototyping. I constantly find myself trying to write server parts of programs before I have even completed basic functionality, and sometimes even before I have a clear idea of what it is I want the program to do.


Oaf is inspired by Stubb. A number of ideas and conventions were borrowed from it. Kudos to @knuton for having a great idea.


Care-free web app prototyping using files and scripts







No packages published