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, response='', reason=None, headers={}):
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())
51
"""Create an archive or message on PUT."""
52
if len(self.path) == 1:
53
# This expected path is /archive/archive_id.
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.
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__)
72
"""Change a message on POST."""
73
if len(self.path) == 2:
74
# This expected path is /archive/archive_id/message_id.
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__)
86
"""Retrieve a list of messages on GET."""
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__)
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)
103
class ForkedFakeService:
104
"""A Grackle service fake, as a ContextManager."""
106
def __init__(self, port, message_archives=None, write_logs=False):
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.
117
if message_archives is None:
118
self.message_archives = {}
120
self.message_archives = message_archives
121
self.read_end, self.write_end = os.pipe()
122
self.write_logs = write_logs
125
def from_client(client, message_archives=None):
126
"""Instantiate a ForkedFakeService from the client.
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.
133
return ForkedFakeService(client.port, message_archives)
136
"""Tell the parent process that the server is ready for writes."""
137
os.write(self.write_end, 'asdf')
142
Fork and start a server in the child. Return when the server is ready
148
os.read(self.read_end, 1)
151
def start_server(self):
152
"""Start the HTTP server."""
153
app = GrackleService(MemoryStore(self.message_archives))
154
service = make_server('', self.port, app)
158
stream=sys.stderr, level=logging.INFO)
159
service.serve_forever()
161
def __exit__(self, exc_type, exc_val, traceback):
162
os.kill(self.pid, SIGKILL)
165
def application(environ, start_response):
166
start_response('200 OK', [('Content-Type', 'text/plain')])
170
if __name__ == '__main__':
171
app = GrackleService(MemoryStore({}))
172
service = make_server('', 8787, app)
173
service.serve_forever()