6
from signal import SIGKILL
9
from wsgiref.simple_server import make_server
10
from wsgiref.util import shift_path_info
11
from grackle.store import (
17
"""A request handler that forwards to server.store."""
19
def __init__(self, store):
21
self.logger = logging.getLogger('http')
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()
33
def handle_request(self):
34
if self.method == 'PUT':
36
if self.method == 'POST':
38
if self.method == 'GET':
41
def send_response(self, code, headers={}, reason=None):
43
reason = httplib.responses[code].upper()
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())
50
"""Create an archive or message on PUT."""
51
if len(self.path) == 1:
52
# This expected path is /archive/archive_id.
54
self.store.put_archive(self.path[0])
55
self.send_response(httplib.CREATED)
57
except Exception, error:
59
httplib.BAD_REQUEST, reason=error.__doc__)
61
if len(self.path) == 2:
62
# This expected path is /archive/archive_id/message_id.
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)
70
self.send_response(httplib.BAD_REQUEST)
74
"""Change a message on POST."""
75
if len(self.path) == 2:
76
# This expected path is /archive/archive_id/message_id.
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)]
84
self.send_response(httplib.BAD_REQUEST)
88
"""Retrieve a list of messages on GET."""
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__)
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)
105
class ForkedFakeService:
106
"""A Grackle service fake, as a ContextManager."""
108
def __init__(self, port, message_archives=None, write_logs=False):
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.
119
if message_archives is None:
120
self.message_archives = {}
122
self.message_archives = message_archives
123
self.read_end, self.write_end = os.pipe()
124
self.write_logs = write_logs
127
def from_client(client, message_archives=None):
128
"""Instantiate a ForkedFakeService from the client.
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.
135
return ForkedFakeService(client.port, message_archives)
138
"""Tell the parent process that the server is ready for writes."""
139
os.write(self.write_end, 'asdf')
144
Fork and start a server in the child. Return when the server is ready
150
os.read(self.read_end, 1)
153
def start_server(self):
154
"""Start the HTTP server."""
155
app = GrackleService(MemoryStore(self.message_archives))
156
service = make_server('', self.port, app)
160
stream=sys.stderr, level=logging.INFO)
161
service.serve_forever()
163
def __exit__(self, exc_type, exc_val, traceback):
164
os.kill(self.pid, SIGKILL)
167
def application(environ, start_response):
168
start_response('200 OK', [('Content-Type', 'text/plain')])
172
if __name__ == '__main__':
173
app = GrackleService(MemoryStore({}))
174
service = make_server('', 8787, app)
175
service.serve_forever()