Library to provide HTTP over STDIN/STDOUT
Project description
[![Build Status](https://travis-ci.org/denismakogon/hotfn-py.svg?branch=master)](https://travis-ci.org/denismakogon/hotfn-py)
HTTP over STDIN/STDOUT lib parser
=================================
Purpose of this library to provide simple interface to parse HTTP 1.1 requests represented as string
Raw HTTP request
----------------
Parses raw HTTP request that contains:
- method
- route + query
- headers
- protocol version
Optionally:
- data
Raw HTTP request may have next look:
GET /v1/apps?something=something&etc=etc HTTP/1.1
Host: localhost:8080
Content-Length: 5
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.51.0
hello
Each new line define by set of special characters:
\n
\r
and combination is:
\r\n
This type of class stands for HTTP request parsing to a sane structure of:
- HTTP request method
- HTTP request URL
- HTTP request query string represented as map
- HTTP request headers represented as map
- HTTP protocol version represented as tuple of major and minor versions
- HTTP request body
```python
import os
import sys
from hotfn.http import request
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
```
Raw HTTP response
-----------------
This type of class stands for transforming HTTP request object into valid string representation
```python
import sys
import os
from hotfn.http import request
from hotfn.http import response
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
resp = response.RawResponse((major, minor), 200, "OK", response_data=body)
with os.fdopen(sys.stdout.fileno(), 'wb') as stdout:
resp.dump(stdout)
```
Example
-------
Assume we have HTTP 1.1 request:
```bash
GET /v1/apps?something=something&etc=etc HTTP/1.1
Host: localhost:8080
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.51.0
hello:hello
```
This request can be transformed into data structure described above.
Using code snippet mentioned above request data can be used to assemble a response object of the following view:
```bash
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
hello:hello
```
This is totally valid HTTP response object.
Notes
-----
Please be aware that response object by default sets content type as `text/plain; charset=utf-8`. If you need to change it use following code:
```python
import os
import sys
from hotfn.http import request
from hotfn.http import response
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
resp = response.RawResponse((major, minor), 200, "OK", response_data=body)
resp.headers["Content-Type"] = "application/json"
with os.fdopen(sys.stdout.fileno(), 'wb') as stdout:
resp.dump(stdout)
```
Handling Hot Functions
----------------------
A main loop is supplied that can repeatedly call a user function with a series of HTTP requests.
(TODO: should this use the WSGI API?)
In order to utilise this, you can write your `app.py` as follows:
```python
from hotfn.http import worker
from hotfn.http import response
def app(context, **kwargs):
body = kwargs.get('data')
return response.RawResponse(context.version, 200, "OK", body.readall())
if __name__ == "__main__":
worker.run(app)
```
Automatic input coercions
-------------------------
Decorators are provided that will attempt to coerce input values to Python types.
Some attempt is made to coerce return values from these functions also:
```python
from hotfn.http import worker
@worker.coerce_input_to_content_type
def app(context, **kwargs):
"""
body is a request body, it's type depends on content type
"""
return kwargs.get('data')
if __name__ == "__main__":
worker.run(app)
```
Working with async automatic input coercions
--------------------------------------------
Latest version (from 0.0.6) supports async coroutines as a request body processors:
```python
import asyncio
from hotfn.http import worker
from hotfn.http import response
@worker.coerce_input_to_content_type
async def app(context, **kwargs):
headers = {
"Content-Type": "plain/text",
}
return response.RawResponse(
context.version, 200, "OK",
http_headers=headers,
response_data="OK")
if __name__ == "__main__":
loop = asyncio.get_event_loop()
worker.run(app, loop=loop)
```
As you can see `app` function is no longer callable, because its type: coroutine, so we need to bypass event loop inside
HTTP over STDIN/STDOUT lib parser
=================================
Purpose of this library to provide simple interface to parse HTTP 1.1 requests represented as string
Raw HTTP request
----------------
Parses raw HTTP request that contains:
- method
- route + query
- headers
- protocol version
Optionally:
- data
Raw HTTP request may have next look:
GET /v1/apps?something=something&etc=etc HTTP/1.1
Host: localhost:8080
Content-Length: 5
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.51.0
hello
Each new line define by set of special characters:
\n
\r
and combination is:
\r\n
This type of class stands for HTTP request parsing to a sane structure of:
- HTTP request method
- HTTP request URL
- HTTP request query string represented as map
- HTTP request headers represented as map
- HTTP protocol version represented as tuple of major and minor versions
- HTTP request body
```python
import os
import sys
from hotfn.http import request
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
```
Raw HTTP response
-----------------
This type of class stands for transforming HTTP request object into valid string representation
```python
import sys
import os
from hotfn.http import request
from hotfn.http import response
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
resp = response.RawResponse((major, minor), 200, "OK", response_data=body)
with os.fdopen(sys.stdout.fileno(), 'wb') as stdout:
resp.dump(stdout)
```
Example
-------
Assume we have HTTP 1.1 request:
```bash
GET /v1/apps?something=something&etc=etc HTTP/1.1
Host: localhost:8080
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
User-Agent: curl/7.51.0
hello:hello
```
This request can be transformed into data structure described above.
Using code snippet mentioned above request data can be used to assemble a response object of the following view:
```bash
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain; charset=utf-8
hello:hello
```
This is totally valid HTTP response object.
Notes
-----
Please be aware that response object by default sets content type as `text/plain; charset=utf-8`. If you need to change it use following code:
```python
import os
import sys
from hotfn.http import request
from hotfn.http import response
with os.fdopen(sys.stdin.fileno(), 'rb') as stdin:
req = request.RawRequest(stdin)
method, url, query_parameters, headers, (major, minor), body = req.parse_raw_request()
resp = response.RawResponse((major, minor), 200, "OK", response_data=body)
resp.headers["Content-Type"] = "application/json"
with os.fdopen(sys.stdout.fileno(), 'wb') as stdout:
resp.dump(stdout)
```
Handling Hot Functions
----------------------
A main loop is supplied that can repeatedly call a user function with a series of HTTP requests.
(TODO: should this use the WSGI API?)
In order to utilise this, you can write your `app.py` as follows:
```python
from hotfn.http import worker
from hotfn.http import response
def app(context, **kwargs):
body = kwargs.get('data')
return response.RawResponse(context.version, 200, "OK", body.readall())
if __name__ == "__main__":
worker.run(app)
```
Automatic input coercions
-------------------------
Decorators are provided that will attempt to coerce input values to Python types.
Some attempt is made to coerce return values from these functions also:
```python
from hotfn.http import worker
@worker.coerce_input_to_content_type
def app(context, **kwargs):
"""
body is a request body, it's type depends on content type
"""
return kwargs.get('data')
if __name__ == "__main__":
worker.run(app)
```
Working with async automatic input coercions
--------------------------------------------
Latest version (from 0.0.6) supports async coroutines as a request body processors:
```python
import asyncio
from hotfn.http import worker
from hotfn.http import response
@worker.coerce_input_to_content_type
async def app(context, **kwargs):
headers = {
"Content-Type": "plain/text",
}
return response.RawResponse(
context.version, 200, "OK",
http_headers=headers,
response_data="OK")
if __name__ == "__main__":
loop = asyncio.get_event_loop()
worker.run(app, loop=loop)
```
As you can see `app` function is no longer callable, because its type: coroutine, so we need to bypass event loop inside
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
hotfn-0.0.6.dev47.tar.gz
(15.6 kB
view details)
File details
Details for the file hotfn-0.0.6.dev47.tar.gz
.
File metadata
- Download URL: hotfn-0.0.6.dev47.tar.gz
- Upload date:
- Size: 15.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 217495723f00c56b9a554255a25bce5326e145cc1fa1187dc2be91e406ce48e9 |
|
MD5 | 469aa713c6b06d795f65c6e54b9a91a6 |
|
BLAKE2b-256 | 4e98fe482a24696d3f8a44b93ff0a168fad9a9283742fe5eb2cce41426d3b8cf |