~cosmin.lupu/+junk/penguintv

« back to all changes in this revision

Viewing changes to penguintv/ptvbittorrent/HTTPHandler.py

  • Committer: cosmin.lupu at gmail
  • Date: 2010-04-27 16:47:43 UTC
  • Revision ID: cosmin.lupu@gmail.com-20100427164743-ds8xrqonipp5ovdf
initial packaging

Show diffs side-by-side

added added

removed removed

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