~didrocks/unity/altf10

60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
1
__metaclass__ = type
2
3
import httplib
4
import logging
5
import os
6
from signal import SIGKILL
7
import simplejson
8
import sys
9
from wsgiref.simple_server import make_server
10
from wsgiref.util import shift_path_info
11
from grackle.store import (
12
    MemoryStore,
13
    )
14
15
16
class GrackleService:
17
    """A request handler that forwards to server.store."""
18
19
    def __init__(self, store):
20
        self.store = store
21
        self.logger = logging.getLogger('http')
22
23
    def __call__(self, environ, start_response):
24
        self.environ = environ
25
        self.start_response = start_response
26
        self.method = environ['REQUEST_METHOD']
27
        self.host_port = shift_path_info(environ)
28
        self.application = shift_path_info(environ)
29
        self.path = environ['PATH_INFO'].split('/')[1:]
30
        self.query_string = environ['QUERY_STRING']
31
        return self.handle_request()
32
33
    def handle_request(self):
34
        if self.method == 'PUT':
35
            return self.do_PUT()
36
        if self.method == 'POST':
37
            return self.do_POST()
38
        if self.method == 'GET':
39
            return self.do_GET()
40
67 by Curtis Hovey
Change argument order to reflect common usage.
41
    def send_response(self, code, response='', reason=None, headers={}):
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
42
        if reason is None:
62 by Curtis Hovey
No need to uppercase the reason.
43
            reason = httplib.responses[code]
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
44
        response_code = '%s %s' % (code, reason)
45
        response_headers = {'content-type': 'application/json'}
46
        response_headers.update(headers.items())
47
        self.start_response(response_code, response_headers.items())
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
48
        return [response]
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
49
50
    def do_PUT(self):
51
        """Create an archive or message on PUT."""
52
        if len(self.path) == 1:
53
            # This expected path is /archive/archive_id.
54
            try:
55
                self.store.put_archive(self.path[0])
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
56
                return self.send_response(httplib.CREATED)
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
57
            except Exception, error:
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
58
                return self.send_response(
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
59
                    httplib.BAD_REQUEST, reason=error.__doc__)
64 by Curtis Hovey
do_PUT aways returns a empty list.
60
        elif len(self.path) == 2:
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
61
            # This expected path is /archive/archive_id/message_id.
62
            try:
63
                put_input = self.environ['wsgi.input']
64
                message = put_input.read(int(self.environ['CONTENT_LENGTH']))
65
                self.store.put_message(self.path[0], self.path[1], message)
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
66
                return self.send_response(httplib.CREATED)
65 by Curtis Hovey
Always return the error reason.
67
            except Exception, error:
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
68
                return self.send_response(
65 by Curtis Hovey
Always return the error reason.
69
                    httplib.BAD_REQUEST, reason=error.__doc__)
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
70
71
    def do_POST(self):
72
        """Change a message on POST."""
73
        if len(self.path) == 2:
74
            # This expected path is /archive/archive_id/message_id.
75
            try:
76
                # This expected path is /archive/archive_id/message_id.
77
                response = self.store.hide_message(
78
                    self.path[0], self.path[1], self.query_string)
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
79
                response = simplejson.dumps(response)
80
                return self.send_response(httplib.OK, response=response)
65 by Curtis Hovey
Always return the error reason.
81
            except Exception, error:
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
82
                return self.send_response(
83
                    httplib.BAD_REQUEST, reason=error.__doc__)
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
84
85
    def do_GET(self):
86
        """Retrieve a list of messages on GET."""
87
        try:
88
            response = self.store.get_messages(
89
                self.path[0], self.query_string)
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
90
            response = simplejson.dumps(response)
91
            return self.send_response(httplib.OK, response=response)
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
92
        except Exception, error:
66 by Curtis Hovey
use send response to ensure a list of strings is returned.
93
            return self.send_response(
94
                httplib.BAD_REQUEST, reason=error.__doc__)
60 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
95
96
    def log_message(self, format, *args):
97
        """Override log_message to use standard Python logging."""
98
        message = "%s - - [%s] %s\n" % (
99
            self.address_string(), self.log_date_time_string(), format % args)
100
        self.logger.info(message)
101
102
103
class ForkedFakeService:
104
    """A Grackle service fake, as a ContextManager."""
105
106
    def __init__(self, port, message_archives=None, write_logs=False):
107
        """Constructor.
108
109
        :param port: The tcp port to use.
110
        :param message_archives: A dict of lists of dicts representing
111
            archives of messages. The outer dict represents the archive,
112
            the list represents the list of messages for that archive.
113
        :param write_logs: If true, log messages will be written to stdout.
114
        """
115
        self.pid = None
116
        self.port = port
117
        if message_archives is None:
118
            self.message_archives = {}
119
        else:
120
            self.message_archives = message_archives
121
        self.read_end, self.write_end = os.pipe()
122
        self.write_logs = write_logs
123
124
    @staticmethod
125
    def from_client(client, message_archives=None):
126
        """Instantiate a ForkedFakeService from the client.
127
128
        :param port: The client to provide service for.
129
        :param message_archives: A dict of lists of dicts representing
130
            archives of messages. The outer dict represents the archive,
131
            the list represents the list of messages for that archive.
132
        """
133
        return ForkedFakeService(client.port, message_archives)
134
135
    def is_ready(self):
136
        """Tell the parent process that the server is ready for writes."""
137
        os.write(self.write_end, 'asdf')
138
139
    def __enter__(self):
140
        """Run the service.
141
142
        Fork and start a server in the child.  Return when the server is ready
143
        for use."""
144
        pid = os.fork()
145
        if pid == 0:
146
            self.start_server()
147
        self.pid = pid
148
        os.read(self.read_end, 1)
149
        return
150
151
    def start_server(self):
152
        """Start the HTTP server."""
153
        app = GrackleService(MemoryStore(self.message_archives))
154
        service = make_server('', self.port, app)
155
        self.is_ready()
156
        if self.write_logs:
157
            logging.basicConfig(
158
                stream=sys.stderr, level=logging.INFO)
159
        service.serve_forever()
160
161
    def __exit__(self, exc_type, exc_val, traceback):
162
        os.kill(self.pid, SIGKILL)
163
164
165
def application(environ, start_response):
166
    start_response('200 OK', [('Content-Type', 'text/plain')])
167
    return "Hello World"
168
169
170
if __name__ == '__main__':
171
    app = GrackleService(MemoryStore({}))
172
    service = make_server('', 8787, app)
173
    service.serve_forever()