# -*- encoding: utf-8 -*-
"""
GObj :class:`GHttpServer`
=========================
.. autoclass:: GHttpServer
:members:
"""
try:
import ssl # Python 2.6+
except ImportError:
ssl = None
from ginsfsm.gobj import GObj
from ginsfsm.c_srv_sock import GServerSock
from ginsfsm.protocols.http.server.c_http_clisrv import GHttpCliSrv
def ac_connected(self, event):
""" New clisvr gsock (http channel):
A new client http has been acepted.
The new clisrv GSock, created by GServerSock is sending this event.
"""
gsock = event.source[-1]
gsock.delete_all_subscriptions()
self._n_channel += 1
clisrv = self.create_gobj(
'channel_%x' % self._n_channel,
GHttpCliSrv,
self,
gsock=gsock,
subscriber=self.config.subscriber, # TODO better to join two configs
maximum_simultaneous_requests=self.config.maximum_simultaneous_requests,
url_scheme=self.config.url_scheme,
inactivity_timeout=self.config.inactivity_timeout,
responseless_timeout=self.config.responseless_timeout,
inbuf_overflow=self.config.inbuf_overflow,
max_request_header_size=self.config.max_request_header_size,
max_request_body_size=self.config.max_request_body_size,
)
clisrv.subscribe_event( # to delete the channel
'EV_HTTP_CHANNEL_CLOSED',
self,
__hard_subscription__=True,
)
self.config.n_channels += 1
def ac_channel_closed(self, event):
""" **Some** clisvr gsock closed, drop it.
"""
channel = event.channel
self.post_event(self, 'EV_DESTROY_CHANNEL', channel=channel)
def ac_drop_httpchannel(self, event):
""" It's better receive this event by post_event().
"""
channel = event.channel
self._n_channel -= 1
# the gsock is destroy in channel.ac_disconnected (GHttpCliSrv)
GObj.destroy_gobj(channel)
self.config.n_channels -= 1
def ac_timeout(self, event):
self.set_timeout(10)
#print("Server's clients: %d, connected %d" % (
# len(self.dl_childs), self._n_channel))
GHTTPSERVER_FSM = {
'event_list': (
'EV_CONNECTED: bottom input',
'EV_HTTP_CHANNEL_CLOSED: bottom input',
'EV_TIMEOUT',
'EV_DESTROY_CHANNEL',
),
'state_list': ('ST_IDLE',),
'machine': {
'ST_IDLE':
(
('EV_TIMEOUT', ac_timeout, None),
('EV_CONNECTED', ac_connected, None),
('EV_HTTP_CHANNEL_CLOSED', ac_channel_closed, None),
('EV_DESTROY_CHANNEL', ac_drop_httpchannel, None),
),
}
}
GHTTPSERVER_GCONFIG = {
'subscriber': [
None, None, 0, None,
"subcriber of all output-events."
"Default is ``None``, i.e., the parent"
],
'host': [str, '', 0, None, "listening host"],
'port': [int, 0, 0, None, "listening port"],
'origins': [
None, None, 0, None,
"TODO:list of (host, port) tuples allowed to connect from"
],
'expose_tracebacks': [
bool, False, 0, None, "expose tracebacks of uncaught exceptions"
],
'maximum_simultaneous_requests': [
int, 0, 0, None,
"maximum simultaneous requests. Default: 0, without limit."
],
'identity': [
str, 'ginsfsm', 0, None,
"server identity (sent in Server: header)"
],
'url_scheme': [str, 'http', 0, None, "default ``http`` value"],
'inactivity_timeout': [
int, 5 * 60 * 60, 0, None,
"Inactivity timeout in seconds."
],
'responseless_timeout': [
int, 5 * 60 * 60, 0, None,
"'Without response' timeout in seconds."
],
# A tempfile should be created if the pending input is larger than
# inbuf_overflow, which is measured in bytes. The default is 512K. This
# is conservative.
'inbuf_overflow': [int, 524288, 0, None, ""],
# maximum number of bytes of all request headers combined (256K default)
'max_request_header_size': [int, 262144, 0, None, ""],
# maximum number of bytes in request body (1GB default)
'max_request_body_size': [int, 1073741824, 0, None, ""],
'n_channels': [int, 0, 0, None, "Server stats, number of channels opened"],
}
[docs]class GHttpServer(GObj):
""" Asynchronous Http Server gobj.
In the startup, this class creates internally a server socket, gobj of
:class:`ginsfsm.c_srv_sock.GServerSock` gclass.
When a new client connects,
a new :class:`ginsfsm.c_sock.GSock` gobj is created,
receiving the EV_CONNECTED event.
Then it creates a
:class:`ginsfsm.protocols.http.server.c_http_clisrv.GHttpCliSrv` gobj
that will process all the events of the GSock gobj.
The output events of
:class:`ginsfsm.protocols.http.server.c_http_clisrv.GHttpCliSrv`
will be processed by ``subscriber`` gobj of this class.
.. ginsfsm::
:fsm: GHTTPSERVER_FSM
:gconfig: GHTTPSERVER_GCONFIG
*Input-Events:*
* :attr:`'EV_CONNECTED'`: new *clisrv*, client socket.
The internal child :class:`ginsfsm.c_srv_sock.GServerSock` gobj
has accepted a new socket connection,
and it has created a new client gobj (*clisrv*) of
:class:`ginsfsm.c_sock.GSock` gclass.
This event has been sended
by the new :class:`ginsfsm.c_sock.GSock` *clisrv* gobj.
A new
:class:`ginsfsm.protocols.http.server.c_http_clisrv.GHttpCliSrv` gobj
is created, to process all the events of the GSock gobj.
This class will be subscred to the ``'EV_HTTP_CHANNEL_CLOSED'`` event
in order to destroy the GHttpCliSrv gobj.
Event attributes:
* ``peername``: remote address to which the socket is connected.
* ``sockname``: the socket’s own address.
* :attr:`'EV_HTTP_CHANNEL_CLOSED'`: http channel disconnected.
The http server subcribes this event from clisrv gobj,
in order to destroy it when became disconnected.
"""
def __init__(self):
GObj.__init__(self, GHTTPSERVER_FSM, GHTTPSERVER_GCONFIG)
self._n_channel = 0
def start_up(self):
self.gserversock = self.create_gobj(
self.name if self.name else 'sock-server',
GServerSock,
self,
subscriber=self, # Iniatially capture all events from new clisrv.
host=self.config.host,
port=self.config.port,
)
def _get_ssl_certificate(self, binary_form=False):
"""Returns the client's SSL certificate, if any.
To use client certificates, the HTTPServer must have been constructed
with cert_reqs set in ssl_options, e.g.::
server = HTTPServer(app,
ssl_options=dict(
certfile="foo.crt",
keyfile="foo.key",
cert_reqs=ssl.CERT_REQUIRED,
ca_certs="cacert.crt"))
By default, the return value is a dictionary (or None, if no
client certificate is present). If ``binary_form`` is true, a
DER-encoded form of the certificate is returned instead. See
SSLSocket.getpeercert() in the standard library for more
details.
http://docs.python.org/library/ssl.html#sslsocket-objects
"""
try:
return self.connection.stream.socket.getpeercert(
binary_form=binary_form)
except ssl.SSLError:
return None