Menu

Tree [f2f267] master /
 History

HTTPS access


File Date Author Commit
 contrib 2019-07-07 vincent.delft vincent.delft [a02a7e] Initial commit
 samples 2024-11-10 vincent.delft vincent.delft [4cb4eb] remove from cgi import parse_qs
 .gitmodules 2024-11-10 vincent.delft vincent.delft [de3979] Add picohttpparser as a submodule
 FAQ 2019-07-07 vincent.delft vincent.delft [a02a7e] Initial commit
 README.md 2024-11-10 vincent.delft vincent.delft [f2f267] Adapt README for git clone code instead of untar
 TODO 2024-07-08 vincent.delft vincent.delft [94244d] sample on how to implement sigint
 base.py 2019-07-07 vincent.delft vincent.delft [a02a7e] Initial commit
 fapws4.c 2024-11-10 vincent.delft vincent.delft [af7d9c] replace strcpy by more secure strlcpy, replace ...
 make.sh 2024-07-08 vincent.delft vincent.delft [727c33] Adapt LIBS based on python version

Read Me

Fapws4

Fast Asynchronous Python Web Server

Fapws4 is a re-write of fapws3, but using libraries like libuv and picohttpparser. So, the goal is to build a very fast python web server where most of the processing aspects are written in C. The developer using it, will have to simply write the logic of his web pages in Python.

Like for Fapws3, the memory foot print and disk space must be limited.

Table of Contents

Backgrouond
Pre-requisites to build Fapws4
Installing
Usage
Sample
Contributions
License

Background

I'm a user of Fapws3 since several years. Initially I was using it inside small devices where the web server is the administration console of the device. Fapws3 do this task very well because his small memory and disk foot's print. Finally, after years of usage on those devices, and because of the simplicity of Fapws3, I'm using it on many different servers. One of the it is my blog vincentdelft.be, where Fapws3 has done the job during last 3 years. Since may 2019 it's served by Fapws4.

Fapws3 is existing since 2008! And is no more evolving since 2012. Few commits in 2015 and 2017.
Since then, the eco-system around webservers has changed : libuv, http parser. And most importantly, we have now python3.

So, it's a good opportunity to give it a fresh impulse to Fapws3. Moreover, as python's developer since 2003, jumping into such C programming aspects is a very fun challenge for me.

Pre-requisites to build Fapws4

As pre-requisites, you need to have libuv and python3.x installed.

On OpenBSD machines (my daily machine), you can get them by doing:

doas pkg_add python libuv

Please select python-3.x

Installing

To install fapws4, you just have to get it via git, compile it and install it.

git clone git://git.code.sf.net/p/fapws4/code fapws4
cd fapws4
git submodule update --init --recursive
sh make.sh build
sh make.sh install

If the build succeed, you can perform the install. No need to be root !!!

Please note that you do no need root (or doas, sudo) account to install it !!!!
Indeed, fapws4 will be installed in the user's local directory: ~/.local/bin/ and in ~/.local/lib/python3.x/site-packages/fapws4 (in my case x is 6, because I'm using python 3.6)

Please make sure that ~/.local/bin is in your PATH environment variable:

export PATH=~/.local/bin:$PATH

In fact, Fapws4 is not a python module. Fapws4 is an executable which embed python.
In the next paragraph, it's explained how to use it.

Doing this facilitate management of my different development environments (DEV, Acceptance, PROD) because they all have their respective chroot and userIDs. This is for security reasons.

Usage

Since Fapws4 is an executable embedding python, you have to adapt a little bit your code (and your Fapws3 programs):

  • Your code must have a PATHS object. This must be a list of tuples. Each tuple have 2 elements: uri, python object. Like for Fapws3, Fapws4 will look for the first uri having a path starting like the one the client is requesting. Once found, we execute the associated python object. Some samples here after will show how to use it.

  • Your python object triggered by a specific uri request, must have the following layout. Such python object must return a string object, a list object, a tuple object or a file object. Here after a sample with a string object returned.

    def test(environ, start_response):
    return "Hello world"

  • Your code must import Start_response and Environ from fapws4.base. Those objects will be instantiated and used by Fapws4 as parameters in your called object

    from fapws4.base import Start_response, Environ

  • Your code can contain the object SERVER (Optional). This object must be a string/unicode object with the form <ip>:[port]. Where ip is the IP on which your web server will listen. By default it's "0.0.0.0". Port is an optional parameter specifying the port at which your web server must listen. By default it's 8888. For sure, if you want to run your Fapws4's webserver on port 80, you must start it with a root account.</ip>

  • Your code can contain the DEBUG object (optional). This object must be an integer. Currently it must be 1, 2 or 3. The default is 0 meaning no debug. This debug element is to debug libuv events. Except developers jumping into the C code of Fapws4, no one need it.

  • Your code can contain the MAX_BODY_SIZE object (Optional). This is an integer value indicating, in bytes, the maximum size of the http body, the client can send to your webserver. By default it's 10MB. If your http clients send you a file bigger than this value, Fapws4 will interrupt the connection by saying "input body too big".

Like the standard python executable code, you can use python libraries located in your usual PYTHONPATH. So, feel free to import all what you need for your web pages. In fact, the only impact is that you will run your script by using the Fapws4 binary instead of Python binary.

How to manage PATHS and environment variables

Let's imagine the following PATH:

PATHS = [('/add',addPage),
        ('/edit', edit Page),
        ('/static', staticFile,
        ('/fix/', fixElem,
        ('/', index))]

It's important to understand that Fapws4 take the first object matching the beginning of the requested URI. This explain why I've put '/' at the end of the list.

Let see now some URI examples and see to which objects they will match:

  • If the user requests "/". It does not start by "/add", neither by "/edit", neither by "/static", but well by "/". So, the index object will be triggered. All environment variables will be empty of default.

  • If the user requests "/add/page1?template=simple". In such case Fapws4 will match the 1st element because we can find "/add" is the beginning of the user's request. Thus the object addPAge will be triggered. In this case, the environment variable will contain QUERY_STRING='template=simple', PATH_INFO='/page1', SCRIPT_NAME='/edit'. As defined by Fapws3, you will also find query elements in fapws.params={'template': ['simple']}

  • If the user requests "/static/image/logo.png". In such case, Fapws4 will stop the loop on the 3rd element of PATHS. So, it will trigger the object staticFile. In such case, the environment variables will contain: PATH_INFO='/image/logo.png', SCRIPT_NAME='/static', fapws.uri=''

  • If the user requests "/fix/image/picture.png". In such case Fapws4 will match the 4rd element of PATHS as defined here above. In this case, the environment variable will be: PATH_INFO='image/picture.png', SCRIPT_NAME='/fix/'. The difference with the above example is coming from the ending "/". So be very careful with your PATHS definition

  • If the user requests "/rtfm". In such case, the only matching element in PATHS is te last one. So, in other words, by typing garbage, the user will see the home page.

Samples

The usual sample is to return a simple text "Hello World!!".

from fapws4.base import Start_response, Environ

def hello(env, resp):
    return "Hello world!!"

PATHS = [('/', hello)]

Imagine that you save this code in a file called: hello.py

To execute it, you just have to provide this code to Fapws4:

fapws4 hello.py

Via your usual browser, go to http://127.0.0.1:8888/ and you ill receive your welcome message "Hello World!!".
You can also use wget or curl command:

wget http://127.0.0.1:8888/

You can retreive this code in the folder samples/hello/.

Contributions

Side this the standard features of Fapws4, I've added some contribution copied (and slightly adapted) from Fapws3.

In headers.py, you can find "redirect". A method that send 301 or 302 HTTP response code.

In views.py, you can find Staticfile. A class allowing you to easily add the management of static files to your python's webserver.

License

This code is written by me: Vincnt Delft: vincent.delft@gmail.com

Since is heavily inspired by Fapws3 having a GPL2 license, Fapws4 is using the same license model.

I would prefer a more BSD like license, but I'm not an expert on that legal aspects. IF there is a possibility to have a BSD license model, please explain it me.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.