-
Notifications
You must be signed in to change notification settings - Fork 0
/
http.py
113 lines (105 loc) · 3.27 KB
/
http.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python
"""
HTTP-parsing for httpxlog app
Couldn't find a decent standalone
"""
import re
from util import spl, flatten, dict1
class HTTP_NotHTTP(Exception):
def __init__(self):
pass
class HTTP_Req:
def __init__(self, s, ts):
m = re.match(r'^(GET|POST|PUT|DELETE|TRACE|CONNECT|OPTIONS|HEAD) (\S{1,4096}) HTTP/(1.\d)\r\n', s)
if not m:
raise HTTP_NotHTTP
self.method, self.fullpath, self.httpver = m.groups()
headers, self.payload = spl(s, '\r\n\r\n')
self.headers = dict1([h.split(': ', 1) for h in headers.split('\r\n')[1:]])
self.path, self.fragment = spl(self.fullpath, '#')
self.path, self.querystring = spl(self.path, '?')
self.query = dict1([spl(s, '=')
for s in re.split('&(?:amp;)?', self.querystring)]) \
if self.querystring else {}
self.host = self.headers.get('host', [''])[0]
self.url = 'http://' + self.host + self.path
self.fullurl = 'http://' + self.host + self.fullpath
self.ts = ts
self.resp = None
self.key = None
def __repr__(self):
return 'REQ %s %s %s %s ?=%s' % (
self.key, self.method, self.path, self.httpver,
self.query)
def __str__(self):
return repr(self)
def geturl(self, opts):
return self.fullurl if opts.include_query else self.url
def referer(self, url):
return self.headers.get('referer', [url])[0]
def cookies(self):
return ';'.join(self.headers.get('cookie',[]))
def each_cookie(self):
cookies = self.cookies()
if not cookies:
return []
kv = flatten(c.strip().split('&') for c in cookies.split(';'))
return [spl(val, '=') for val in kv]
class HTTP_Resp:
def __init__(self, s, ts):
m = re.match(r'^HTTP/(1.\d) (\d{3}) (?:[^\r\n]{1,64})\r\n', s)
if not m:
raise HTTP_NotHTTP
self.httpver, self.code = m.groups()
headers, self.payload = spl(s, '\r\n\r\n')
self.headers = dict1([h.split(': ', 1) for h in headers.split('\r\n')[1:]])
self.ts_start = ts
self.ts_last = None
self.size = len(s)
def __repr__(self):
return 'RESP code=%3u httpver=%s' % (self.code, self.httpver)
def __str__(self):
return repr(self)
class HTTP_ReqCache:
""""""
def __init__(self, act, max_age_sec=120):
self.act = act
self.max_age_sec = max_age_sec
self.map = {} # requests keyed by IP:IP:srcport
self.ordered = [] # requests in order to facilitate timeout
def keyresp(self, ip, tcp):
return '%s:%s:%s' % (ip.destination, ip.source, tcp.destinationport)
def keyreq(self, ip, tcp):
return '%s:%s:%s' % (ip.source, ip.destination, tcp.sourceport)
def mapadd(self, req):
if req.key not in self.map:
self.map[req.key] = [req]
else:
self.map[req.key].append(req)
def add(self, ip, tcp, req):
req.key = self.keyreq(ip, tcp)
self.mapadd(req)
self.ordered.append(req)
def get(self, ip, tcp, dest=True):
key = (self.keyresp if dest else self.keyreq)(ip, tcp)
try:
return self.map[key][0]
except KeyError, IndexError:
pass
def remove(self, req):
try:
r = self.map[req.key]
r.pop(0)
if not r:
del self.map[req.key]
except KeyError:
pass
self.ordered.remove(req)
def check(self, ts):
"""timeout stale requests"""
while self.ordered and ts - self.ordered[0].ts > self.max_age_sec:
too_old = self.ordered[0]
self.remove(too_old)
self.act.on_http_resp_timeout(too_old, ts)
def __len__(self):
return sum(len(v) for v in self.map.itervalues())