~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/service.py

  • Committer: William Grant
  • Date: 2012-01-25 06:19:56 UTC
  • Revision ID: william.grant@canonical.com-20120125061956-4tltjt6a4xf5yufj
Fix test.

Show diffs side-by-side

added added

removed removed

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