~unity-2d-team/unity-2d/Shell-MultiMonitor

6.1.54 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
41
    def send_response(self, code, headers={}, reason=None):
42
        if reason is None:
6.1.56 by Curtis Hovey
No need to uppercase the reason.
43
            reason = httplib.responses[code]
6.1.54 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())
48
49
    def do_PUT(self):
50
        """Create an archive or message on PUT."""
51
        if len(self.path) == 1:
52
            # This expected path is /archive/archive_id.
53
            try:
54
                self.store.put_archive(self.path[0])
55
                self.send_response(httplib.CREATED)
56
            except Exception, error:
57
                self.send_response(
58
                    httplib.BAD_REQUEST, reason=error.__doc__)
6.1.58 by Curtis Hovey
do_PUT aways returns a empty list.
59
        elif len(self.path) == 2:
6.1.54 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
60
            # This expected path is /archive/archive_id/message_id.
61
            try:
62
                put_input = self.environ['wsgi.input']
63
                message = put_input.read(int(self.environ['CONTENT_LENGTH']))
64
                self.store.put_message(self.path[0], self.path[1], message)
65
                self.send_response(httplib.CREATED)
66
            except:
67
                self.send_response(httplib.BAD_REQUEST)
6.1.58 by Curtis Hovey
do_PUT aways returns a empty list.
68
        return []
6.1.54 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
69
70
    def do_POST(self):
71
        """Change a message on POST."""
72
        if len(self.path) == 2:
73
            # This expected path is /archive/archive_id/message_id.
74
            try:
75
                # This expected path is /archive/archive_id/message_id.
76
                response = self.store.hide_message(
77
                    self.path[0], self.path[1], self.query_string)
78
                self.send_response(httplib.OK)
79
                return [simplejson.dumps(response)]
80
            except:
81
                self.send_response(httplib.BAD_REQUEST)
6.1.57 by Curtis Hovey
Do not return a needless empty string.
82
                return []
6.1.54 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
83
84
    def do_GET(self):
85
        """Retrieve a list of messages on GET."""
86
        try:
87
            response = self.store.get_messages(
88
                self.path[0], self.query_string)
89
            self.send_response(httplib.OK)
90
            return [simplejson.dumps(response)]
91
        except Exception, error:
92
            self.send_response(httplib.BAD_REQUEST, reason=error.__doc__)
6.1.57 by Curtis Hovey
Do not return a needless empty string.
93
            return []
6.1.54 by Curtis Hovey
Converted gracle from a request handler to a wsgi app.
94
95
    def log_message(self, format, *args):
96
        """Override log_message to use standard Python logging."""
97
        message = "%s - - [%s] %s\n" % (
98
            self.address_string(), self.log_date_time_string(), format % args)
99
        self.logger.info(message)
100
101
102
class ForkedFakeService:
103
    """A Grackle service fake, as a ContextManager."""
104
105
    def __init__(self, port, message_archives=None, write_logs=False):
106
        """Constructor.
107
108
        :param port: The tcp port to use.
109
        :param message_archives: A dict of lists of dicts representing
110
            archives of messages. The outer dict represents the archive,
111
            the list represents the list of messages for that archive.
112
        :param write_logs: If true, log messages will be written to stdout.
113
        """
114
        self.pid = None
115
        self.port = port
116
        if message_archives is None:
117
            self.message_archives = {}
118
        else:
119
            self.message_archives = message_archives
120
        self.read_end, self.write_end = os.pipe()
121
        self.write_logs = write_logs
122
123
    @staticmethod
124
    def from_client(client, message_archives=None):
125
        """Instantiate a ForkedFakeService from the client.
126
127
        :param port: The client to provide service for.
128
        :param message_archives: A dict of lists of dicts representing
129
            archives of messages. The outer dict represents the archive,
130
            the list represents the list of messages for that archive.
131
        """
132
        return ForkedFakeService(client.port, message_archives)
133
134
    def is_ready(self):
135
        """Tell the parent process that the server is ready for writes."""
136
        os.write(self.write_end, 'asdf')
137
138
    def __enter__(self):
139
        """Run the service.
140
141
        Fork and start a server in the child.  Return when the server is ready
142
        for use."""
143
        pid = os.fork()
144
        if pid == 0:
145
            self.start_server()
146
        self.pid = pid
147
        os.read(self.read_end, 1)
148
        return
149
150
    def start_server(self):
151
        """Start the HTTP server."""
152
        app = GrackleService(MemoryStore(self.message_archives))
153
        service = make_server('', self.port, app)
154
        self.is_ready()
155
        if self.write_logs:
156
            logging.basicConfig(
157
                stream=sys.stderr, level=logging.INFO)
158
        service.serve_forever()
159
160
    def __exit__(self, exc_type, exc_val, traceback):
161
        os.kill(self.pid, SIGKILL)
162
163
164
def application(environ, start_response):
165
    start_response('200 OK', [('Content-Type', 'text/plain')])
166
    return "Hello World"
167
168
169
if __name__ == '__main__':
170
    app = GrackleService(MemoryStore({}))
171
    service = make_server('', 8787, app)
172
    service.serve_forever()