Commits

Ginés Martínez Sánchez committed f94679a Draft

preparing sockjs

Comments (0)

Files changed (3)

docs/src/api/sockjs-server.png

Old
Old image
New
New image

ginsfsm/protocols/sockjs/server/c_sockjs_server.py

 GObj :class:`GSockjsServer`
 ===========================
 
+Sockjs server.
+
+It uses :class:`ginsfsm.protocols.http.server.c_http_server.GHttpServer`.
+
 .. autoclass:: GSockjsServer
     :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.sockjs.server.c_sockjs_clisrv import GSockjsCliSrv
+from ginsfsm.compat import string_types
+from ginsfsm.globals import get_global_app
+from ginsfsm.protocols.http.server.c_http_server import GHttpServer
+from ginsfsm.protocols.wsgi.common.wsgi_response import SockjsResponse
+from ginsfsm.protocols.http.server.c_http_server import GHTTPSERVER_GCONFIG
 
 
-def ac_connected(self, event):
-    """ New clisvr gsock:
-    A new client gsock has been acepted.
-    The new clisrv GSock, created by GServerSock is sending this event.
+def ac_channel_opened(self, event):
+    """ New client opened.
     """
-    self._n_connected_clisrv += 1
-    gsock = event.source[-1]
-    gsock.delete_all_subscriptions()
 
-    if gsock.name:
-        prefix_name = gsock.name
-    else:
-        prefix_name = None
-    clisrv = self.create_gobj(
-        prefix_name + '.bichannel' if prefix_name else None,
-        GSockjsCliSrv,
-        self,
-        gsock=gsock,
-        subscriber=self.subscriber,  # TODO better to join two configs
-        identity=self.identity,
-        maximum_simultaneous_requests=self.maximum_simultaneous_requests,
-        url_scheme=self.url_scheme,
-        inactivity_timeout=self.inactivity_timeout,
-        responseless_timeout=self.responseless_timeout,
-        inbuf_overflow=self.inbuf_overflow,
-        max_request_header_size=self.max_request_header_size,
-        max_request_body_size=self.max_request_body_size,
-    )
-    clisrv.subscribe_event('EV_HTTP_CHANNEL_CLOSED', self)
 
+def ac_channel_closed(self, event):
+    """ Client closed.
+    """
 
-def ac_disconnected(self, event):
-    """ **Some** clisvr gsock closed, drop it.
-    """
-    gobj = event.source[-1]
-    self.post_event(self, 'EV_DESTROY_CLISRV', gobj=gobj)
 
+def ac_request(self, event):
+    channel = event.source[-1]
+    request = event.request
+    application = self.select_app(request)
+    response = SockjsResponse(request, self, application)
+    self.send_event(channel, 'EV_HTTP_RESPONSE', response=response)
 
-def ac_drop_bichannel(self, event):
-    """ It's better receive this event by post_event().
-    """
-    gobj = event.gobj
-    self._n_connected_clisrv -= 1
-    self.destroy_gobj(gobj)
 
-
-def ac_timeout(self, event):
-    self.set_timeout(10)
-    print "Server's clients: %d, connected %d" % (
-        len(self.dl_childs), self._n_connected_clisrv)
-
-
-GSOCKJSSERVER_FSM = {
+GWSGISERVER_FSM = {
     'event_list': (
-        'EV_CONNECTED: bottom input',
+        'EV_HTTP_CHANNEL_OPENED: bottom input',
         'EV_HTTP_CHANNEL_CLOSED: bottom input',
-        'EV_TIMEOUT',
-        'EV_DESTROY_CLISRV',
+        'EV_HTTP_REQUEST: bottom input',
     ),
     'state_list': ('ST_IDLE',),
     'machine': {
         'ST_IDLE':
         (
-            ('EV_TIMEOUT',              ac_timeout,             None),
-            ('EV_CONNECTED',            ac_connected,           None),
-            ('EV_HTTP_CHANNEL_CLOSED',  ac_disconnected,        None),
-            ('EV_DESTROY_CLISRV',       ac_drop_bichannel,      None),
+            ('EV_HTTP_CHANNEL_OPENED',  ac_channel_opened,  'ST_IDLE'),
+            ('EV_HTTP_CHANNEL_CLOSED',  ac_channel_closed,  'ST_IDLE'),
+            ('EV_HTTP_REQUEST',         ac_request,         'ST_IDLE'),
         ),
     }
 }
 
-GSOCKJSSERVER_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, ""],
-}
+GWSGISERVER_GCONFIG = GHTTPSERVER_GCONFIG.copy()
+GWSGISERVER_GCONFIG.update({
+    'application': [None, None, 0, None, "wsgi application"],
+})
 
 
 class GSockjsServer(GObj):
-    """  Asynchronous Sockjs Server gobj.
+    """  WSGI Server gobj.
 
-    In the startup, this class creates internally a server socket, gobj of
-    :class:`ginsfsm.c_srv_sock.GServerSock` gclass.
+    The incoming connections will create a new :class:`ginsfsm.c_sock.GSock`
+    :term:`gobj`,
+    that will be child of the :attr:`subscriber` :term:`gobj`.
 
-    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.GSockjsCliSrv` gobj
-    that will process all the events of the GSock gobj.
+        .. warning::  Remember destroy the accepted `gobj`
+           with :func:`destroy_gobj` when the `gobj` has been disconnected.
 
-    The output events of
-    :class:`ginsfsm.protocols.http.server.c_http_clisrv.GSockjsCliSrv`
-    will be processed by ``subscriber`` gobj of this class.
+           The `subcriber` knows when a new `gobj` has been accepted because it
+           receives the ``'EV_CONNECTED'`` event.
+
+           When the `subcriber` receives a ``'EV_DISCONNECTED'`` event must
+           destroy the `gobj` because the connection ceases to exist.
 
     .. ginsfsm::
-       :fsm: GSOCKJSSERVER_FSM
-       :gconfig: GSOCKJSSERVER_GCONFIG
+       :fsm: GWSGISERVER_FSM
+       :gconfig: GWSGISERVER_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.
+        The relationship is directly between the
+        accepted :class:`ginsfsm.c_sock.GSock` gobj and the :attr:`subscriber`.
 
-          This event has been sended
-          by the new :class:`ginsfsm.c_sock.GSock` *clisrv* gobj.
+        See :class:`ginsfsm.c_sock.GSock` `input-events`.
 
-          A new
-          :class:`ginsfsm.protocols.http.server.c_http_clisrv.GSockjsCliSrv` gobj
-          is created, to process all the events of the GSock gobj.
+    *Output-Events:*
 
-          This class will be subscred to the ``'EV_HTTP_CHANNEL_CLOSED'`` event
-          in order to destroy the GSockjsCliSrv gobj.
+        The relationship is directly between the
+        accepted :class:`ginsfsm.c_sock.GSock` gobj and the :attr:`subscriber`.
 
-          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.
+        See :class:`ginsfsm.c_sock.GSock` `output-events`.
     """
 
     def __init__(self):
-        GObj.__init__(self, GSOCKJSSERVER_FSM, GSOCKJSSERVER_GCONFIG)
+        GObj.__init__(self, GWSGISERVER_FSM, GWSGISERVER_GCONFIG)
         self._n_connected_clisrv = 0
 
     def start_up(self):
-        if self.name:
+        if self.name and len(self.name):
             prefix_name = self.name
         else:
             prefix_name = None
-        self.gserversock = self.create_gobj(
-            prefix_name + '.server-sock' if prefix_name else None,
-            GServerSock,
+
+        self.ghttpserver = self.create_gobj(
+            prefix_name + '.http-server' if prefix_name else None,
+            GHttpServer,
             self,
-            subscriber=self,  # Iniatially capture all events from new clisrv.
+            subscriber=self,
             host=self.host,
             port=self.port,
+            origins=self.origins,
+            inactivity_timeout=10,
+            responseless_timeout=2,
+            maximum_simultaneous_requests=0,
         )
+        self.serversock = self.ghttpserver.gserversock.socket
 
-    def _get_ssl_certificate(self, binary_form=False):
-        """Returns the client's SSL certificate, if any.
+        # Used in environ
+        self.effective_host, self.effective_port = self.getsockname()
+        self.server_name = self._get_server_name(self.host)
 
-        To use client certificates, the HTTPServer must have been constructed
-        with cert_reqs set in ssl_options, e.g.::
+    def select_app(self, request):
+        # Starting from ini file, the application references is a string
+        # because the wsgi-apps are loaded later than main gaplic.
+        if isinstance(self.application, string_types):
+            self.application = get_global_app(self.application)
+        return self.application
 
-            server = HTTPServer(app,
-                ssl_options=dict(
-                    certfile="foo.crt",
-                    keyfile="foo.key",
-                    cert_reqs=ssl.CERT_REQUIRED,
-                    ca_certs="cacert.crt"))
+    def _get_server_name(self, ip):
+        """Given an IP or hostname, try to determine the server name."""
+        if ip:
+            srv_name = str(ip)
+        else:
+            srv_name = str(self.serversock.socketmod.gethostname())
+        # Convert to a host name if necessary.
+        for c in srv_name:
+            if c != '.' and not c.isdigit():
+                return srv_name
+        try:
+            if srv_name == '0.0.0.0':
+                return 'localhost'
+            srv_name = self.serversock.socketmod.gethostbyaddr(srv_name)[0]
+        except:
+            pass
+        return srv_name
 
-        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
+    def getsockname(self):
+        return self.serversock.getsockname()

ginsfsm/protocols/wsgi/common/urlmap.py

         return self.not_found_application(environ, start_response)
 
 
-class PathProxyURLMap(object):
-
-    """
-    This is a wrapper for URLMap that catches any strings that
-    are passed in as applications; these strings are treated as
-    filenames (relative to `base_path`) and are passed to the
-    callable `builder`, which will return an application.
-
-    This is intended for cases when configuration files can be
-    treated as applications.
-
-    `base_paste_url` is the URL under which all applications added through
-    this wrapper must go.  Use ``""`` if you want this to not
-    change incoming URLs.
-    """
-
-    def __init__(self, map, base_paste_url, base_path, builder):
-        self.map = map
-        self.base_paste_url = self.map.normalize_url(base_paste_url)
-        self.base_path = base_path
-        self.builder = builder
-
-    def __setitem__(self, url, app):
-        if isinstance(app, (str, unicode)):
-            app_fn = os.path.join(self.base_path, app)
-            app = self.builder(app_fn)
-        url = self.map.normalize_url(url)
-        # @@: This means http://foo.com/bar will potentially
-        # match foo.com, but /base_paste_url/bar, which is unintuitive
-        url = (url[0] or self.base_paste_url[0],
-               self.base_paste_url[1] + url[1])
-        self.map[url] = app
-
-    def __getattr__(self, attr):
-        return getattr(self.map, attr)
-
-    # This is really the only settable attribute
-    def not_found_application__get(self):
-        return self.map.not_found_application
-
-    def not_found_application__set(self, value):
-        self.map.not_found_application = value
-
-    not_found_application = property(not_found_application__get,
-                                     not_found_application__set)