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