import itertools
import logging
import socket
import urlparse

import feather.http
import feather.wsgitools
import greenhouse


__all__ = ["Server", "HTTPConnectionHandler", "HTTPWSGIRequestHandler"]

logger = logging.getLogger("feather.server")

class HTTPWSGIRequestHandler(object):

    wsgiapp = None

    def __init__(self, request, server, connection_handler):
        self.request = request
        self.server = server
        self.connection_handler = connection_handler

    def respond(self):
        environ = feather.wsgitools.make_environ(self.request,
                                                 self.server.address)
        start_response, collector = feather.wsgitools.make_start_response()

        try:
            result = self.wsgiapp(environ, start_response)
        except:
            raise feather.http.HTTPError(500)

        for key, value in collector['headers']:
            if key.lower() == 'content-length':
                break
        else:
            # if we aren't sending a Content-Length, close the connection
            logger.debug("close connection b/c no Content-Length in response")
            self.connection_handler.open = False

        # don't put the headers in their own send() call, so prepend
        # them to the first item yielded from the iterable
        result = itertools.chain([collector['prefix'].getvalue()], result)
        firstresult = iter(result).next()

        # after getting the first result from the response iterable,
        # the headers have to have been made available
        head = "HTTP/1.1 %s\r\n%s\r\n\r\n" % (collector['status'],
                "\r\n".join("%s: %s" % pair for pair in collector['headers']))

        return itertools.chain([head + firstresult], result)

class HTTPConnectionHandler(object):

    request_handler = HTTPWSGIRequestHandler

    def __init__(self, sock, address, server):
        self.sock = sock
        self.client_address = address
        self.server = server
        self.open = True

    def handle(self):
        logger.debug("greenlet with connection %d started" % id(self))
        while self.open:
            try:
                # create the file object expecting no body, when we parse the
                # request headers from it we will replace the expected body
                # length with the Content-Length header, if any
                rfile = feather.http.InputFile(self.sock, 0)
                request = feather.http.parse_request(rfile)
                logger.debug("got and parsed a request from connection %d" %
                             id(self))

                req_handler = self.request_handler(request, self.server, self)

                for chunk in req_handler.respond():
                    self.sock.sendall(chunk)

                connheader = request.headers.get('connection').lower()
                if connheader == 'close' or (request.version < (1, 1) and
                                             connheader != 'keep-alive'):
                    if connheader == close:
                        logger.debug("closing connection - Connection: close")
                    else:
                        logger.debug("HTTP<1.1 and no Connection: keep-alive")
                    self.open = False
            except feather.http.HTTPError, err:
                logger.debug("sending an HTTP %d error response" % err.args[0])
                short, long = feather.http.responses[err.args[0]]
                self.sock.sendall("HTTP/1.0 %d %s\r\n"
                                  "Connection: close\r\n"
                                  "Content-Length: %d\r\n"
                                  "Content-Type: text/plain\r\n\r\n"
                                  "%s" %
                                  (err.args[0], short, len(long), long))
                self.open = False
            except:
                logger.debug("unknown exception, HTTP 500 error response")
                self.open = False

        self.sock.close()

class Server(object):

    connection_handler = HTTPConnectionHandler

    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 5

    def __init__(self, server_address):
        self.address = server_address
        self.is_setup = False
        self._serversock = greenhouse.Socket(self.address_family,
                self.socket_type)

    def setup(self):
        logger.info("binding listening socket to %r" % (self.address,))
        self._serversock.bind(self.address)
        self._serversock.listen(self.request_queue_size)
        self.is_setup = True

    def serve(self):
        if not self.is_setup:
            self.setup()
        while 1:
            try:
                logger.debug("listening for a new connection")
                clientsock, clientaddr = self._serversock.accept()
                conn_handler = self.connection_handler(clientsock, clientaddr,
                                                       self)
                greenhouse.schedule(conn_handler.handle)
                logger.debug("passed off new connection %d to a new greenlet" %
                             id(conn_handler))
            except KeyboardInterrupt:
                logger.info("KeyboardInterrupt caught, closing listen socket")
                self._serversock.close()
                raise

@@ -39,16 +39,44 @@
  socket%0A
+import sys%0Aimport traceback%0A
 import u
@@ -2863,16 +2863,20 @@
 nection'
+, ''
 ).lower(
@@ -2921,16 +2921,22 @@
 ose' or 
+(tuple
 (request
@@ -2943,16 +2943,17 @@
 .version
+)
  %3C (1, 1
@@ -2953,20 +2953,16 @@
 %3C (1, 1)
- and
 %0A       
@@ -2991,32 +2991,36 @@
                 
+and 
 connheader != 'k
@@ -3069,21 +3069,23 @@
 ader == 
+'
 close
+'
 :%0A      
@@ -3937,51 +3937,184 @@
 ug(%22
-unknown exception, HTTP 500 error response%22
+HTTP 500 error response from an exception:%22)%0A                logger.debug(%22%22.join(%22  %25s%22 %25 line for line in%0A                        traceback.format_exception(*sys.exc_info()))
 )%0A  
@@ -5516,14 +5516,19 @@
         
-raise
+sys.exit()
 %0A

fix some connection-closing tracebacks, and report TBs better