lateefj / Frisky (http://src.hackingthought.com/projects/frisky/)
Frisky is an Asynchronous WSGI web server. Cross-platform, high performance, caching and compression API's included, and lightweight. Test server running Frisky: http://src.hackingthought.com/
| commit 6: | 7d32ad212ad5 |
| parent 5: | ac05bec4f8c8 |
| branch: | default |
Changed (Δ2.4 KB):
.hgignore (1 lines added, 1 lines removed)
TODO.txt (42 lines added, 2 lines removed)
frisky/httpd.py (37 lines added, 14 lines removed)
frisky/oracle/io.py (1 lines added, 1 lines removed)
frisky/request.py (2 lines added, 2 lines removed)
samples/pylons_sample/run.py (1 lines added, 1 lines removed)
samples/selector_sample/run.py (6 lines added, 0 lines removed)
1 |
WSGI support: |
|
2 |
* SELECTOR |
|
1 |
Server Configuration: |
|
2 |
* Static files |
|
3 |
* regex configuration which can be integrated from older code |
|
4 |
* cache and compression |
|
5 |
* URL mapping |
|
6 |
* Map urls to web frameworks |
|
7 |
* Domain aliases |
|
8 |
* Redirect using regex |
|
9 |
* Other |
|
10 |
* Logging |
|
11 |
* Port |
|
12 |
* Restart using memento |
|
13 |
* import memento |
|
14 |
* app = memento.Assassin("frisky:frisky", ["frisky"]) |
|
15 |
||
16 |
WSGI Frameworks: |
|
3 |
17 |
* Pylons |
18 |
* Controller support |
|
19 |
* Database support |
|
20 |
* Read the configuration file for serving static files |
|
21 |
* Django |
|
22 |
* Basic startup |
|
23 |
* Controller (whatever) support |
|
24 |
* Database support |
|
25 |
* Admin tool |
|
26 |
* read configuration for serving static files |
|
27 |
* TurboGears |
|
28 |
* Basic startup |
|
29 |
* Controller support |
|
30 |
* Database support |
|
31 |
* read configuration for serving static files (should be same as Pylons) |
|
32 |
||
33 |
Optimization: |
|
34 |
* Pyevent (could use python implementation for getting started (http://code.google.com/p/registeredeventlistener/)) |
|
35 |
* pyprocessing using additional processes to increase peformance |
|
36 |
* Cython |
|
37 |
* Profile and use Cython on slowest/most used code |
|
38 |
* Use for more direct access to pyevent/libevent |
|
39 |
* Caching |
|
40 |
* Use high level cache |
|
41 |
* Static files |
|
42 |
* Use async push file system to server files |
|
43 |
||
4 |
44 |
|
5 |
45 |
Caching: |
6 |
46 |
Need to support cache invalidation programmatically and cache invalidation timeouts |
Up to file-list frisky/httpd.py:
| … | … | @@ -276,9 +276,9 @@ def test_handler(environ, reponse): |
276 |
276 |
return ['Hello World from test'] |
277 |
277 |
|
278 |
278 |
from frisky import request |
279 |
#import memento |
|
280 |
#app = memento.Assassin("frisky:frisky", ["frisky"]) |
|
281 |
279 |
import logging |
280 |
||
281 |
APP_MEMENTO = None |
|
282 |
282 |
class WSGIHandler(RequestHandler): |
283 |
283 |
""" |
284 |
284 |
The only thing that this class must do is setup the |
| … | … | @@ -288,29 +288,45 @@ class WSGIHandler(RequestHandler): |
288 |
288 |
|
289 |
289 |
def handle_data(self): |
290 |
290 |
""" |
291 |
Env vars needed |
|
292 |
* SCRIPT_NAME |
|
293 |
* QUERY_STING |
|
294 |
||
291 |
It basically does a brute force to populate the environ. |
|
295 |
292 |
""" |
296 |
293 |
self.rfile.seek(0) |
294 |
# OPTIMIZATION: This could be done faster somehow |
|
295 |
content_length = len(self.rfile.read()) |
|
296 |
# Parse the path to find the query string |
|
297 |
delimiter = self.path.find('?') |
|
298 |
query_string = '' |
|
299 |
if delimiter > 0: |
|
300 |
query_string = self.path[delimiter+1:] |
|
301 |
||
302 |
self.rfile.seek(0) |
|
297 |
303 |
lines = self.rfile.readlines() |
298 |
env = {'PATH_INFO':self.path, |
|
304 |
env = {'PATH_INFO':self.path, 'CONTENT_LENGTH':content_length, |
|
305 |
'QUERY_STING':query_string} |
|
306 |
# OPTIMIZATION: The environ population could be made faster |
|
307 |
# with a better algorithm? |
|
299 |
308 |
headers = {} |
300 |
309 |
for l in lines: |
301 |
310 |
parts = l.split(':') |
302 |
311 |
if len(parts) > 1: |
312 |
# Special WSGI population variable |
|
303 |
313 |
if parts[0] == 'Host': |
304 |
env['SERVER_NAME'] = parts[1] |
|
314 |
env['SERVER_NAME'] = parts[1].strip() |
|
305 |
315 |
if len(parts) > 2: |
306 |
316 |
env['SERVER_PORT'] = parts[2] |
307 |
317 |
else: |
308 |
318 |
env['SERVER_PORT'] = 80 |
309 |
|
|
319 |
elif len(parts) > 2: |
|
320 |
# if value has embeded : in it |
|
321 |
headers[parts[0]] = ':'.join(parts[1:len(parts)]).strip() |
|
322 |
else: |
|
323 |
# If just simple name:value |
|
324 |
headers[parts[0]] = parts[1].strip() |
|
310 |
325 |
|
311 |
326 |
environ = request.Environ() |
327 |
environ.update_method({'REQUEST_METHOD':self.command}) |
|
312 |
328 |
environ.update_uri(env) |
313 |
#print(' |
|
329 |
#print('env %s' % env) |
|
314 |
330 |
environ.update_headers(headers) |
315 |
331 |
environ.update_headers(self.headers) |
316 |
332 |
#print('request line is %s' % self.parse_request()) |
| … | … | @@ -319,18 +335,25 @@ class WSGIHandler(RequestHandler): |
319 |
335 |
'SCRIPT_NAME':self.path, |
320 |
336 |
'SERVER_PROTOCOL':self.request_version, |
321 |
337 |
}) |
322 |
|
|
338 |
||
323 |
339 |
|
324 |
340 |
response = request.StartResponse() |
325 |
341 |
res = self.callback(environ, response) |
342 |
||
343 |
self.wfile.write(str(response)) |
|
344 |
||
326 |
345 |
for r in res: |
327 |
346 |
self.wfile.write(r) |
328 |
|
|
347 |
||
329 |
348 |
|
330 |
349 |
|
331 |
def start(host='0.0.0.0', port=7777, callback=None |
|
350 |
def start(host='0.0.0.0', port=7777, callback=None, packages=[]): |
|
351 |
""" |
|
352 |
import memento |
|
353 |
for p in packages: |
|
354 |
APP_MEMENTO = memento.Assassin("%s:%s" % (p, p), ["%s" % p]) |
|
355 |
""" |
|
332 |
356 |
# launch the server on the specified port |
333 |
port = 7777 |
|
334 |
357 |
s = Server(host, port, WSGIHandler, callback=callback) |
335 |
358 |
print "SimpleAsyncHTTPServer running on port %s" %port |
336 |
359 |
try: |
Up to file-list frisky/oracle/io.py:
| … | … | @@ -4,7 +4,7 @@ The io module is design to improve perfo |
4 |
4 |
import cStringIO |
5 |
5 |
import gzip |
6 |
6 |
|
7 |
from frisky |
|
7 |
from frisky.oracle import cache |
|
8 |
8 |
|
9 |
9 |
CACHE_TYPE = cache.RAM |
10 |
10 |
Up to file-list frisky/request.py:
| … | … | @@ -133,8 +133,8 @@ class StartResponse: |
133 |
133 |
self.cookies[key] = '' |
134 |
134 |
self.cookies[key]['max-age'] = "0" |
135 |
135 |
def __str__(self): |
136 |
#res = "HTTP/1.1 %s %s\r\n" % (self.status_code, self.status_reasons) |
|
137 |
res="" |
|
136 |
res = "HTTP/1.1 %s %s\r\n" % (self.status_code, self.status_reasons) |
|
137 |
#res="" |
|
138 |
138 |
for key, val in self.response_headers.items(): |
139 |
139 |
if key.upper() == "SET-COOKIE": |
140 |
140 |
res += "%s\r\n" % val |
Up to file-list samples/pylons_sample/run.py:
| … | … | @@ -8,5 +8,5 @@ from frisky import httpd |
8 |
8 |
|
9 |
9 |
|
10 |
10 |
|
11 |
httpd.start(callback=application |
|
11 |
httpd.start(callback=application, packages=['pylons_sample']) |
|
12 |
12 |
Up to file-list samples/selector_sample/run.py:
| … | … | @@ -8,7 +8,13 @@ Note: Yaro performance is horrible so no |
8 |
8 |
import selector |
9 |
9 |
|
10 |
10 |
from frisky import httpd |
11 |
from frisky.oracle.io import optimize |
|
11 |
12 |
|
13 |
# It seems to have a small performance impact |
|
14 |
# not sure why but it is a bit slower using the cache |
|
15 |
# might just be more code in the way. |
|
16 |
# This is such an unrealistic example |
|
17 |
@optimize(compress=False, cache=True, cache_key='hello_name') |
|
12 |
18 |
def hello_name(environ, start_response): |
13 |
19 |
name = environ['selector.vars']['name'] |
14 |
20 |
return ["Hello %s!" % name] |
