HTTP Service
The HTTP service is a basic servlet container, dispatching HTTP requests to the handler registered for the given path. A servlet can be a simple class or a component, registered programmatically to the HTTP service, or a service registered in the Pelix framework and automatically registered by the HTTP service.
Note
Even if it borrows the concept of servlets from Java, the Pelix HTTP service doesn’t follow the OSGi specification. The latter inherits a lot from the existing Java APIs, while this is an uncommon way to work in Python.
The basic implementation of the HTTP service is defined in pelix.http.basic
.
It is based on the HTTP server available in the standard Python library
(see http.server).
Future implementations might appear in the future Pelix implementations, based
on more robust requests handlers.
Configuration properties
All implementations of the HTTP service must support the following property:
Property |
Default |
Description |
---|---|---|
pelix.http.address |
0.0.0.0 |
The address the HTTP server is bound to |
pelix.http.port |
8080 |
The port the HTTP server is bound to |
Instantiation
The HTTP bundle defines a component factory which name is
implementation-dependent.
The HTTP service factory provided by Pelix/iPOPO is
pelix.http.service.basic.factory
.
Here is a snippet that starts a HTTP server component, named http-server
,
which only accepts local clients on port 9000:
from pelix.framework import FrameworkFactory
from pelix.ipopo.constants import use_ipopo
# Start the framework
framework = FrameworkFactory.get_framework()
framework.start()
context = framework.get_bundle_context()
# Install & start iPOPO
context.install_bundle('pelix.ipopo.core').start()
# Install & start the basic HTTP service
context.install_bundle('pelix.http.basic').start()
# Instantiate a HTTP service component
with use_ipopo(context) as ipopo:
ipopo.instantiate(
'pelix.http.service.basic.factory', 'http-server',
{'pelix.http.address': 'localhost',
'pelix.http.port': 9000})
This code starts an HTTP server which will be listening on port 9000 and the HTTP service will be ready to handle requests. As no servlet service has been registered, the server will only return 404 errors.
API
HTTP service
The HTTP service provides the following interface:
- class pelix.http.HTTPService(*args, **kwargs)
HTTP service interface
- get_access() Tuple[str, int]
Retrieves the (address, port) tuple to access the server
- get_hostname() str
Retrieves the server host name
- Returns:
The server host name
- get_registered_paths() List[str]
Returns the paths registered by servlets
- Returns:
The paths registered by servlets (sorted list)
- get_servlet(path: str | None) Tuple[Servlet, Dict[str, Any], str] | None
Retrieves the servlet matching the given path and its parameters. Returns None if no servlet matches the given path.
- Parameters:
path – A request URI
- Returns:
A tuple (servlet, parameters, prefix) or None
- is_https() bool
Returns True if this is an HTTPS server
- Returns:
True if this server uses SSL
- register_servlet(path: str, servlet: Servlet, parameters: Dict[str, Any] | None = None) bool
Registers a servlet
- Parameters:
path – Path handled by this servlet
servlet – The servlet instance
parameters – The parameters associated to this path
- Returns:
True if the servlet has been registered, False if it refused the binding.
- Raises:
ValueError – Invalid path or handler
The service also provides two utility methods to ease the display of error pages:
- class pelix.http.HTTPService(*args, **kwargs)
HTTP service interface
- make_exception_page(path: str, stack: str) str
Prepares a page printing an exception stack trace in a 500 error
- Parameters:
path – Request path
stack – Exception stack trace
- Returns:
A HTML page
- make_not_found_page(path: str) str
Prepares a “page not found” page for a 404 error
- Parameters:
path – Request path
- Returns:
A HTML page
Servlet service
To use the whiteboard pattern, a servlet can be registered as a service
providing the pelix.http.servlet
specification.
It must also have a valid pelix.http.path
property, or it will be ignored.
The binding methods described below have a parameters
argument, which
represents a set of properties of the server, given as a dictionary.
Some parameters can also be given when using the
register_servlet()
method, with the parameters
argument.
In any case, the following entries must be set by all implementations of the HTTP service and can’t be overridden when register a servlet. Note that their content and liability is implementation-dependent:
http.address
: the binding address (str) of the HTTP server;http.port
: the real listening port (int) of the HTTP server;http.https
: a boolean flag indicating if the server is listening to HTTP (False) or HTTPS (True) requests;http.name
: the name (str) of the server. If the server is an iPOPO component, it should be the instance name;http.extra
: an implementation dependent set of properties.
A servlet for the Pelix HTTP service has the following methods:
- class pelix.http.Servlet
These are the methods that the HTTP service can call in a servlet. Note that it is not necessary to implement them all: the service has a default behaviour for missing methods.
- accept_binding(path: str, parameters: Dict[str, Any]) bool | None
This method is called before trying to bind the servlet. If it returns False, the servlet won’t be bound to the server. This allows a servlet service to be bound to a specific server.
If this method doesn’t exist or returns None or anything else but False, the calling HTTP service will consider that the servlet accepts to be bound to it.
- Parameters:
path (str) – The path of the servlet in the server
parameters (dict) – The parameters of the server
- bound_to(path: str, parameters: Dict[str, Any]) bool | None
This method is called when the servlet is bound to a path. If it returns False or raises an Exception, the registration is aborted.
- Parameters:
path (str) – The path of the servlet in the server
parameters (dict) – The parameters of the server
- unbound_from(path: str, parameters: Dict[str, Any]) None
This method is called when the servlet is bound to a path. The parameters are the ones given in
accept_binding()
andbound_to()
.- Parameters:
path (str) – The path of the servlet in the server
parameters (dict) – The parameters of the server
- do_XXX(request: AbstractHTTPServletRequest, response: AbstractHTTPServletResponse) None
Each request is handled by the method call
do_XXX
whereXXX
is the name of an HTTP method (do_GET
,do_POST
,do_PUT
,do_HEAD
, …).If it raises an exception, the server automatically sends an HTTP 500 error page. In nominal behaviour, the method must use the
response
argument to send a reply to the client.- Parameters:
request – A
AbstractHTTPServletRequest
representation of the requestresponse – The
AbstractHTTPServletResponse
object to use to reply to the client
HTTP request
Each request method has a request helper argument, which implements the
AbstractHTTPServletRequest
abstract class.
- class pelix.http.AbstractHTTPServletRequest
Abstract HTTP Servlet request helper
- abstract get_client_address() Tuple[str, int]
Returns the address of the client
- Returns:
A (host, port) tuple
- abstract get_command() str
Returns the HTTP verb (GET, POST, …) used for the request
- abstract get_header(name: str, default: Any = None) Any
Returns the value of a header
- Parameters:
name – Header name
default – Default value if the header doesn’t exist
- Returns:
The header value or the default one
- abstract get_headers() Dict[str, Any]
Returns a copy all headers, with a dictionary interface
- Returns:
A dictionary-like object
- abstract get_path() str
Returns the request full path
- Returns:
A request full path (string)
- abstract get_prefix_path() str
Returns the path to the servlet root
- Returns:
A request path (string)
- abstract get_rfile() IO[bytes]
Returns the request input as a file stream
- Returns:
A file-like input stream
- abstract get_sub_path() str
Returns the servlet-relative path, i.e. after the prefix
- Returns:
A request path (string)
- read_data() bytes
Reads all the data in the input stream
- Returns:
The read data
HTTP response
Each request method also has a response helper argument, which implements the
AbstractHTTPServletResponse
abstract class.
- class pelix.http.AbstractHTTPServletResponse
HTTP Servlet response helper
- abstract end_headers() None
Ends the headers part
- abstract get_wfile() IO[bytes]
Retrieves the output as a file stream.
end_headers()
should have been called before, except if you want to write your own headers.- Returns:
A file-like output stream
- abstract is_header_set(name: str) bool
Checks if the given header has already been set
- Parameters:
name – Header name
- Returns:
True if it has already been set
- send_content(http_code: int, content: str, mime_type: str | None = 'text/html', http_message: str | None = None, content_length: int = -1) None
Utility method to send the given content as an answer. You can still use get_wfile or write afterwards, if you forced the content length.
If content_length is negative (default), it will be computed as the length of the content; if it is positive, the given value will be used; if it is None, the content-length header won’t be sent.
- Parameters:
http_code – HTTP result code
content – Data to be sent (must be a string)
mime_type – Content MIME type (content-type)
http_message – HTTP code description
content_length – Forced content length
- abstract set_header(name: str, value: Any) None
Sets the value of a header. This method should not be called after
end_headers()
.- Parameters:
name – Header name
value – Header value
- abstract set_response(code: int, message: str | None = None) None
Sets the response line. This method should be the first called when sending an answer.
- Parameters:
code – HTTP result code
message – Associated message
- abstract write(data: bytes) None
Writes the given data.
end_headers()
should have been called before, except if you want to write your own headers.- Parameters:
data – Data to be written
Write a servlet
This snippet shows how to write a component providing the servlet service:
from pelix.ipopo.decorators import ComponentFactory, Property, Provides, \
Requires, Validate, Invalidate, Unbind, Bind, Instantiate
@ComponentFactory(name='simple-servlet-factory')
@Instantiate('simple-servlet')
@Provides(specifications='pelix.http.servlet')
@Property('_path', 'pelix.http.path', "/servlet")
class SimpleServletFactory:
"""
Simple servlet factory
"""
def __init__(self):
self._path = None
def bound_to(self, path, params):
"""
Servlet bound to a path
"""
print('Bound to ' + path)
return True
def unbound_from(self, path, params):
"""
Servlet unbound from a path
"""
print('Unbound from ' + path)
return None
def do_GET(self, request, response):
"""
Handle a GET
"""
content = """<html>
<head>
<title>Test SimpleServlet</title>
</head>
<body>
<ul>
<li>Client address: {clt_addr[0]}</li>
<li>Client port: {clt_addr[1]}</li>
<li>Host: {host}</li>
<li>Keys: {keys}</li>
</ul>
</body>
</html>""".format(clt_addr=request.get_client_address(),
host=request.get_header('host', 0),
keys=request.get_headers().keys())
response.send_content(200, content)
To test this snippet, install and start this bundle and the HTTP service bundle in a framework, then open a browser to the servlet URL. If you used the HTTP service instantiation sample, this URL should be http://localhost:9000/servlet.