Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
36 kaklik 1
# Written by Bram Cohen
2
# see LICENSE.txt for license information
3
 
4
from cStringIO import StringIO
5
from sys import stdout
6
import time
7
from clock import clock
8
from gzip import GzipFile
9
try:
10
    True
11
except:
12
    True = 1
13
    False = 0
14
 
15
DEBUG = False
16
 
17
weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
18
 
19
months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
20
    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
21
 
22
class HTTPConnection:
23
    def __init__(self, handler, connection):
24
        self.handler = handler
25
        self.connection = connection
26
        self.buf = ''
27
        self.closed = False
28
        self.done = False
29
        self.donereading = False
30
        self.next_func = self.read_type
31
 
32
    def get_ip(self):
33
        return self.connection.get_ip()
34
 
35
    def data_came_in(self, data):
36
        if self.donereading or self.next_func is None:
37
            return True
38
        self.buf += data
39
        while True:
40
            try:
41
                i = self.buf.index('\n')
42
            except ValueError:
43
                return True
44
            val = self.buf[:i]
45
            self.buf = self.buf[i+1:]
46
            self.next_func = self.next_func(val)
47
            if self.donereading:
48
                return True
49
            if self.next_func is None or self.closed:
50
                return False
51
 
52
    def read_type(self, data):
53
        self.header = data.strip()
54
        words = data.split()
55
        if len(words) == 3:
56
            self.command, self.path, garbage = words
57
            self.pre1 = False
58
        elif len(words) == 2:
59
            self.command, self.path = words
60
            self.pre1 = True
61
            if self.command != 'GET':
62
                return None
63
        else:
64
            return None
65
        if self.command not in ('HEAD', 'GET'):
66
            return None
67
        self.headers = {}
68
        return self.read_header
69
 
70
    def read_header(self, data):
71
        data = data.strip()
72
        if data == '':
73
            self.donereading = True
74
            if self.headers.get('accept-encoding','').find('gzip') > -1:
75
                self.encoding = 'gzip'
76
            else:
77
                self.encoding = 'identity'
78
            r = self.handler.getfunc(self, self.path, self.headers)
79
            if r is not None:
80
                self.answer(r)
81
            return None
82
        try:
83
            i = data.index(':')
84
        except ValueError:
85
            return None
86
        self.headers[data[:i].strip().lower()] = data[i+1:].strip()
87
        if DEBUG:
88
            print data[:i].strip() + ": " + data[i+1:].strip()
89
        return self.read_header
90
 
91
    def answer(self, (responsecode, responsestring, headers, data)):
92
        if self.closed:
93
            return
94
        if self.encoding == 'gzip':
95
            compressed = StringIO()
96
            gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9)
97
            gz.write(data)
98
            gz.close()
99
            cdata = compressed.getvalue()
100
            if len(cdata) >= len(data):
101
                self.encoding = 'identity'
102
            else:
103
                if DEBUG:
104
                   print "Compressed: %i  Uncompressed: %i\n" % (len(cdata),len(data))
105
                data = cdata
106
                headers['Content-Encoding'] = 'gzip'
107
 
108
        # i'm abusing the identd field here, but this should be ok
109
        if self.encoding == 'identity':
110
            ident = '-'
111
        else:
112
            ident = self.encoding
113
        self.handler.log( self.connection.get_ip(), ident, '-',
114
                          self.header, responsecode, len(data),
115
                          self.headers.get('referer','-'),
116
                          self.headers.get('user-agent','-') )
117
        self.done = True
118
        r = StringIO()
119
        r.write('HTTP/1.0 ' + str(responsecode) + ' ' + 
120
            responsestring + '\r\n')
121
        if not self.pre1:
122
            headers['Content-Length'] = len(data)
123
            for key, value in headers.items():
124
                r.write(key + ': ' + str(value) + '\r\n')
125
            r.write('\r\n')
126
        if self.command != 'HEAD':
127
            r.write(data)
128
        self.connection.write(r.getvalue())
129
        if self.connection.is_flushed():
130
            self.connection.shutdown(1)
131
 
132
class HTTPHandler:
133
    def __init__(self, getfunc, minflush):
134
        self.connections = {}
135
        self.getfunc = getfunc
136
        self.minflush = minflush
137
        self.lastflush = clock()
138
 
139
    def external_connection_made(self, connection):
140
        self.connections[connection] = HTTPConnection(self, connection)
141
 
142
    def connection_flushed(self, connection):
143
        if self.connections[connection].done:
144
            connection.shutdown(1)
145
 
146
    def connection_lost(self, connection):
147
        ec = self.connections[connection]
148
        ec.closed = True
149
        del ec.connection
150
        del ec.next_func
151
        del self.connections[connection]
152
 
153
    def data_came_in(self, connection, data):
154
        c = self.connections[connection]
155
        if not c.data_came_in(data) and not c.closed:
156
            c.connection.shutdown(1)
157
 
158
    def log(self, ip, ident, username, header,
159
            responsecode, length, referrer, useragent):
160
        year, month, day, hour, minute, second, a, b, c = time.localtime(time.time())
161
        print '%s %s %s [%02d/%3s/%04d:%02d:%02d:%02d] "%s" %i %i "%s" "%s"' % (
162
            ip, ident, username, day, months[month], year, hour,
163
            minute, second, header, responsecode, length, referrer, useragent)
164
        t = clock()
165
        if t - self.lastflush > self.minflush:
166
            self.lastflush = t
167
            stdout.flush()