~didrocks/unity/altf10

« back to all changes in this revision

Viewing changes to grackle/service.py

  • Committer: Aaron Bentley
  • Date: 2012-01-13 14:30:09 UTC
  • Revision ID: aaron@canonical.com-20120113143009-t0kzaz3czxnv1nwz
get_messagesĀ supportsĀ headerĀ parameter.

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, response='', reason=None, headers={}):
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
 
        return [response]
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])
56
 
                return self.send_response(httplib.CREATED)
57
 
            except Exception, error:
58
 
                return self.send_response(
59
 
                    httplib.BAD_REQUEST, reason=error.__doc__)
60
 
        elif len(self.path) == 2:
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
 
                return self.send_response(httplib.CREATED)
67
 
            except Exception, error:
68
 
                return self.send_response(
69
 
                    httplib.BAD_REQUEST, reason=error.__doc__)
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)
79
 
                response = simplejson.dumps(response)
80
 
                return self.send_response(httplib.OK, response=response)
81
 
            except Exception, error:
82
 
                return self.send_response(
83
 
                    httplib.BAD_REQUEST, reason=error.__doc__)
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)
90
 
            response = simplejson.dumps(response)
91
 
            return self.send_response(httplib.OK, response=response)
92
 
        except Exception, error:
93
 
            return self.send_response(
94
 
                httplib.BAD_REQUEST, reason=error.__doc__)
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()