Commits

Ginés Martínez Sánchez committed 57882ea Draft

wsgi server development

Comments (0)

Files changed (4)

ginsfsm/protocols/http/server/c_http_server.py

         "TODO:list of (host, port) tuples allowed to connect from"],
 
     'maximum_simultaneous_requests': [int, 0, 0, None,
-        "maximum simultaneous requests."
+        "maximum simultaneous requests. Default: 0, without limit."
         ],
     'identity': [str, 'ginsfsm', 0, None,
         "server identity (sent in Server: header)"
             port=self.port,
         )
 
-        # Used in environ
-        self.effective_host, self.effective_port = \
-            self.serversock.getsockname()
-        self.server_name = self._get_server_name(self.host)
-
-    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
-
-    def getsockname(self):
-        return self.serversock.socket.getsockname()
-
     def _get_ssl_certificate(self, binary_form=False):
         """Returns the client's SSL certificate, if any.
 

ginsfsm/protocols/wsgi/common/task.py

-##############################################################################
-#
-# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-
-import logging
-import socket
-import sys
-import time
-from ginsfsm.deferred import DeferredInterrupt
-
-from ginsfsm.buffers import ReadOnlyFileBasedBuffer
-
-from ginsfsm.compat import (
-    tobytes,
-    reraise,
-    )
-
-from ginsfsm.wsgi.utilities import (
-    build_http_date,
-    )
-
-rename_headers = {
-    'CONTENT_LENGTH' : 'CONTENT_LENGTH',
-    'CONTENT_TYPE'   : 'CONTENT_TYPE',
-    'CONNECTION'     : 'CONNECTION_TYPE',
-    }
-
-hop_by_hop = frozenset((
-    'connection',
-    'keep-alive',
-    'proxy-authenticate',
-    'proxy-authorization',
-    'te',
-    'trailers',
-    'transfer-encoding',
-    'upgrade'
-    ))
-
-
-class JustTesting(Exception):
-    pass
-
-
-class Task(object):
-    close_on_finish = False
-    status = '200 OK'
-    wrote_header = False
-    start_time = 0
-    content_length = None
-    content_bytes_written = 0
-    logged_write_excess = False
-    complete = False
-    chunked_response = False
-
-    def __init__(self, wsgi_server, gsock, request, ext_event=None):
-        self.wsgi_server = wsgi_server
-        self.gsock = gsock
-        self.request = request
-        self.ext_event = ext_event
-        self.response_headers = []
-        version = request.version
-        if version not in ('1.0', '1.1'):
-            # fall back to a version we support.
-            version = '1.0'
-        self.version = version
-
-    def service(self):
-        try:
-            try:
-                self.start()
-                self.execute()
-                self.finish()
-            except socket.error:
-                self.close_on_finish = True
-                raise  # ??? if self.gsock.adj.log_socket_errors:
-        except DeferredInterrupt:
-            raise
-        finally:
-            pass
-
-    def build_response_header(self):
-        version = self.version
-        # Figure out whether the connection should be closed.
-        connection = self.request.headers.get('CONNECTION', '').lower()
-        response_headers = self.response_headers
-        content_length_header = None
-        date_header = None
-        server_header = None
-        connection_close_header = None
-
-        for i, (headername, headerval) in enumerate(response_headers):
-            headername = '-'.join(
-                [x.capitalize() for x in headername.split('-')]
-                )
-            if headername == 'Content-Length':
-                content_length_header = headerval
-            if headername == 'Date':
-                date_header = headerval
-            if headername == 'Server':
-                server_header = headerval
-            if headername == 'Connection':
-                connection_close_header = headerval.lower()
-            # replace with properly capitalized version
-            response_headers[i] = (headername, headerval)
-
-        if content_length_header is None and self.content_length is not None:
-            content_length_header = str(self.content_length)
-            self.response_headers.append(
-                ('Content-Length', content_length_header)
-                )
-
-        def close_on_finish():
-            if connection_close_header is None:
-                response_headers.append(('Connection', 'close'))
-            self.close_on_finish = True
-
-        if version == '1.0':
-            if connection == 'keep-alive':
-                if not content_length_header:
-                    close_on_finish()
-                else:
-                    response_headers.append(('Connection', 'Keep-Alive'))
-            else:
-                close_on_finish()
-
-        elif version == '1.1':
-            if connection == 'close':
-                close_on_finish()
-
-            if not content_length_header:
-                response_headers.append(('Transfer-Encoding', 'chunked'))
-                self.chunked_response = True
-                if not self.close_on_finish:
-                    close_on_finish()
-
-            # under HTTP 1.1 keep-alive is default, no need to set the header
-        else:
-            raise AssertionError('neither HTTP/1.0 or HTTP/1.1')
-
-        # Set the Server and Date field, if not yet specified. This is needed
-        # if the server is used as a proxy.
-        ident = self.wsgi_server.identity
-        if not server_header:
-            response_headers.append(('Server', ident))
-        else:
-            response_headers.append(('Via', ident))
-        if not date_header:
-            response_headers.append(('Date', build_http_date(self.start_time)))
-
-        first_line = 'HTTP/%s %s' % (self.version, self.status)
-        next_lines = ['%s: %s' % hv for hv in sorted(self.response_headers)]
-        lines = [first_line] + next_lines
-        res = '%s\r\n\r\n' % '\r\n'.join(lines)
-        return tobytes(res)
-
-    def remove_content_length_header(self):
-        for i, (header_name, header_value) in enumerate(self.response_headers):
-            if header_name.lower() == 'content-length':
-                del self.response_headers[i]
-
-    def start(self):
-        self.start_time = time.time()
-
-    def finish(self):
-        if not self.wrote_header:
-            self.write(b'')
-        if self.chunked_response:
-            # not self.write, it will chunk it!
-            self.gsock.write_soon(b'0\r\n\r\n')
-        self.gsock.send_some()
-
-    def write(self, data):
-        if not self.complete:
-            raise RuntimeError('start_response was not called before body '
-                               'written')
-        gsock = self.gsock
-        if not self.wrote_header:
-            rh = self.build_response_header()
-            gsock.write_soon(rh)
-            self.wrote_header = True
-        if data:
-            towrite = data
-            cl = self.content_length
-            if self.chunked_response:
-                # use chunked encoding response
-                towrite = tobytes(hex(len(data))[2:].upper()) + b'\r\n'
-                towrite += data + b'\r\n'
-            elif cl is not None:
-                towrite = data[:cl - self.content_bytes_written]
-                self.content_bytes_written += len(towrite)
-                if towrite != data and not self.logged_write_excess:
-                    logging.error(
-                        'application-written content exceeded the number of '
-                        'bytes specified by Content-Length header (%s)' % cl)
-                    self.logged_write_excess = True
-            if towrite:
-                gsock.write_soon(towrite)
-
-
-class ErrorTask(Task):
-    """ An error task produces an error response """
-    complete = True
-
-    def execute(self):
-        e = self.request.error
-        body = '%s\r\n\r\n%s' % (e.reason, e.body)
-        tag = '\r\n\r\n(generated by ginsfsm.wsgi)'
-        body = body + tag
-        self.status = '%s %s' % (e.code, e.reason)
-        cl = len(body)
-        self.content_length = cl
-        self.response_headers.append(('Content-Length', str(cl)))
-        self.response_headers.append(('Content-Type', 'text/plain'))
-        self.response_headers.append(('Connection', 'close'))
-        self.close_on_finish = True
-        self.write(tobytes(body))
-
-
-class WSGITask(Task):
-    """A WSGI task produces a response from a WSGI application.
-    """
-    environ = None
-
-    def execute(self):
-        env = self.get_environment()
-
-        def start_response(status, headers, exc_info=None):
-            if self.complete and not exc_info:
-                raise AssertionError("start_response called a second time "
-                                     "without providing exc_info.")
-            if exc_info:
-                try:
-                    if self.complete:
-                        # higher levels will catch and handle raised exception:
-                        # 1. "service" method in task.py
-                        # 2. "service" method in gsock.py
-                        # 3. "handler_thread" method in task.py
-                        reraise(exc_info[0], exc_info[1], exc_info[2])
-                    else:
-                        # As per WSGI spec existing headers must be cleared
-                        self.response_headers = []
-                finally:
-                    exc_info = None
-
-            self.complete = True
-
-            if not status.__class__ is str:
-                raise AssertionError('status %s is not a string' % status)
-
-            self.status = status
-
-            # Prepare the headers for output
-            for k, v in headers:
-                if not k.__class__ is str:
-                    raise AssertionError(
-                        'Header name %r is not a string in %r' % (k, (k, v))
-                        )
-                if not v.__class__ is str:
-                    raise AssertionError(
-                        'Header value %r is not a string in %r' % (v, (k, v))
-                        )
-                kl = k.lower()
-                if kl == 'content-length':
-                    self.content_length = int(v)
-                elif kl in hop_by_hop:
-                    raise AssertionError(
-                        '%s is a "hop-by-hop" header; it cannot be used by '
-                        'a WSGI application (see PEP 3333)' % k)
-
-            self.response_headers.extend(headers)
-
-            # Return a method used to write the response data.
-            return self.write
-
-        # Call the application to handle the request and write a response
-        # TODO: do a multi wsgi-app
-        try:
-            app_iter = self.wsgi_server.application(env, start_response)
-        except DeferredInterrupt:
-            """ A gobj inside his gaplic need
-                to wait for data from another gaplic.
-            """
-            raise
-
-        try:
-            if app_iter.__class__ is ReadOnlyFileBasedBuffer:
-                cl = self.content_length
-                size = app_iter.prepare(cl)
-                if size:
-                    if cl != size:
-                        if cl is not None:
-                            self.remove_content_length_header()
-                        self.content_length = size
-                    self.write(b'')  # generate headers
-                    self.gsock.write_soon(app_iter)
-                    return
-
-            first_chunk_len = None
-            for chunk in app_iter:
-                if first_chunk_len is None:
-                    first_chunk_len = len(chunk)
-                    # Set a Content-Length header if one is not supplied.
-                    # start_response may not have been called until first
-                    # iteration as per PEP, so we must reinterrogate
-                    # self.content_length here
-                    if self.content_length is None:
-                        app_iter_len = None
-                        if hasattr(app_iter, '__len__'):
-                            app_iter_len = len(app_iter)
-                        if app_iter_len == 1:
-                            self.content_length = first_chunk_len
-                # transmit headers only after first iteration of the iterable
-                # that returns a non-empty bytestring (PEP 3333)
-                if chunk:
-                    self.write(chunk)
-
-            cl = self.content_length
-            if cl is not None:
-                if self.content_bytes_written != cl:
-                    # close the connection so the client isn't sitting around
-                    # waiting for more data when there are too few bytes
-                    # to service content-length
-                    self.close_on_finish = True
-                    logging.error(
-                        'application returned too few bytes (%s) '
-                        'for specified Content-Length (%s) via app_iter' % (
-                            self.content_bytes_written, cl),
-                        )
-        finally:
-            if hasattr(app_iter, 'close'):
-                app_iter.close()
-
-    def get_environment(self):
-        """Returns a WSGI environment."""
-        environ = self.environ
-        if environ is not None:
-            # Return the cached copy.
-            return environ
-
-        request = self.request
-        path = request.path
-        gsock = self.gsock
-        wsgi_server = self.wsgi_server
-
-        while path and path.startswith('/'):
-            path = path[1:]
-
-        environ = {}
-        environ['gsock'] = gsock
-        environ['ext_event'] = self.ext_event
-        environ['REQUEST_METHOD'] = request.command.upper()
-        environ['SERVER_PORT'] = str(wsgi_server.effective_port)
-        environ['SERVER_NAME'] = wsgi_server.server_name
-        environ['SERVER_SOFTWARE'] = wsgi_server.identity
-        environ['SERVER_PROTOCOL'] = 'HTTP/%s' % self.version
-        environ['SCRIPT_NAME'] = ''
-        environ['PATH_INFO'] = '/' + path
-        environ['QUERY_STRING'] = request.query
-        environ['REMOTE_ADDR'] = gsock.addr[0]
-
-        for key, value in request.headers.items():
-            value = value.strip()
-            mykey = rename_headers.get(key, None)
-            if mykey is None:
-                mykey = 'HTTP_%s' % key
-            if not mykey in environ:
-                environ[mykey] = value
-
-        # the following environment variables are required by the WSGI spec
-        environ['wsgi.version'] = (1, 0)
-        environ['wsgi.url_scheme'] = request.url_scheme
-        environ['wsgi.errors'] = sys.stderr  # apps should use the logging module
-        environ['wsgi.multithread'] = False
-        environ['wsgi.multiprocess'] = False
-        environ['wsgi.run_once'] = False
-        environ['wsgi.input'] = request.get_body_stream()
-        environ['wsgi.file_wrapper'] = ReadOnlyFileBasedBuffer
-
-        self.environ = environ
-        return environ

ginsfsm/protocols/wsgi/common/wsgi_response.py

+# -*- encoding: utf-8 -*-
+"""
+GObj :class:`WsgiResponse`
+===========================
+
+Code copied from waitress.task.py adapted to ginsfsm.
+
+.. autoclass:: HttpResponse
+    :members:
+
+"""
+
+import logging
+import sys
+from ginsfsm.deferred import DeferredInterrupt
+from ginsfsm.buffers import ReadOnlyFileBasedBuffer
+from ginsfsm.compat import (
+    reraise,
+)
+from ginsfsm.protocols.http.common.response import HttpResponse
+
+rename_headers = {
+    'CONTENT_LENGTH' : 'CONTENT_LENGTH',
+    'CONTENT_TYPE'   : 'CONTENT_TYPE',
+    'CONNECTION'     : 'CONNECTION_TYPE',
+    }
+
+hop_by_hop = frozenset((
+    'connection',
+    'keep-alive',
+    'proxy-authenticate',
+    'proxy-authorization',
+    'te',
+    'trailers',
+    'transfer-encoding',
+    'upgrade'
+    ))
+
+
+class WsgiResponse(HttpResponse):
+    """Get a response from a WSGI application.
+    """
+    environ = None
+
+    def __init__(self, request, wsgi_server, toclose=True):
+        HttpResponse.__init__(self, request)
+        self.wsgi_server = wsgi_server
+        self.toclose = toclose
+
+    def execute(self):
+        env = self.get_environment()
+
+        def start_response(status, headers, exc_info=None):
+            if self.complete and not exc_info:
+                raise AssertionError("start_response called a second time "
+                                     "without providing exc_info.")
+            if exc_info:
+                try:
+                    if self.complete:
+                        # higher levels will catch and handle raised exception:
+                        # 1. "service" method in task.py
+                        # 2. "service" method in gsock.py
+                        # 3. "handler_thread" method in task.py
+                        reraise(exc_info[0], exc_info[1], exc_info[2])
+                    else:
+                        # As per WSGI spec existing headers must be cleared
+                        self.response_headers = []
+                finally:
+                    exc_info = None
+
+            self.complete = True
+
+            if not status.__class__ is str:
+                raise AssertionError('status %s is not a string' % status)
+
+            self.status = status
+
+            # Prepare the headers for output
+            for k, v in headers:
+                if not k.__class__ is str:
+                    raise AssertionError(
+                        'Header name %r is not a string in %r' % (k, (k, v))
+                        )
+                if not v.__class__ is str:
+                    raise AssertionError(
+                        'Header value %r is not a string in %r' % (v, (k, v))
+                        )
+                kl = k.lower()
+                if kl == 'content-length':
+                    self.content_length = int(v)
+                elif kl in hop_by_hop:
+                    raise AssertionError(
+                        '%s is a "hop-by-hop" header; it cannot be used by '
+                        'a WSGI application (see PEP 3333)' % k)
+
+            self.response_headers.extend(headers)
+
+            # Return a method used to write the response data.
+            return self.write
+
+        # Call the application to handle the request and write a response
+        # TODO: do a multi wsgi-app
+        try:
+            app_iter = self.wsgi_server.application(env, start_response)
+        except DeferredInterrupt:
+            """ A gobj inside his gaplic need
+                to wait for data from another gaplic.
+            """
+            raise
+
+        try:
+            if app_iter.__class__ is ReadOnlyFileBasedBuffer:
+                cl = self.content_length
+                size = app_iter.prepare(cl)
+                if size:
+                    if cl != size:
+                        if cl is not None:
+                            self.remove_content_length_header()
+                        self.content_length = size
+                    self.write(b'')  # generate headers
+                    self.gsock.write_soon(app_iter)
+                    return
+
+            first_chunk_len = None
+            for chunk in app_iter:
+                if first_chunk_len is None:
+                    first_chunk_len = len(chunk)
+                    # Set a Content-Length header if one is not supplied.
+                    # start_response may not have been called until first
+                    # iteration as per PEP, so we must reinterrogate
+                    # self.content_length here
+                    if self.content_length is None:
+                        app_iter_len = None
+                        if hasattr(app_iter, '__len__'):
+                            app_iter_len = len(app_iter)
+                        if app_iter_len == 1:
+                            self.content_length = first_chunk_len
+                # transmit headers only after first iteration of the iterable
+                # that returns a non-empty bytestring (PEP 3333)
+                if chunk:
+                    self.write(chunk)
+
+            cl = self.content_length
+            if cl is not None:
+                if self.content_bytes_written != cl:
+                    # close the connection so the client isn't sitting around
+                    # waiting for more data when there are too few bytes
+                    # to service content-length
+                    self.close_on_finish = True
+                    logging.error(
+                        'application returned too few bytes (%s) '
+                        'for specified Content-Length (%s) via app_iter' % (
+                            self.content_bytes_written, cl),
+                        )
+        finally:
+            if hasattr(app_iter, 'close'):
+                app_iter.close()
+
+    def get_environment(self):
+        """Returns a WSGI environment."""
+        environ = self.environ
+        if environ is not None:
+            # Return the cached copy.
+            return environ
+
+        request = self.request
+        path = request.path
+        gsock = self.gsock
+        wsgi_server = self.wsgi_server
+
+        while path and path.startswith('/'):
+            path = path[1:]
+
+        environ = {}
+        environ['gsock'] = gsock
+        environ['ext_event'] = self.ext_event
+        environ['REQUEST_METHOD'] = request.command.upper()
+        environ['SERVER_PORT'] = str(wsgi_server.effective_port)
+        environ['SERVER_NAME'] = wsgi_server.server_name
+        environ['SERVER_SOFTWARE'] = wsgi_server.identity
+        environ['SERVER_PROTOCOL'] = 'HTTP/%s' % self.version
+        environ['SCRIPT_NAME'] = ''
+        environ['PATH_INFO'] = '/' + path
+        environ['QUERY_STRING'] = request.query
+        environ['REMOTE_ADDR'] = gsock.addr[0]
+
+        for key, value in request.headers.items():
+            value = value.strip()
+            mykey = rename_headers.get(key, None)
+            if mykey is None:
+                mykey = 'HTTP_%s' % key
+            if not mykey in environ:
+                environ[mykey] = value
+
+        # the following environment variables are required by the WSGI spec
+        environ['wsgi.version'] = (1, 0)
+        environ['wsgi.url_scheme'] = request.url_scheme
+        environ['wsgi.errors'] = sys.stderr  # apps should use the logging module
+        environ['wsgi.multithread'] = False
+        environ['wsgi.multiprocess'] = False
+        environ['wsgi.run_once'] = False
+        environ['wsgi.input'] = request.get_body_stream()
+        environ['wsgi.file_wrapper'] = ReadOnlyFileBasedBuffer
+
+        self.environ = environ
+        return environ

ginsfsm/protocols/wsgi/server/c_wsgi_server.py

 # -*- encoding: utf-8 -*-
+"""
+GObj :class:`GWsgiServer`
+=========================
 
-import time
-import traceback
-import logging
+WSGI server.
 
-from ginsfsm.protocols.http.common.parser import HTTPRequestParser
-from ginsfsm.protocols.wsgi.common.task import (
-    ErrorTask,
-    WSGITask,
-    )
-from ginsfsm.protocols.http.common.utilities import (
-    InternalServerError,
-    )
+It uses :class:`ginsfsm.protocols.http.server.c_http_server.GHttpServer`.
+
+.. autoclass:: GWsgiServer
+    :members:
+
+"""
 
 from ginsfsm.gobj import GObj
-from ginsfsm.deferred import DeferredInterrupt
+from ginsfsm.protocols.http.server.c_http_server import GHttpServer
+from ginsfsm.protocols.wsgi.common.wsgi_response import WsgiResponse
+from ginsfsm.protocols.http.server.c_http_server import GHTTPSERVER_GCONFIG
 
+def ac_channel_opened(self, event):
+    print "======> channel opened"
 
-def ac_execute_requests(self, event):
-    """Execute all pending requests """
+
+def ac_channel_closed(self, event):
+    print "======> channel closed"
+
+
+def ac_request(self, event):
+    print "======> channel request"
+    channel = event.source[-1]
+    request = event.request
+    response = WsgiResponse(
+        request,
+        'Caraculo\r\nCaraculo\r\nCaraculo\r\n'
+    )
+    self.send_event(channel, 'EV_HTTP_RESPONSE', response=response)
+
+
     requests = event.requests
     gsock = self.gsock
     ext_event = None
     gsock.last_activity = time.time()
 
 
-WSGIAPP_FSM = {
+GWSGISERVER_FSM = {
     'event_list': (
-        'EV_EXECUTE_REQUESTS: top input',
-        'EV_WSGIAPP_RESPONSE: top output',
+        'EV_HTTP_CHANNEL_OPENED: bottom input',
+        'EV_HTTP_CHANNEL_CLOSED: bottom input',
+        'EV_HTTP_REQUEST: bottom input',
     ),
     'state_list': ('ST_IDLE',),
     'machine': {
         'ST_IDLE':
         (
-            ('EV_EXECUTE_REQUESTS', ac_execute_requests,    'ST_IDLE'),
+            ('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'),
         ),
     }
 }
 
-WSGIAPP_GCONFIG = {
-    'wsgi_server': [None, None, 0, None, "wsgi server"],
-    'gsock': [None, None, 0, None, "clisrv gsock"],
+GWSGISERVER_GCONFIG = GHTTPSERVER_GCONFIG + {
     'expose_tracebacks': [bool, False, 0, None,
         "expose tracebacks of uncaught exceptions"],
+    #TODO: implement a multi wsgi application
+    'application': [None, None, 0, None, "wsgi application"],
 }
 
 
-class WSGIApp(GObj):
-    """  Execute WSGI apps.
+class GWsgiServer(GObj):
+    """  WSGI Server gobj.
+
+    The incoming connections will create a new :class:`ginsfsm.c_sock.GSock`
+    :term:`gobj`,
+    that will be child of the :attr:`subscriber` :term:`gobj`.
+
+        .. warning::  Remember destroy the accepted `gobj`
+           with :func:`destroy_gobj` when the `gobj` has been disconnected.
+
+           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: GWSGISERVER_FSM
+       :gconfig: GWSGISERVER_GCONFIG
+
+    *Input-Events:*
+
+        The relationship is directly between the
+        accepted :class:`ginsfsm.c_sock.GSock` gobj and the :attr:`subscriber`.
+
+        See :class:`ginsfsm.c_sock.GSock` `input-events`.
+
+    *Output-Events:*
+
+        The relationship is directly between the
+        accepted :class:`ginsfsm.c_sock.GSock` gobj and the :attr:`subscriber`.
+
+        See :class:`ginsfsm.c_sock.GSock` `output-events`.
     """
 
     def __init__(self):
-        GObj.__init__(self, WSGIAPP_FSM, WSGIAPP_GCONFIG)
+        GObj.__init__(self, GWSGISERVER_FSM, GWSGISERVER_GCONFIG)
+        self._n_connected_clisrv = 0
 
     def start_up(self):
-        pass
+        if self.name and len(self.name):
+            prefix_name = self.name
+        else:
+            prefix_name = None
+
+        self.server = self.create_gobj(
+            prefix_name + '.sock-server' if prefix_name else None,
+            GHttpServer,
+            self,
+            subscriber=self,
+            host=self.host,
+            port=self.port,
+            origins=self.origins,
+            inactivity_timeout=10,
+            responseless_timeout=2,
+            maximum_simultaneous_requests=0,
+        )
+
+        # Used in environ
+        self.effective_host, self.effective_port = \
+            self.serversock.getsockname()
+        self.server_name = self._get_server_name(self.host)
+
+    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
+
+    def getsockname(self):
+        return self.serversock.socket.getsockname()
+