#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2010 British Broadcasting Corporation and Kamaelia Contributors(1)
#
# (1) Kamaelia Contributors are listed in the AUTHORS file and at
#     http://www.kamaelia.org/AUTHORS - please extend this file,
#     not this notice.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This is the beginnings of a Kamaelia based webserver that is generally useful.
It is being used as the core basis of Kamaelia Publish
"""
 
 
# Import socket to get at constants for socketOptions
import socket
import pprint
 
# We need to import Axon - Kamaelia's core component system - to write Kamaelia components!
import Axon
 
# Import the server framework, the HTTP protocol handling, the minimal request handler, and error handlers
 
from Kamaelia.Chassis.ConnectedServer import SimpleServer
from Kamaelia.Chassis.ConnectedServer import ServerCore
 
from Kamaelia.Protocol.HTTP.HTTPServer import HTTPServer
 
Axon.Box.ShowAllTransits = False
 
from Kamaelia.Util.OneShot import OneShot
 
# This allows for configuring the request handlers in a nicer way. This is candidate
# for merging into the mainline code. Effectively this is a factory that creates functions
# capable of choosing which request handler to use.
 
def requestHandlers(URLHandlers, errorpages=None):
    if errorpages is None:
        import Kamaelia.Protocol.HTTP.ErrorPages as ErrorPages
        errorpages = ErrorPages
    def createRequestHandler(request):
        if request.get("bad"):
            return OneShot(errorpages.getErrorPage(400, request.get("errormsg","")))
        else:
            for (prefix, handler) in URLHandlers:
                if request["raw-uri"][:len(prefix)] == prefix:
                    request["uri-prefix-trigger"] = prefix
                    request["uri-suffix"] = request["raw-uri"][len(prefix):]
                    return handler(request)
 
        return OneShot(errorpages.getErrorPage(404, "No resource handlers could be found for the requested URL"))
 
    return createRequestHandler
 
class HelloHandler(Axon.Component.component):
    def __init__(self, request):
        super(HelloHandler, self).__init__()
        self.request = request
 
    def main(self):
        resource = {
           "statuscode"     : "200",
           "headers"    : [
                ("content-type", "text/html"),
           ]
        }
        self.send(resource, "outbox"); yield 1
        page = {
          "data" : "<html><body><h1>Hello World</h1><P>Woo!!</body></html>",
        }
        self.send(page, "outbox"); yield 1
        self.send(Axon.Ipc.producerFinished(self), "signal")
        yield 1
 
# ----------------------------------------------------------------------------------------------------
#
# Simple WSGI Handler
#
import time
def simple_app(environ, start_response):
    """Simplest possible application object""" 
    status = '200 OK'
    response_headers = [('Content-type','text/html'),('Pragma','no-cache')]
    start_response(status, response_headers)
    yield '<P> My Own Hello World!\n'
    for i in sorted(environ.keys()):
        yield "<li>%s: %s\n" % (i, environ[i])
    yield "<li> Date:" + time.ctime()
 
# ----------------------------------------------------------------------------------------------------
#
# Simple WSGI Handler
#
def HTML_WRAP(app):
    """
    Wraps the output of app in HTML
    """
    def gen(environ, start_response):
        """The standard WSGI interface"""
        yield "<html>\n"
        yield "<body>\n"
        for i in app(environ, start_response):
            yield i
        yield "</body>\n"
        yield "</html>\n"
    return gen
 
class _WSGIHandler(Axon.ThreadedComponent.threadedcomponent):
    """Choosing to run the WSGI app in a thread rather than the same
       context, this means we don't have to worry what they get up
       to really"""
    def __init__(self, app_name, request, app):
        super(_WSGIHandler, self).__init__()
        self.app_name = app_name
        self.request = request
        self.environ = request
        self.app = app
 
    def start_response(self, status, response_headers):
        self.status = status
        self.response_headers = response_headers
 
    def munge_headers(self):
        for header in self.environ["headers"]:
            cgi_varname = "HTTP_"+header.replace("-","_").upper()
            self.environ[cgi_varname] = self.environ["headers"][header]
        pprint.pprint(self.environ)
        pprint.pprint(self.environ["headers"])
 
    def main(self):
        required = "*** REQUIRED FIX THIS ***"
        headers = self.environ["headers"]
        self.environ["REQUEST_METHOD"] = required  # Required
        self.environ["SCRIPT_NAME"] = self.app_name  # Portion of URL that relates to the application object. May be empty. (eg /cgi-bin/test.pl)
        self.environ["PATH_INFO"] = self.environ["uri-suffix"]  # Remainder of request path after "SCRIPT_NAME", designating a content path may be empty.
 
        if self.environ["uri-suffix"].find("?") != -1:
            self.environ["QUERY_STRING"] = self.environ["uri-suffix"][self.environ["uri-suffix"].find("?")+1:]
        else:
            self.environ["QUERY_STRING"] = ""
        self.environ["CONTENT_TYPE"] = headers.get("content-type","") # Contents of an HTTP_CONTENT_TYPE field - may be absent or empty
        self.environ["CONTENT_LENGTH"] = headers.get("content-length","") # Contents of an HTTP_CONTENT_LENGTH field - may be absent or empty
        self.environ["SERVER_NAME"] = required     # Server name published to the outside world
        self.environ["SERVER_PORT"] = required     # Server port published to the outside world
        self.environ["SERVER_PROTOCOL"] = required # Version of protocol client _sent us_ (what they would like back)
 
        consider = " **CONSIDER ADDING THIS -- eg: "
        self.environ["SERVER_ADDR"] = consider + "192.168.2.9"
        self.environ["HTTP_REFERER"] = consider + "-"
        self.environ["SERVER_ADMIN"] = consider + "[no address given]"
        self.environ["SERVER_SIGNATURE"] = consider + "...."
        self.environ["SERVER_SOFTWARE"] = consider + "Apache/1.3.33 (Darwin)"
        self.environ["SCRIPT_FILENAME"] = consider + "/usr/local/httpd/sites/com.thwackety/cgi/test.pl"
        self.environ["DOCUMENT_ROOT"] = consider + "/usr/local/httpd/sites/com.thwackety/docs"
        self.environ["REQUEST_URI"] = consider + "/cgi-bin/test.pl"
        self.environ["SCRIPT_URL"] = consider + "/cgi-bin/test.pl"
        self.environ["SCRIPT_URI"] = consider + "http://thwackety.com/cgi-bin/test.pl"
        self.environ["REMOTE_ADDR"] = consider + "192.168.2.5"
        self.environ["REMOTE_PORT"] = consider + "56669"
        self.environ["DATE"] = consider + "Sat Sep 15 15:42:25 2007" #####
        self.environ["PATH"] = consider + "/bin:/sbin:/usr/bin:/usr/sbin:/usr/libexec:/System/Library/CoreServices"
        self.environ["GATEWAY_INTERFACE"] = consider + "CGI/1.1"
 
        self.munge_headers()
        R = [ x for x in self.app(self.environ, self.start_response) ]
        resource = {
           "statuscode" : self.status,
           "headers"    : self.response_headers,
        }
        self.send(resource, "outbox")
        for fragment in R:
            page = {
              "data" : fragment,
            }
            self.send(page, "outbox")
        self.send(Axon.Ipc.producerFinished(self), "signal")
 
def WSGIHandler(app_name, app):
    def R(request):
        return _WSGIHandler(app_name, request,app)
    return R
 
def HTTPProtocol():
    def foo(self,**argd):
        print self.routing
        return HTTPServer(requestHandlers(self.routing),**argd)
    return foo
 
# Finally we create the actual server and run it.
 
class WebServer(ServerCore):
    routing = [
               ["/wsgi", WSGIHandler("/wsgi", HTML_WRAP(simple_app)) ],
               ["/hello", HelloHandler],
              ]
    protocol=HTTPProtocol()
    port=8082
    socketOptions=(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 
WebServer().run()