Simple HTTP Server Example with Cellframe Node
The http.simple
module provides classes and functions for implementing a simple HTTP server and handling HTTP requests to Cellframe Node. Http.simple documentation.
Prerequisites
To allow Cellframe Node to accept HTTP-requests, you need to enable a HTTP-server in the config file. Set enabled=true
and specify listen_port_tcp
and listen_address
values under [[Cellframe Node General Config#Section [notify_server]|notify_server]]
Overview
Example implements GET and POST HTTP requests to local Cellframe Node server upon starting up (self-requests) based on standard python urllib only for testing.
Warning
You should never use blocking and time-heavy methods in plugins
init()
function
post_request(url, data)
: Sends a POST request to the specified URL with the provided data and returns the decoded response.get_request(url)
: Sends a GET request to the specified URL and returns the decoded response.
Handler Functions
The main payload of the example defines handler functions for processing HTTP requests from methods above:
handler_example(request: CFSimpleHTTPRequest)
: parses the request data and returns it in a different format.another_handler_example(request: CFSimpleHTTPRequest)
: another handler function demonstrating the usage of a decorator for registration.
Initialization and Making Requests
In the init()
method we perform all steps, set up the necessary configurations, register handler functions, and send test requests.
Note
To register handler functions, you can use either a decorator
CFSimpleHTTPServer.handler()
or theregister_uri_handler()
method of theCFSimpleHTTPServer
instance.
This example provides a simple yet effective way to implement an HTTP server and handle HTTP requests within the Cellframe framework.
from DAP.Core import logIt
from pycfhelpers.node.http.simple import (CFSimpleHTTPRequestHandler,
CFSimpleHTTPServer,
CFSimpleHTTPRequest)
from pycfhelpers.node.logging import CFLog
from urllib.parse import urlencode
from urllib.request import Request, urlopen
# You have to set up a configuration settings
# if you want Cellframe Node to accept requests.
# Go to the server part and set enabled=true.
# To send requests you also have to set up
# listen_port_tcp and listen_address (from [notify_server]) values.
URI_1 = "/example"
URI_2 = "/another"
LISTEN_PORT_TCP = "8079" # default value from config file.
LISTEN_ADDRESS = "127.0.0.1" # default value from config file.
# Create function to send request with
# method POST and get a decoded response.
def post_request(url, data):
""" Send a POST request.
Sends data to the specified address.
Args:
url (str): the URL for sending the request.
data (dict): data to send.
Returns:
response_body (str): response to the received request.
"""
# Convert the data dictionary to a URL encoding string
data = urlencode(data)
# Encode into a byte object using UTF-8 encoding.
data = data.encode("utf-8")
# Create a request object to send a POST request
# to the specified URL with encoded data.
request = Request(url, data, method="POST")
# Open a connection to the server,
# send a request, and receive a response.
with urlopen(request) as response:
response_body = response.read().decode('utf-8')
return response_body
# Create function to send request with
# method GET and get a decoded response.
def get_request(url):
"""Send a GET request to the specified address.
Args:
url (str): the URL for sending the request.
Returns:
response_body (str): response to the received request.
"""
# Open a connection to the server by using
# specified URL and receive a response.
with urlopen(url) as response:
response_body = response.read().decode('utf-8')
return response_body
# Create a handler function
def handler_example(request: CFSimpleHTTPRequest) -> tuple[bytes, int]:
"""The request handler function.
Parses the request data and returns it in a different format.
Args:
request (CFSimpleHTTPRequest): Request.
Returns:
tuple[bytes, int]: contains the response_body and http status code
"""
# Get the necessary data from the request object.
url = request.url
method = request.method
body = request.body
client_address = request.client_address
# Initialize the variable to form the response body.
response_body = "The request has been processed by handler"
response_body += " registered via register_uri_handler function"
# Add the request data to the response body
response_body += f"\nurl = {url}"
response_body += f"\nmethod = {method}"
response_body += f"\nbody = [{body}]"
response_body += f"\nclient_address = {client_address}"
response_body += f"\nquery = [{request.query}]"
# encode the response body in bytes.
response_body = response_body.encode("utf-8")
# Return the response body and HTTP status code.
return response_body, 200
# In http.simple module there are two options for registration
# handler functions and their binding to a specific URi.
# The first way to register hadler is to use a decorator
# CFSimpleHTTPServer.handler(). In this example it is registered
# only to the request with "GET" method, therefore it is not passed
# to the arguments. The "uri" is a required argument.
# Below, under the decorator, describe what the handler function will do.
@CFSimpleHTTPServer.handler(uri=URI_2, methods=["GET"])
def another_handler_example(request: CFSimpleHTTPRequest) -> tuple[bytes, int]:
"""Another request handler function.
Args:
request (CFSimpleHTTPRequest): Request.
Returns:
tuple[bytes, int]: contains the response_body and http status code
"""
# Get the necessary data from the request object.
method = request.method
# Initialize the variable to form the response body.
response_body = "The request has been processed by handler"
response_body += " created via wrapper function"
response_body += f"\nmethod = {method}"
# encode the response body in bytes.
response_body = response_body.encode("utf-8")
# Return the response body and HTTP status code.
return response_body, 200
def init():
# The second way is to register via register_uri_handler method
# of the CFSimpleHTTPServer instance.
# First, create an instance of the CFSimpleHTTPRequestHandler.
# The "methods" is an optional argument that's default to "GET",
# but in this example it is registered for tho methods.
# In the argument "handler" pass the handler function.
handler = CFSimpleHTTPRequestHandler(methods=["GET", "POST"],
handler=handler_example)
CFSimpleHTTPServer().register_uri_handler(uri=URI_1,
handler=handler)
# IMPORTANT!
# If you register a handler function through a decorator,
# do it outside the "init" function. If register via register_uri_handler
# method, do it inside the "init" function.
# Create an instanse of CFLog to see the result.
log = CFLog()
# Create the url paths.
url_1 = "http://" + LISTEN_ADDRESS + ":" + LISTEN_PORT_TCP + URI_1
url_2 = "http://" + LISTEN_ADDRESS + ":" + LISTEN_PORT_TCP + URI_2
# Create some data.
data = {'Hello,': 'world!'}
# Get the responses from the functions that send requests.
# Log their answers.
response_get = get_request(url_1)
log.notice(response_get)
response_post = post_request(url_1, data)
log.notice(response_post)
another_response_get = get_request(url_2)
log.notice(another_response_get)
return 0
# Start or restart the Cellframe Node check the log.