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]
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)
56
except Exception, error:
58
httplib.BAD_REQUEST, reason=error.__doc__)
59
elif len(self.path) == 2:
60
# This expected path is /archive/archive_id/message_id.
62
put_input = self.environ['wsgi.input']
63
message = put_input.read(int(self.environ['CONTENT_LENGTH']))
64
self.store.put_message(self.path[0], self.path[1], message)
65
self.send_response(httplib.CREATED)
67
self.send_response(httplib.BAD_REQUEST)
71
"""Change a message on POST."""
72
if len(self.path) == 2:
73
# This expected path is /archive/archive_id/message_id.
75
# This expected path is /archive/archive_id/message_id.
76
response = self.store.hide_message(
77
self.path[0], self.path[1], self.query_string)
78
self.send_response(httplib.OK)
79
return [simplejson.dumps(response)]
81
self.send_response(httplib.BAD_REQUEST)
85
"""Retrieve a list of messages on GET."""
87
response = self.store.get_messages(
88
self.path[0], self.query_string)
89
self.send_response(httplib.OK)
90
return [simplejson.dumps(response)]
91
except Exception, error:
92
self.send_response(httplib.BAD_REQUEST, reason=error.__doc__)
95
def log_message(self, format, *args):
96
"""Override log_message to use standard Python logging."""
97
message = "%s - - [%s] %s\n" % (
98
self.address_string(), self.log_date_time_string(), format % args)
99
self.logger.info(message)
102
class ForkedFakeService:
103
"""A Grackle service fake, as a ContextManager."""
105
def __init__(self, port, message_archives=None, write_logs=False):
108
:param port: The tcp port to use.
109
:param message_archives: A dict of lists of dicts representing
110
archives of messages. The outer dict represents the archive,
111
the list represents the list of messages for that archive.
112
:param write_logs: If true, log messages will be written to stdout.
116
if message_archives is None:
117
self.message_archives = {}
119
self.message_archives = message_archives
120
self.read_end, self.write_end = os.pipe()
121
self.write_logs = write_logs
124
def from_client(client, message_archives=None):
125
"""Instantiate a ForkedFakeService from the client.
127
:param port: The client to provide service for.
128
:param message_archives: A dict of lists of dicts representing
129
archives of messages. The outer dict represents the archive,
130
the list represents the list of messages for that archive.
132
return ForkedFakeService(client.port, message_archives)
135
"""Tell the parent process that the server is ready for writes."""
136
os.write(self.write_end, 'asdf')
141
Fork and start a server in the child. Return when the server is ready
147
os.read(self.read_end, 1)
150
def start_server(self):
151
"""Start the HTTP server."""
152
app = GrackleService(MemoryStore(self.message_archives))
153
service = make_server('', self.port, app)
157
stream=sys.stderr, level=logging.INFO)
158
service.serve_forever()
160
def __exit__(self, exc_type, exc_val, traceback):
161
os.kill(self.pid, SIGKILL)
164
def application(environ, start_response):
165
start_response('200 OK', [('Content-Type', 'text/plain')])
169
if __name__ == '__main__':
170
app = GrackleService(MemoryStore({}))
171
service = make_server('', 8787, app)
172
service.serve_forever()