git.fiddlerwoaroof.com
Ed L authored on 23/06/2011 22:07:23
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,1545 +0,0 @@
1
-# -*- test-case-name: twisted.web.test.test_http -*-
2
-# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
3
-# See LICENSE for details.
4
-
5
-"""
6
-HyperText Transfer Protocol implementation.
7
-
8
-This is used by twisted.web.
9
-
10
-Future Plans:
11
- - HTTP client support will at some point be refactored to support HTTP/1.1.
12
- - Accept chunked data from clients in server.
13
- - Other missing HTTP features from the RFC.
14
-
15
-Maintainer: Itamar Shtull-Trauring
16
-"""
17
-
18
-# system imports
19
-from cStringIO import StringIO
20
-import tempfile
21
-import base64, binascii
22
-import cgi
23
-import socket
24
-import math
25
-import time
26
-import calendar
27
-import warnings
28
-import os
29
-from urlparse import urlparse as _urlparse
30
-
31
-from zope.interface import implements
32
-
33
-# twisted imports
34
-from twisted.internet import interfaces, reactor, protocol, address
35
-from twisted.protocols import policies, basic
36
-from twisted.python import log
37
-try: # try importing the fast, C version
38
-    from twisted.protocols._c_urlarg import unquote
39
-except ImportError:
40
-    from urllib import unquote
41
-
42
-from twisted.web.http_headers import _DictHeaders, Headers
43
-
44
-protocol_version = "HTTP/1.1"
45
-
46
-_CONTINUE = 100
47
-SWITCHING = 101
48
-
49
-OK                              = 200
50
-CREATED                         = 201
51
-ACCEPTED                        = 202
52
-NON_AUTHORITATIVE_INFORMATION   = 203
53
-NO_CONTENT                      = 204
54
-RESET_CONTENT                   = 205
55
-PARTIAL_CONTENT                 = 206
56
-MULTI_STATUS                    = 207
57
-
58
-MULTIPLE_CHOICE                 = 300
59
-MOVED_PERMANENTLY               = 301
60
-FOUND                           = 302
61
-SEE_OTHER                       = 303
62
-NOT_MODIFIED                    = 304
63
-USE_PROXY                       = 305
64
-TEMPORARY_REDIRECT              = 307
65
-
66
-BAD_REQUEST                     = 400
67
-UNAUTHORIZED                    = 401
68
-PAYMENT_REQUIRED                = 402
69
-FORBIDDEN                       = 403
70
-NOT_FOUND                       = 404
71
-NOT_ALLOWED                     = 405
72
-NOT_ACCEPTABLE                  = 406
73
-PROXY_AUTH_REQUIRED             = 407
74
-REQUEST_TIMEOUT                 = 408
75
-CONFLICT                        = 409
76
-GONE                            = 410
77
-LENGTH_REQUIRED                 = 411
78
-PRECONDITION_FAILED             = 412
79
-REQUEST_ENTITY_TOO_LARGE        = 413
80
-REQUEST_URI_TOO_LONG            = 414
81
-UNSUPPORTED_MEDIA_TYPE          = 415
82
-REQUESTED_RANGE_NOT_SATISFIABLE = 416
83
-EXPECTATION_FAILED              = 417
84
-
85
-INTERNAL_SERVER_ERROR           = 500
86
-NOT_IMPLEMENTED                 = 501
87
-BAD_GATEWAY                     = 502
88
-SERVICE_UNAVAILABLE             = 503
89
-GATEWAY_TIMEOUT                 = 504
90
-HTTP_VERSION_NOT_SUPPORTED      = 505
91
-INSUFFICIENT_STORAGE_SPACE      = 507
92
-NOT_EXTENDED                    = 510
93
-
94
-RESPONSES = {
95
-    # 100
96
-    _CONTINUE: "Continue",
97
-    SWITCHING: "Switching Protocols",
98
-
99
-    # 200
100
-    OK: "OK",
101
-    CREATED: "Created",
102
-    ACCEPTED: "Accepted",
103
-    NON_AUTHORITATIVE_INFORMATION: "Non-Authoritative Information",
104
-    NO_CONTENT: "No Content",
105
-    RESET_CONTENT: "Reset Content.",
106
-    PARTIAL_CONTENT: "Partial Content",
107
-    MULTI_STATUS: "Multi-Status",
108
-
109
-    # 300
110
-    MULTIPLE_CHOICE: "Multiple Choices",
111
-    MOVED_PERMANENTLY: "Moved Permanently",
112
-    FOUND: "Found",
113
-    SEE_OTHER: "See Other",
114
-    NOT_MODIFIED: "Not Modified",
115
-    USE_PROXY: "Use Proxy",
116
-    # 306 not defined??
117
-    TEMPORARY_REDIRECT: "Temporary Redirect",
118
-
119
-    # 400
120
-    BAD_REQUEST: "Bad Request",
121
-    UNAUTHORIZED: "Unauthorized",
122
-    PAYMENT_REQUIRED: "Payment Required",
123
-    FORBIDDEN: "Forbidden",
124
-    NOT_FOUND: "Not Found",
125
-    NOT_ALLOWED: "Method Not Allowed",
126
-    NOT_ACCEPTABLE: "Not Acceptable",
127
-    PROXY_AUTH_REQUIRED: "Proxy Authentication Required",
128
-    REQUEST_TIMEOUT: "Request Time-out",
129
-    CONFLICT: "Conflict",
130
-    GONE: "Gone",
131
-    LENGTH_REQUIRED: "Length Required",
132
-    PRECONDITION_FAILED: "Precondition Failed",
133
-    REQUEST_ENTITY_TOO_LARGE: "Request Entity Too Large",
134
-    REQUEST_URI_TOO_LONG: "Request-URI Too Long",
135
-    UNSUPPORTED_MEDIA_TYPE: "Unsupported Media Type",
136
-    REQUESTED_RANGE_NOT_SATISFIABLE: "Requested Range not satisfiable",
137
-    EXPECTATION_FAILED: "Expectation Failed",
138
-
139
-    # 500
140
-    INTERNAL_SERVER_ERROR: "Internal Server Error",
141
-    NOT_IMPLEMENTED: "Not Implemented",
142
-    BAD_GATEWAY: "Bad Gateway",
143
-    SERVICE_UNAVAILABLE: "Service Unavailable",
144
-    GATEWAY_TIMEOUT: "Gateway Time-out",
145
-    HTTP_VERSION_NOT_SUPPORTED: "HTTP Version not supported",
146
-    INSUFFICIENT_STORAGE_SPACE: "Insufficient Storage Space",
147
-    NOT_EXTENDED: "Not Extended"
148
-    }
149
-
150
-CACHED = """Magic constant returned by http.Request methods to set cache
151
-validation headers when the request is conditional and the value fails
152
-the condition."""
153
-
154
-# backwards compatability
155
-responses = RESPONSES
156
-
157
-
158
-# datetime parsing and formatting
159
-weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
160
-monthname = [None,
161
-             'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
162
-             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
163
-weekdayname_lower = [name.lower() for name in weekdayname]
164
-monthname_lower = [name and name.lower() for name in monthname]
165
-
166
-def urlparse(url):
167
-    """
168
-    Parse an URL into six components.
169
-
170
-    This is similar to L{urlparse.urlparse}, but rejects C{unicode} input
171
-    and always produces C{str} output.
172
-
173
-    @type url: C{str}
174
-
175
-    @raise TypeError: The given url was a C{unicode} string instead of a
176
-    C{str}.
177
-
178
-    @rtype: six-tuple of str
179
-    @return: The scheme, net location, path, params, query string, and fragment
180
-    of the URL.
181
-    """
182
-    if isinstance(url, unicode):
183
-        raise TypeError("url must be str, not unicode")
184
-    scheme, netloc, path, params, query, fragment = _urlparse(url)
185
-    if isinstance(scheme, unicode):
186
-        scheme = scheme.encode('ascii')
187
-        netloc = netloc.encode('ascii')
188
-        path = path.encode('ascii')
189
-        query = query.encode('ascii')
190
-        fragment = fragment.encode('ascii')
191
-    return scheme, netloc, path, params, query, fragment
192
-
193
-
194
-def parse_qs(qs, keep_blank_values=0, strict_parsing=0, unquote=unquote):
195
-    """like cgi.parse_qs, only with custom unquote function"""
196
-    d = {}
197
-    items = [s2 for s1 in qs.split("&") for s2 in s1.split(";")]
198
-    for item in items:
199
-        try:
200
-            k, v = item.split("=", 1)
201
-        except ValueError:
202
-            if strict_parsing:
203
-                raise
204
-            continue
205
-        if v or keep_blank_values:
206
-            k = unquote(k.replace("+", " "))
207
-            v = unquote(v.replace("+", " "))
208
-            if k in d:
209
-                d[k].append(v)
210
-            else:
211
-                d[k] = [v]
212
-    return d
213
-
214
-def datetimeToString(msSinceEpoch=None):
215
-    """Convert seconds since epoch to HTTP datetime string."""
216
-    if msSinceEpoch == None:
217
-        msSinceEpoch = time.time()
218
-    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(msSinceEpoch)
219
-    s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
220
-        weekdayname[wd],
221
-        day, monthname[month], year,
222
-        hh, mm, ss)
223
-    return s
224
-
225
-def datetimeToLogString(msSinceEpoch=None):
226
-    """Convert seconds since epoch to log datetime string."""
227
-    if msSinceEpoch == None:
228
-        msSinceEpoch = time.time()
229
-    year, month, day, hh, mm, ss, wd, y, z = time.gmtime(msSinceEpoch)
230
-    s = "[%02d/%3s/%4d:%02d:%02d:%02d +0000]" % (
231
-        day, monthname[month], year,
232
-        hh, mm, ss)
233
-    return s
234
-
235
-
236
-# a hack so we don't need to recalculate log datetime every hit,
237
-# at the price of a small, unimportant, inaccuracy.
238
-_logDateTime = None
239
-_logDateTimeUsers = 0
240
-_resetLogDateTimeID = None
241
-
242
-def _resetLogDateTime():
243
-    global _logDateTime
244
-    global _resetLogDateTime
245
-    global _resetLogDateTimeID
246
-    _logDateTime = datetimeToLogString()
247
-    _resetLogDateTimeID = reactor.callLater(1, _resetLogDateTime)
248
-
249
-def _logDateTimeStart():
250
-    global _logDateTimeUsers
251
-    if not _logDateTimeUsers:
252
-        _resetLogDateTime()
253
-    _logDateTimeUsers += 1
254
-
255
-def _logDateTimeStop():
256
-    global _logDateTimeUsers
257
-    _logDateTimeUsers -= 1;
258
-    if (not _logDateTimeUsers and _resetLogDateTimeID
259
-        and _resetLogDateTimeID.active()):
260
-        _resetLogDateTimeID.cancel()
261
-
262
-def timegm(year, month, day, hour, minute, second):
263
-    """Convert time tuple in GMT to seconds since epoch, GMT"""
264
-    EPOCH = 1970
265
-    assert year >= EPOCH
266
-    assert 1 <= month <= 12
267
-    days = 365*(year-EPOCH) + calendar.leapdays(EPOCH, year)
268
-    for i in range(1, month):
269
-        days = days + calendar.mdays[i]
270
-    if month > 2 and calendar.isleap(year):
271
-        days = days + 1
272
-    days = days + day - 1
273
-    hours = days*24 + hour
274
-    minutes = hours*60 + minute
275
-    seconds = minutes*60 + second
276
-    return seconds
277
-
278
-def stringToDatetime(dateString):
279
-    """Convert an HTTP date string (one of three formats) to seconds since epoch."""
280
-    parts = dateString.split()
281
-
282
-    if not parts[0][0:3].lower() in weekdayname_lower:
283
-        # Weekday is stupid. Might have been omitted.
284
-        try:
285
-            return stringToDatetime("Sun, "+dateString)
286
-        except ValueError:
287
-            # Guess not.
288
-            pass
289
-
290
-    partlen = len(parts)
291
-    if (partlen == 5 or partlen == 6) and parts[1].isdigit():
292
-        # 1st date format: Sun, 06 Nov 1994 08:49:37 GMT
293
-        # (Note: "GMT" is literal, not a variable timezone)
294
-        # (also handles without "GMT")
295
-        # This is the normal format
296
-        day = parts[1]
297
-        month = parts[2]
298
-        year = parts[3]
299
-        time = parts[4]
300
-    elif (partlen == 3 or partlen == 4) and parts[1].find('-') != -1:
301
-        # 2nd date format: Sunday, 06-Nov-94 08:49:37 GMT
302
-        # (Note: "GMT" is literal, not a variable timezone)
303
-        # (also handles without without "GMT")
304
-        # Two digit year, yucko.
305
-        day, month, year = parts[1].split('-')
306
-        time = parts[2]
307
-        year=int(year)
308
-        if year < 69:
309
-            year = year + 2000
310
-        elif year < 100:
311
-            year = year + 1900
312
-    elif len(parts) == 5:
313
-        # 3rd date format: Sun Nov  6 08:49:37 1994
314
-        # ANSI C asctime() format.
315
-        day = parts[2]
316
-        month = parts[1]
317
-        year = parts[4]
318
-        time = parts[3]
319
-    else:
320
-        raise ValueError("Unknown datetime format %r" % dateString)
321
-
322
-    day = int(day)
323
-    month = int(monthname_lower.index(month.lower()))
324
-    year = int(year)
325
-    hour, min, sec = map(int, time.split(':'))
326
-    return int(timegm(year, month, day, hour, min, sec))
327
-
328
-def toChunk(data):
329
-    """Convert string to a chunk.
330
-
331
-    @returns: a tuple of strings representing the chunked encoding of data"""
332
-    return ("%x\r\n" % len(data), data, "\r\n")
333
-
334
-def fromChunk(data):
335
-    """Convert chunk to string.
336
-
337
-    @returns: tuple (result, remaining), may raise ValueError.
338
-    """
339
-    prefix, rest = data.split('\r\n', 1)
340
-    length = int(prefix, 16)
341
-    if length < 0:
342
-        raise ValueError("Chunk length must be >= 0, not %d" % (length,))
343
-    if not rest[length:length + 2] == '\r\n':
344
-        raise ValueError, "chunk must end with CRLF"
345
-    return rest[:length], rest[length + 2:]
346
-
347
-
348
-def parseContentRange(header):
349
-    """Parse a content-range header into (start, end, realLength).
350
-
351
-    realLength might be None if real length is not known ('*').
352
-    """
353
-    kind, other = header.strip().split()
354
-    if kind.lower() != "bytes":
355
-        raise ValueError, "a range of type %r is not supported"
356
-    startend, realLength = other.split("/")
357
-    start, end = map(int, startend.split("-"))
358
-    if realLength == "*":
359
-        realLength = None
360
-    else:
361
-        realLength = int(realLength)
362
-    return (start, end, realLength)
363
-
364
-
365
-class StringTransport:
366
-    """
367
-    I am a StringIO wrapper that conforms for the transport API. I support
368
-    the `writeSequence' method.
369
-    """
370
-    def __init__(self):
371
-        self.s = StringIO()
372
-    def writeSequence(self, seq):
373
-        self.s.write(''.join(seq))
374
-    def __getattr__(self, attr):
375
-        return getattr(self.__dict__['s'], attr)
376
-
377
-
378
-class HTTPClient(basic.LineReceiver):
379
-    """A client for HTTP 1.0
380
-
381
-    Notes:
382
-    You probably want to send a 'Host' header with the name of
383
-    the site you're connecting to, in order to not break name
384
-    based virtual hosting.
385
-    """
386
-    length = None
387
-    firstLine = 1
388
-    __buffer = None
389
-
390
-    def sendCommand(self, command, path):
391
-        self.transport.write('%s %s HTTP/1.0\r\n' % (command, path))
392
-
393
-    def sendHeader(self, name, value):
394
-        self.transport.write('%s: %s\r\n' % (name, value))
395
-
396
-    def endHeaders(self):
397
-        self.transport.write('\r\n')
398
-
399
-    def lineReceived(self, line):
400
-        if self.firstLine:
401
-            self.firstLine = 0
402
-            l = line.split(None, 2)
403
-            version = l[0]
404
-            status = l[1]
405
-            try:
406
-                message = l[2]
407
-            except IndexError:
408
-                # sometimes there is no message
409
-                message = ""
410
-            self.handleStatus(version, status, message)
411
-            return
412
-        if line:
413
-            key, val = line.split(':', 1)
414
-            val = val.lstrip()
415
-            self.handleHeader(key, val)
416
-            if key.lower() == 'content-length':
417
-                self.length = int(val)
418
-        else:
419
-            self.__buffer = StringIO()
420
-            self.handleEndHeaders()
421
-            self.setRawMode()
422
-
423
-    def connectionLost(self, reason):
424
-        self.handleResponseEnd()
425
-
426
-    def handleResponseEnd(self):
427
-        if self.__buffer is not None:
428
-            b = self.__buffer.getvalue()
429
-            self.__buffer = None
430
-            self.handleResponse(b)
431
-
432
-    def handleResponsePart(self, data):
433
-        self.__buffer.write(data)
434
-
435
-    def connectionMade(self):
436
-        pass
437
-
438
-    def handleStatus(self, version, status, message):
439
-        """
440
-        Called when the status-line is received.
441
-
442
-        @param version: e.g. 'HTTP/1.0'
443
-        @param status: e.g. '200'
444
-        @type status: C{str}
445
-        @param message: e.g. 'OK'
446
-        """
447
-
448
-    def handleHeader(self, key, val):
449
-        """
450
-        Called every time a header is received.
451
-        """
452
-
453
-    def handleEndHeaders(self):
454
-        """
455
-        Called when all headers have been received.
456
-        """
457
-
458
-
459
-    def rawDataReceived(self, data):
460
-        if self.length is not None:
461
-            data, rest = data[:self.length], data[self.length:]
462
-            self.length -= len(data)
463
-        else:
464
-            rest = ''
465
-        self.handleResponsePart(data)
466
-        if self.length == 0:
467
-            self.handleResponseEnd()
468
-            self.setLineMode(rest)
469
-
470
-
471
-
472
-# response codes that must have empty bodies
473
-NO_BODY_CODES = (204, 304)
474
-
475
-class Request:
476
-    """
477
-    A HTTP request.
478
-
479
-    Subclasses should override the process() method to determine how
480
-    the request will be processed.
481
-
482
-    @ivar method: The HTTP method that was used.
483
-    @ivar uri: The full URI that was requested (includes arguments).
484
-    @ivar path: The path only (arguments not included).
485
-    @ivar args: All of the arguments, including URL and POST arguments.
486
-    @type args: A mapping of strings (the argument names) to lists of values.
487
-                i.e., ?foo=bar&foo=baz&quux=spam results in
488
-                {'foo': ['bar', 'baz'], 'quux': ['spam']}.
489
-
490
-    @type requestHeaders: L{http_headers.Headers}
491
-    @ivar requestHeaders: All received HTTP request headers.
492
-
493
-    @ivar received_headers: Backwards-compatibility access to
494
-        C{requestHeaders}.  Use C{requestHeaders} instead.  C{received_headers}
495
-        behaves mostly like a C{dict} and does not provide access to all header
496
-        values.
497
-
498
-    @type responseHeaders: L{http_headers.Headers}
499
-    @ivar responseHeaders: All HTTP response headers to be sent.
500
-
501
-    @ivar headers: Backwards-compatibility access to C{responseHeaders}.  Use
502
-        C{responseHeaders} instead.  C{headers} behaves mostly like a C{dict}
503
-        and does not provide access to all header values nor does it allow
504
-        multiple values for one header to be set.
505
-    """
506
-    implements(interfaces.IConsumer)
507
-
508
-    producer = None
509
-    finished = 0
510
-    code = OK
511
-    code_message = RESPONSES[OK]
512
-    method = "(no method yet)"
513
-    clientproto = "(no clientproto yet)"
514
-    uri = "(no uri yet)"
515
-    startedWriting = 0
516
-    chunked = 0
517
-    sentLength = 0 # content-length of response, or total bytes sent via chunking
518
-    etag = None
519
-    lastModified = None
520
-    _forceSSL = 0
521
-
522
-    def __init__(self, channel, queued):
523
-        """
524
-        @param channel: the channel we're connected to.
525
-        @param queued: are we in the request queue, or can we start writing to
526
-            the transport?
527
-        """
528
-        self.channel = channel
529
-        self.queued = queued
530
-        self.requestHeaders = Headers()
531
-        self.received_cookies = {}
532
-        self.responseHeaders = Headers()
533
-        self.cookies = [] # outgoing cookies
534
-
535
-        if queued:
536
-            self.transport = StringTransport()
537
-        else:
538
-            self.transport = self.channel.transport
539
-
540
-
541
-    def __setattr__(self, name, value):
542
-        """
543
-        Support assignment of C{dict} instances to C{received_headers} for
544
-        backwards-compatibility.
545
-        """
546
-        if name == 'received_headers':
547
-            # A property would be nice, but Request is classic.
548
-            self.requestHeaders = headers = Headers()
549
-            for k, v in value.iteritems():
550
-                headers.setRawHeaders(k, [v])
551
-        elif name == 'requestHeaders':
552
-            self.__dict__[name] = value
553
-            self.__dict__['received_headers'] = _DictHeaders(value)
554
-        elif name == 'headers':
555
-            self.responseHeaders = headers = Headers()
556
-            for k, v in value.iteritems():
557
-                headers.setRawHeaders(k, [v])
558
-        elif name == 'responseHeaders':
559
-            self.__dict__[name] = value
560
-            self.__dict__['headers'] = _DictHeaders(value)
561
-        else:
562
-            self.__dict__[name] = value
563
-
564
-
565
-    def _cleanup(self):
566
-        """Called when have finished responding and are no longer queued."""
567
-        if self.producer:
568
-            log.err(RuntimeError("Producer was not unregistered for %s" % self.uri))
569
-            self.unregisterProducer()
570
-        self.channel.requestDone(self)
571
-        del self.channel
572
-        try:
573
-            self.content.close()
574
-        except OSError:
575
-            # win32 suckiness, no idea why it does this
576
-            pass
577
-        del self.content
578
-
579
-    # methods for channel - end users should not use these
580
-
581
-    def noLongerQueued(self):
582
-        """Notify the object that it is no longer queued.
583
-
584
-        We start writing whatever data we have to the transport, etc.
585
-
586
-        This method is not intended for users.
587
-        """
588
-        if not self.queued:
589
-            raise RuntimeError, "noLongerQueued() got called unnecessarily."
590
-
591
-        self.queued = 0
592
-
593
-        # set transport to real one and send any buffer data
594
-        data = self.transport.getvalue()
595
-        self.transport = self.channel.transport
596
-        if data:
597
-            self.transport.write(data)
598
-
599
-        # if we have producer, register it with transport
600
-        if (self.producer is not None) and not self.finished:
601
-            self.transport.registerProducer(self.producer, self.streamingProducer)
602
-
603
-        # if we're finished, clean up
604
-        if self.finished:
605
-            self._cleanup()
606
-
607
-    def gotLength(self, length):
608
-        """
609
-        Called when HTTP channel got length of content in this request.
610
-
611
-        This method is not intended for users.
612
-
613
-        @param length: The length of the request body, as indicated by the
614
-            request headers.  C{None} if the request headers do not indicate a
615
-            length.
616
-        """
617
-        if length is not None and length < 100000:
618
-            self.content = StringIO()
619
-        else:
620
-            self.content = tempfile.TemporaryFile()
621
-
622
-
623
-    def parseCookies(self):
624
-        """
625
-        Parse cookie headers.
626
-
627
-        This method is not intended for users.
628
-        """
629
-        cookieheaders = self.requestHeaders.getRawHeaders("cookie")
630
-
631
-        if cookieheaders is None:
632
-            return
633
-
634
-        for cookietxt in cookieheaders:
635
-            if cookietxt:
636
-                for cook in cookietxt.split(';'):
637
-                    cook = cook.lstrip()
638
-                    try:
639
-                        k, v = cook.split('=', 1)
640
-                        self.received_cookies[k] = v
641
-                    except ValueError:
642
-                        pass
643
-
644
-
645
-    def handleContentChunk(self, data):
646
-        """Write a chunk of data.
647
-
648
-        This method is not intended for users.
649
-        """
650
-        self.content.write(data)
651
-
652
-
653
-    def requestReceived(self, command, path, version):
654
-        """
655
-        Called by channel when all data has been received.
656
-
657
-        This method is not intended for users.
658
-
659
-        @type command: C{str}
660
-        @param command: The HTTP verb of this request.  This has the case
661
-            supplied by the client (eg, it maybe "get" rather than "GET").
662
-
663
-        @type path: C{str}
664
-        @param path: The URI of this request.
665
-
666
-        @type version: C{str}
667
-        @param version: The HTTP version of this request.
668
-        """
669
-        self.content.seek(0,0)
670
-        self.args = {}
671
-        self.stack = []
672
-
673
-        self.method, self.uri = command, path
674
-        self.clientproto = version
675
-        x = self.uri.split('?', 1)
676
-
677
-        if len(x) == 1:
678
-            self.path = self.uri
679
-        else:
680
-            self.path, argstring = x
681
-            self.args = parse_qs(argstring, 1)
682
-
683
-        # cache the client and server information, we'll need this later to be
684
-        # serialized and sent with the request so CGIs will work remotely
685
-        self.client = self.channel.transport.getPeer()
686
-        self.host = self.channel.transport.getHost()
687
-
688
-        # Argument processing
689
-        args = self.args
690
-        ctype = self.requestHeaders.getRawHeaders('content-type')
691
-        if ctype is not None:
692
-            ctype = ctype[0]
693
-
694
-        if self.method == "POST" and ctype:
695
-            mfd = 'multipart/form-data'
696
-            key, pdict = cgi.parse_header(ctype)
697
-            if key == 'application/x-www-form-urlencoded':
698
-                args.update(parse_qs(self.content.read(), 1))
699
-            elif key == mfd:
700
-                try:
701
-                    args.update(cgi.parse_multipart(self.content, pdict))
702
-                except KeyError, e:
703
-                    if e.args[0] == 'content-disposition':
704
-                        # Parse_multipart can't cope with missing
705
-                        # content-dispostion headers in multipart/form-data
706
-                        # parts, so we catch the exception and tell the client
707
-                        # it was a bad request.
708
-                        self.channel.transport.write(
709
-                                "HTTP/1.1 400 Bad Request\r\n\r\n")
710
-                        self.channel.transport.loseConnection()
711
-                        return
712
-                    raise
713
-
714
-        self.process()
715
-
716
-
717
-    def __repr__(self):
718
-        return '<%s %s %s>'% (self.method, self.uri, self.clientproto)
719
-
720
-    def process(self):
721
-        """Override in subclasses.
722
-
723
-        This method is not intended for users.
724
-        """
725
-        pass
726
-
727
-
728
-    # consumer interface
729
-
730
-    def registerProducer(self, producer, streaming):
731
-        """Register a producer."""
732
-        if self.producer:
733
-            raise ValueError, "registering producer %s before previous one (%s) was unregistered" % (producer, self.producer)
734
-
735
-        self.streamingProducer = streaming
736
-        self.producer = producer
737
-
738
-        if self.queued:
739
-            producer.pauseProducing()
740
-        else:
741
-            self.transport.registerProducer(producer, streaming)
742
-
743
-    def unregisterProducer(self):
744
-        """Unregister the producer."""
745
-        if not self.queued:
746
-            self.transport.unregisterProducer()
747
-        self.producer = None
748
-
749
-    # private http response methods
750
-
751
-    def _sendError(self, code, resp=''):
752
-        self.transport.write('%s %s %s\r\n\r\n' % (self.clientproto, code, resp))
753
-
754
-
755
-    # The following is the public interface that people should be
756
-    # writing to.
757
-    def getHeader(self, key):
758
-        """
759
-        Get an HTTP request header.
760
-
761
-        @type key: C{str}
762
-        @param key: The name of the header to get the value of.
763
-
764
-        @rtype: C{str} or C{NoneType}
765
-        @return: The value of the specified header, or C{None} if that header
766
-            was not present in the request.
767
-        """
768
-        value = self.requestHeaders.getRawHeaders(key)
769
-        if value is not None:
770
-            return value[-1]
771
-
772
-
773
-    def getCookie(self, key):
774
-        """Get a cookie that was sent from the network.
775
-        """
776
-        return self.received_cookies.get(key)
777
-
778
-
779
-    def finish(self):
780
-        """We are finished writing data."""
781
-        if self.finished:
782
-            warnings.warn("Warning! request.finish called twice.", stacklevel=2)
783
-            return
784
-
785
-        if not self.startedWriting:
786
-            # write headers
787
-            self.write('')
788
-
789
-        if self.chunked:
790
-            # write last chunk and closing CRLF
791
-            self.transport.write("0\r\n\r\n")
792
-
793
-        # log request
794
-        if hasattr(self.channel, "factory"):
795
-            self.channel.factory.log(self)
796
-
797
-        self.finished = 1
798
-        if not self.queued:
799
-            self._cleanup()
800
-
801
-    def write(self, data):
802
-        """
803
-        Write some data as a result of an HTTP request.  The first
804
-        time this is called, it writes out response data.
805
-
806
-        @type data: C{str}
807
-        @param data: Some bytes to be sent as part of the response body.
808
-        """
809
-        if not self.startedWriting:
810
-            self.startedWriting = 1
811
-            version = self.clientproto
812
-            l = []
813
-            l.append('%s %s %s\r\n' % (version, self.code,
814
-                                       self.code_message))
815
-            # if we don't have a content length, we send data in
816
-            # chunked mode, so that we can support pipelining in
817
-            # persistent connections.
818
-            if ((version == "HTTP/1.1") and
819
-                (self.responseHeaders.getRawHeaders('content-length') is None) and
820
-                self.method != "HEAD" and self.code not in NO_BODY_CODES):
821
-                l.append("%s: %s\r\n" % ('Transfer-Encoding', 'chunked'))
822
-                self.chunked = 1
823
-
824
-            if self.lastModified is not None:
825
-                if self.responseHeaders.hasHeader('last-modified'):
826
-                    log.msg("Warning: last-modified specified both in"
827
-                            " header list and lastModified attribute.")
828
-                else:
829
-                    self.responseHeaders.setRawHeaders(
830
-                        'last-modified',
831
-                        [datetimeToString(self.lastModified)])
832
-
833
-            if self.etag is not None:
834
-                self.responseHeaders.setRawHeaders('ETag', [self.etag])
835
-
836
-            for name, values in self.responseHeaders.getAllRawHeaders():
837
-                for value in values:
838
-                    l.append("%s: %s\r\n" % (name, value))
839
-
840
-            for cookie in self.cookies:
841
-                l.append('%s: %s\r\n' % ("Set-Cookie", cookie))
842
-
843
-            l.append("\r\n")
844
-
845
-            self.transport.writeSequence(l)
846
-
847
-            # if this is a "HEAD" request, we shouldn't return any data
848
-            if self.method == "HEAD":
849
-                self.write = lambda data: None
850
-                return
851
-
852
-            # for certain result codes, we should never return any data
853
-            if self.code in NO_BODY_CODES:
854
-                self.write = lambda data: None
855
-                return
856
-
857
-        self.sentLength = self.sentLength + len(data)
858
-        if data:
859
-            if self.chunked:
860
-                self.transport.writeSequence(toChunk(data))
861
-            else:
862
-                self.transport.write(data)
863
-
864
-    def addCookie(self, k, v, expires=None, domain=None, path=None, max_age=None, comment=None, secure=None):
865
-        """Set an outgoing HTTP cookie.
866
-
867
-        In general, you should consider using sessions instead of cookies, see
868
-        twisted.web.server.Request.getSession and the
869
-        twisted.web.server.Session class for details.
870
-        """
871
-        cookie = '%s=%s' % (k, v)
872
-        if expires is not None:
873
-            cookie = cookie +"; Expires=%s" % expires
874
-        if domain is not None:
875
-            cookie = cookie +"; Domain=%s" % domain
876
-        if path is not None:
877
-            cookie = cookie +"; Path=%s" % path
878
-        if max_age is not None:
879
-            cookie = cookie +"; Max-Age=%s" % max_age
880
-        if comment is not None:
881
-            cookie = cookie +"; Comment=%s" % comment
882
-        if secure:
883
-            cookie = cookie +"; Secure"
884
-        self.cookies.append(cookie)
885
-
886
-    def setResponseCode(self, code, message=None):
887
-        """Set the HTTP response code.
888
-        """
889
-        if not isinstance(code, (int, long)):
890
-            raise TypeError("HTTP response code must be int or long")
891
-        self.code = code
892
-        if message:
893
-            self.code_message = message
894
-        else:
895
-            self.code_message = RESPONSES.get(code, "Unknown Status")
896
-
897
-
898
-    def setHeader(self, name, value):
899
-        """
900
-        Set an HTTP response header.  Overrides any previously set values for
901
-        this header.
902
-
903
-        @type name: C{str}
904
-        @param name: The name of the header for which to set the value.
905
-
906
-        @type value: C{str}
907
-        @param value: The value to set for the named header.
908
-        """
909
-        self.responseHeaders.setRawHeaders(name, [value])
910
-
911
-
912
-    def redirect(self, url):
913
-        """Utility function that does a redirect.
914
-
915
-        The request should have finish() called after this.
916
-        """
917
-        self.setResponseCode(FOUND)
918
-        self.setHeader("location", url)
919
-
920
-
921
-    def setLastModified(self, when):
922
-        """
923
-        Set the C{Last-Modified} time for the response to this request.
924
-
925
-        If I am called more than once, I ignore attempts to set
926
-        Last-Modified earlier, only replacing the Last-Modified time
927
-        if it is to a later value.
928
-
929
-        If I am a conditional request, I may modify my response code
930
-        to L{NOT_MODIFIED} if appropriate for the time given.
931
-
932
-        @param when: The last time the resource being returned was
933
-            modified, in seconds since the epoch.
934
-        @type when: number
935
-        @return: If I am a C{If-Modified-Since} conditional request and
936
-            the time given is not newer than the condition, I return
937
-            L{http.CACHED<CACHED>} to indicate that you should write no
938
-            body.  Otherwise, I return a false value.
939
-        """
940
-        # time.time() may be a float, but the HTTP-date strings are
941
-        # only good for whole seconds.
942
-        when = long(math.ceil(when))
943
-        if (not self.lastModified) or (self.lastModified < when):
944
-            self.lastModified = when
945
-
946
-        modified_since = self.getHeader('if-modified-since')
947
-        if modified_since:
948
-            modified_since = stringToDatetime(modified_since.split(';', 1)[0])
949
-            if modified_since >= when:
950
-                self.setResponseCode(NOT_MODIFIED)
951
-                return CACHED
952
-        return None
953
-
954
-    def setETag(self, etag):
955
-        """
956
-        Set an C{entity tag} for the outgoing response.
957
-
958
-        That's \"entity tag\" as in the HTTP/1.1 C{ETag} header, \"used
959
-        for comparing two or more entities from the same requested
960
-        resource.\"
961
-
962
-        If I am a conditional request, I may modify my response code
963
-        to L{NOT_MODIFIED} or L{PRECONDITION_FAILED}, if appropriate
964
-        for the tag given.
965
-
966
-        @param etag: The entity tag for the resource being returned.
967
-        @type etag: string
968
-        @return: If I am a C{If-None-Match} conditional request and
969
-            the tag matches one in the request, I return
970
-            L{http.CACHED<CACHED>} to indicate that you should write
971
-            no body.  Otherwise, I return a false value.
972
-        """
973
-        if etag:
974
-            self.etag = etag
975
-
976
-        tags = self.getHeader("if-none-match")
977
-        if tags:
978
-            tags = tags.split()
979
-            if (etag in tags) or ('*' in tags):
980
-                self.setResponseCode(((self.method in ("HEAD", "GET"))
981
-                                      and NOT_MODIFIED)
982
-                                     or PRECONDITION_FAILED)
983
-                return CACHED
984
-        return None
985
-
986
-
987
-    def getAllHeaders(self):
988
-        """
989
-        Return dictionary mapping the names of all received headers to the last
990
-        value received for each.
991
-
992
-        Since this method does not return all header information,
993
-        C{self.requestHeaders.getAllRawHeaders()} may be preferred.
994
-        """
995
-        headers = {}
996
-        for k, v in self.requestHeaders.getAllRawHeaders():
997
-            headers[k.lower()] = v[-1]
998
-        return headers
999
-
1000
-
1001
-    def getRequestHostname(self):
1002
-        """
1003
-        Get the hostname that the user passed in to the request.
1004
-
1005
-        This will either use the Host: header (if it is available) or the
1006
-        host we are listening on if the header is unavailable.
1007
-
1008
-        @returns: the requested hostname
1009
-        @rtype: C{str}
1010
-        """
1011
-        # XXX This method probably has no unit tests.  I changed it a ton and
1012
-        # nothing failed.
1013
-        host = self.getHeader('host')
1014
-        if host:
1015
-            return host.split(':', 1)[0]
1016
-        return self.getHost().host
1017
-
1018
-
1019
-    def getHost(self):
1020
-        """Get my originally requesting transport's host.
1021
-
1022
-        Don't rely on the 'transport' attribute, since Request objects may be
1023
-        copied remotely.  For information on this method's return value, see
1024
-        twisted.internet.tcp.Port.
1025
-        """
1026
-        return self.host
1027
-
1028
-    def setHost(self, host, port, ssl=0):
1029
-        """
1030
-        Change the host and port the request thinks it's using.
1031
-
1032
-        This method is useful for working with reverse HTTP proxies (e.g.
1033
-        both Squid and Apache's mod_proxy can do this), when the address
1034
-        the HTTP client is using is different than the one we're listening on.
1035
-
1036
-        For example, Apache may be listening on https://www.example.com, and then
1037
-        forwarding requests to http://localhost:8080, but we don't want HTML produced
1038
-        by Twisted to say 'http://localhost:8080', they should say 'https://www.example.com',
1039
-        so we do::
1040
-
1041
-           request.setHost('www.example.com', 443, ssl=1)
1042
-
1043
-        @type host: C{str}
1044
-        @param host: The value to which to change the host header.
1045
-
1046
-        @type ssl: C{bool}
1047
-        @param ssl: A flag which, if C{True}, indicates that the request is
1048
-            considered secure (if C{True}, L{isSecure} will return C{True}).
1049
-        """
1050
-        self._forceSSL = ssl
1051
-        self.requestHeaders.setRawHeaders("host", [host])
1052
-        self.host = address.IPv4Address("TCP", host, port)
1053
-
1054
-
1055
-    def getClientIP(self):
1056
-        """
1057
-        Return the IP address of the client who submitted this request.
1058
-
1059
-        @returns: the client IP address
1060
-        @rtype: C{str}
1061
-        """
1062
-        if isinstance(self.client, address.IPv4Address):
1063
-            return self.client.host
1064
-        else:
1065
-            return None
1066
-
1067
-    def isSecure(self):
1068
-        """
1069
-        Return True if this request is using a secure transport.
1070
-
1071
-        Normally this method returns True if this request's HTTPChannel
1072
-        instance is using a transport that implements ISSLTransport.
1073
-
1074
-        This will also return True if setHost() has been called
1075
-        with ssl=True.
1076
-
1077
-        @returns: True if this request is secure
1078
-        @rtype: C{bool}
1079
-        """
1080
-        if self._forceSSL:
1081
-            return True
1082
-        transport = getattr(getattr(self, 'channel', None), 'transport', None)
1083
-        if interfaces.ISSLTransport(transport, None) is not None:
1084
-            return True
1085
-        return False
1086
-
1087
-    def _authorize(self):
1088
-        # Authorization, (mostly) per the RFC
1089
-        try:
1090
-            authh = self.getHeader("Authorization")
1091
-            if not authh:
1092
-                self.user = self.password = ''
1093
-                return
1094
-            bas, upw = authh.split()
1095
-            if bas.lower() != "basic":
1096
-                raise ValueError
1097
-            upw = base64.decodestring(upw)
1098
-            self.user, self.password = upw.split(':', 1)
1099
-        except (binascii.Error, ValueError):
1100
-            self.user = self.password = ""
1101
-        except:
1102
-            log.err()
1103
-            self.user = self.password = ""
1104
-
1105
-    def getUser(self):
1106
-        """
1107
-        Return the HTTP user sent with this request, if any.
1108
-
1109
-        If no user was supplied, return the empty string.
1110
-
1111
-        @returns: the HTTP user, if any
1112
-        @rtype: C{str}
1113
-        """
1114
-        try:
1115
-            return self.user
1116
-        except:
1117
-            pass
1118
-        self._authorize()
1119
-        return self.user
1120
-
1121
-    def getPassword(self):
1122
-        """
1123
-        Return the HTTP password sent with this request, if any.
1124
-
1125
-        If no password was supplied, return the empty string.
1126
-
1127
-        @returns: the HTTP password, if any
1128
-        @rtype: C{str}
1129
-        """
1130
-        try:
1131
-            return self.password
1132
-        except:
1133
-            pass
1134
-        self._authorize()
1135
-        return self.password
1136
-
1137
-    def getClient(self):
1138
-        if self.client.type != 'TCP':
1139
-            return None
1140
-        host = self.client.host
1141
-        try:
1142
-            name, names, addresses = socket.gethostbyaddr(host)
1143
-        except socket.error:
1144
-            return host
1145
-        names.insert(0, name)
1146
-        for name in names:
1147
-            if '.' in name:
1148
-                return name
1149
-        return names[0]
1150
-
1151
-    def connectionLost(self, reason):
1152
-        """connection was lost"""
1153
-        pass
1154
-
1155
-
1156
-
1157
-class _ChunkedTransferEncoding(object):
1158
-    """
1159
-    Protocol for decoding chunked Transfer-Encoding, as defined by RFC 2616,
1160
-    section 3.6.1.  This protocol can interpret the contents of a request or
1161
-    response body which uses the I{chunked} Transfer-Encoding.  It cannot
1162
-    interpret any of the rest of the HTTP protocol.
1163
-
1164
-    It may make sense for _ChunkedTransferEncoding to be an actual IProtocol
1165
-    implementation.  Currently, the only user of this class will only ever
1166
-    call dataReceived on it.  However, it might be an improvement if the
1167
-    user could connect this to a transport and deliver connection lost
1168
-    notification.  This way, `dataCallback` becomes `self.transport.write`
1169
-    and perhaps `finishCallback` becomes `self.transport.loseConnection()`
1170
-    (although I'm not sure where the extra data goes in that case).  This
1171
-    could also allow this object to indicate to the receiver of data that
1172
-    the stream was not completely received, an error case which should be
1173
-    noticed. -exarkun
1174
-
1175
-    @ivar dataCallback: A one-argument callable which will be invoked each
1176
-        time application data is received.
1177
-
1178
-    @ivar finishCallback: A one-argument callable which will be invoked when
1179
-        the terminal chunk is received.  It will be invoked with all bytes
1180
-        which were delivered to this protocol which came after the terminal
1181
-        chunk.
1182
-
1183
-    @ivar length: Counter keeping track of how many more bytes in a chunk there
1184
-        are to receive.
1185
-
1186
-    @ivar state: One of C{'chunk-length'}, C{'trailer'}, C{'body'}, or
1187
-        C{'finished'}.  For C{'chunk-length'}, data for the chunk length line
1188
-        is currently being read.  For C{'trailer'}, the CR LF pair which
1189
-        follows each chunk is being read.  For C{'body'}, the contents of a
1190
-        chunk are being read.  For C{'finished'}, the last chunk has been
1191
-        completely read and no more input is valid.
1192
-
1193
-    @ivar finish: A flag indicating that the last chunk has been started.  When
1194
-        it finishes, the state will change to C{'finished'} and no more data
1195
-        will be accepted.
1196
-    """
1197
-    state = 'chunk-length'
1198
-    finish = False
1199
-
1200
-    def __init__(self, dataCallback, finishCallback):
1201
-        self.dataCallback = dataCallback
1202
-        self.finishCallback = finishCallback
1203
-        self._buffer = ''
1204
-
1205
-
1206
-    def dataReceived(self, data):
1207
-        """
1208
-        Interpret data from a request or response body which uses the
1209
-        I{chunked} Transfer-Encoding.
1210
-        """
1211
-        data = self._buffer + data
1212
-        self._buffer = ''
1213
-        while data:
1214
-            if self.state == 'chunk-length':
1215
-                if '\r\n' in data:
1216
-                    line, rest = data.split('\r\n', 1)
1217
-                    parts = line.split(';')
1218
-                    self.length = int(parts[0], 16)
1219
-                    if self.length == 0:
1220
-                        self.state = 'trailer'
1221
-                        self.finish = True
1222
-                    else:
1223
-                        self.state = 'body'
1224
-                    data = rest
1225
-                else:
1226
-                    self._buffer = data
1227
-                    data = ''
1228
-            elif self.state == 'trailer':
1229
-                if data.startswith('\r\n'):
1230
-                    data = data[2:]
1231
-                    if self.finish:
1232
-                        self.finishCallback(data)
1233
-                        self.state = 'finished'
1234
-                        data = ''
1235
-                    else:
1236
-                        self.state = 'chunk-length'
1237
-                else:
1238
-                    self._buffer = data
1239
-                    data = ''
1240
-            elif self.state == 'body':
1241
-                if len(data) >= self.length:
1242
-                    chunk, data = data[:self.length], data[self.length:]
1243
-                    self.dataCallback(chunk)
1244
-                    self.state = 'trailer'
1245
-                elif len(data) < self.length:
1246
-                    self.length -= len(data)
1247
-                    self.dataCallback(data)
1248
-                    data = ''
1249
-            elif self.state == 'finished':
1250
-                raise RuntimeError(
1251
-                    "_ChunkedTransferEncoding.dataReceived called after last "
1252
-                    "chunk was processed")
1253
-
1254
-
1255
-
1256
-
1257
-class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
1258
-    """
1259
-    A receiver for HTTP requests.
1260
-
1261
-    @ivar _transferDecoder: C{None} or an instance of
1262
-        L{_ChunkedTransferEncoding} if the request body uses the I{chunked}
1263
-        Transfer-Encoding.
1264
-    """
1265
-
1266
-    maxHeaders = 500 # max number of headers allowed per request
1267
-
1268
-    length = 0
1269
-    persistent = 1
1270
-    __header = ''
1271
-    __first_line = 1
1272
-    __content = None
1273
-
1274
-    # set in instances or subclasses
1275
-    requestFactory = Request
1276
-
1277
-    _savedTimeOut = None
1278
-    _receivedHeaderCount = 0
1279
-
1280
-    def __init__(self):
1281
-        # the request queue
1282
-        self.requests = []
1283
-        self._transferDecoder = None
1284
-
1285
-
1286
-    def connectionMade(self):
1287
-        self.setTimeout(self.timeOut)
1288
-
1289
-    def lineReceived(self, line):
1290
-        self.resetTimeout()
1291
-
1292
-        if self.__first_line:
1293
-            # if this connection is not persistent, drop any data which
1294
-            # the client (illegally) sent after the last request.
1295
-            if not self.persistent:
1296
-                self.dataReceived = self.lineReceived = lambda *args: None
1297
-                return
1298
-
1299
-            # IE sends an extraneous empty line (\r\n) after a POST request;
1300
-            # eat up such a line, but only ONCE
1301
-            if not line and self.__first_line == 1:
1302
-                self.__first_line = 2
1303
-                return
1304
-
1305
-            # create a new Request object
1306
-            request = self.requestFactory(self, len(self.requests))
1307
-            self.requests.append(request)
1308
-
1309
-            self.__first_line = False
1310
-            parts = line.split()
1311
-            if len(parts) != 3:
1312
-                self.transport.write("HTTP/1.1 400 Bad Request\r\n\r\n")
1313
-                self.transport.loseConnection()
1314
-                return
1315
-            command, request, version = parts
1316
-            self._command = command
1317
-            self._path = request
1318
-            self._version = version
1319
-        elif line == '':
1320
-            if self.__header:
1321
-                self.headerReceived(self.__header)
1322
-            self.__header = ''
1323
-            self.allHeadersReceived()
1324
-            if self.length == 0:
1325
-                self.allContentReceived()
1326
-            else:
1327
-                self.setRawMode()
1328
-        elif line[0] in ' \t':
1329
-            self.__header = self.__header+'\n'+line
1330
-        else:
1331
-            if self.__header:
1332
-                self.headerReceived(self.__header)
1333
-            self.__header = line
1334
-
1335
-
1336
-    def _finishRequestBody(self, data):
1337
-        self.allContentReceived()
1338
-        self.setLineMode(data)
1339
-
1340
-
1341
-    def headerReceived(self, line):
1342
-        """
1343
-        Do pre-processing (for content-length) and store this header away.
1344
-        Enforce the per-request header limit.
1345
-
1346
-        @type line: C{str}
1347
-        @param line: A line from the header section of a request, excluding the
1348
-            line delimiter.
1349
-        """
1350
-        header, data = line.split(':', 1)
1351
-        header = header.lower()
1352
-        data = data.strip()
1353
-        if header == 'content-length':
1354
-            self.length = int(data)
1355
-        elif header == 'transfer-encoding' and data.lower() == 'chunked':
1356
-            self.length = None
1357
-            self._transferDecoder = _ChunkedTransferEncoding(self.requests[-1].handleContentChunk, self._finishRequestBody)
1358
-
1359
-        reqHeaders = self.requests[-1].requestHeaders
1360
-        values = reqHeaders.getRawHeaders(header)
1361
-        if values is not None:
1362
-            values.append(data)
1363
-        else:
1364
-            reqHeaders.setRawHeaders(header, [data])
1365
-
1366
-        self._receivedHeaderCount += 1
1367
-        if self._receivedHeaderCount > self.maxHeaders:
1368
-            self.transport.write("HTTP/1.1 400 Bad Request\r\n\r\n")
1369
-            self.transport.loseConnection()
1370
-
1371
-
1372
-    def allContentReceived(self):
1373
-        command = self._command
1374
-        path = self._path
1375
-        version = self._version
1376
-
1377
-        # reset ALL state variables, so we don't interfere with next request
1378
-        self.length = 0
1379
-        self._receivedHeaderCount = 0
1380
-        self.__first_line = 1
1381
-        self._transferDecoder = None
1382
-        del self._command, self._path, self._version
1383
-
1384
-        # Disable the idle timeout, in case this request takes a long
1385
-        # time to finish generating output.
1386
-        if self.timeOut:
1387
-            self._savedTimeOut = self.setTimeout(None)
1388
-
1389
-        req = self.requests[-1]
1390
-        req.requestReceived(command, path, version)
1391
-
1392
-    def rawDataReceived(self, data):
1393
-        self.resetTimeout()
1394
-        if self._transferDecoder is not None:
1395
-            self._transferDecoder.dataReceived(data)
1396
-        elif len(data) < self.length:
1397
-            self.requests[-1].handleContentChunk(data)
1398
-            self.length = self.length - len(data)
1399
-        else:
1400
-            self.requests[-1].handleContentChunk(data[:self.length])
1401
-            self._finishRequestBody(data[self.length:])
1402
-
1403
-
1404
-    def allHeadersReceived(self):
1405
-        req = self.requests[-1]
1406
-        req.parseCookies()
1407
-        self.persistent = self.checkPersistence(req, self._version)
1408
-        req.gotLength(self.length)
1409
-
1410
-
1411
-    def checkPersistence(self, request, version):
1412
-        """
1413
-        Check if the channel should close or not.
1414
-
1415
-        @param request: The request most recently received over this channel
1416
-            against which checks will be made to determine if this connection
1417
-            can remain open after a matching response is returned.
1418
-
1419
-        @type version: C{str}
1420
-        @param version: The version of the request.
1421
-
1422
-        @rtype: C{bool}
1423
-        @return: A flag which, if C{True}, indicates that this connection may
1424
-            remain open to receive another request; if C{False}, the connection
1425
-            must be closed in order to indicate the completion of the response
1426
-            to C{request}.
1427
-        """
1428
-        connection = request.requestHeaders.getRawHeaders('connection')
1429
-        if connection:
1430
-            tokens = map(str.lower, connection[0].split(' '))
1431
-        else:
1432
-            tokens = []
1433
-
1434
-        # HTTP 1.0 persistent connection support is currently disabled,
1435
-        # since we need a way to disable pipelining. HTTP 1.0 can't do
1436
-        # pipelining since we can't know in advance if we'll have a
1437
-        # content-length header, if we don't have the header we need to close the
1438
-        # connection. In HTTP 1.1 this is not an issue since we use chunked
1439
-        # encoding if content-length is not available.
1440
-
1441
-        #if version == "HTTP/1.0":
1442
-        #    if 'keep-alive' in tokens:
1443
-        #        request.setHeader('connection', 'Keep-Alive')
1444
-        #        return 1
1445
-        #    else:
1446
-        #        return 0
1447
-        if version == "HTTP/1.1":
1448
-            if 'close' in tokens:
1449
-                request.responseHeaders.setRawHeaders('connection', ['close'])
1450
-                return False
1451
-            else:
1452
-                return True
1453
-        else:
1454
-            return False
1455
-
1456
-
1457
-    def requestDone(self, request):
1458
-        """Called by first request in queue when it is done."""
1459
-        if request != self.requests[0]: raise TypeError
1460
-        del self.requests[0]
1461
-
1462
-        if self.persistent:
1463
-            # notify next request it can start writing
1464
-            if self.requests:
1465
-                self.requests[0].noLongerQueued()
1466
-            else:
1467
-                if self._savedTimeOut:
1468
-                    self.setTimeout(self._savedTimeOut)
1469
-        else:
1470
-            self.transport.loseConnection()
1471
-
1472
-    def timeoutConnection(self):
1473
-        log.msg("Timing out client: %s" % str(self.transport.getPeer()))
1474
-        policies.TimeoutMixin.timeoutConnection(self)
1475
-
1476
-    def connectionLost(self, reason):
1477
-        self.setTimeout(None)
1478
-        for request in self.requests:
1479
-            request.connectionLost(reason)
1480
-
1481
-
1482
-class HTTPFactory(protocol.ServerFactory):
1483
-    """Factory for HTTP server."""
1484
-
1485
-    protocol = HTTPChannel
1486
-
1487
-    logPath = None
1488
-
1489
-    timeOut = 60 * 60 * 12
1490
-
1491
-    def __init__(self, logPath=None, timeout=60*60*12):
1492
-        if logPath is not None:
1493
-            logPath = os.path.abspath(logPath)
1494
-        self.logPath = logPath
1495
-        self.timeOut = timeout
1496
-
1497
-    def buildProtocol(self, addr):
1498
-        p = protocol.ServerFactory.buildProtocol(self, addr)
1499
-        # timeOut needs to be on the Protocol instance cause
1500
-        # TimeoutMixin expects it there
1501
-        p.timeOut = self.timeOut
1502
-        return p
1503
-
1504
-    def startFactory(self):
1505
-        _logDateTimeStart()
1506
-        if self.logPath:
1507
-            self.logFile = self._openLogFile(self.logPath)
1508
-        else:
1509
-            self.logFile = log.logfile
1510
-
1511
-    def stopFactory(self):
1512
-        if hasattr(self, "logFile"):
1513
-            if self.logFile != log.logfile:
1514
-                self.logFile.close()
1515
-            del self.logFile
1516
-        _logDateTimeStop()
1517
-
1518
-    def _openLogFile(self, path):
1519
-        """Override in subclasses, e.g. to use twisted.python.logfile."""
1520
-        f = open(path, "a", 1)
1521
-        return f
1522
-
1523
-    def _escape(self, s):
1524
-        # pain in the ass. Return a string like python repr, but always
1525
-        # escaped as if surrounding quotes were "".
1526
-        r = repr(s)
1527
-        if r[0] == "'":
1528
-            return r[1:-1].replace('"', '\\"').replace("\\'", "'")
1529
-        return r[1:-1]
1530
-
1531
-    def log(self, request):
1532
-        """Log a request's result to the logfile, by default in combined log format."""
1533
-        if hasattr(self, "logFile"):
1534
-            line = '%s - - %s "%s" %d %s "%s" "%s"\n' % (
1535
-                request.getClientIP(),
1536
-                # request.getUser() or "-", # the remote user is almost never important
1537
-                _logDateTime,
1538
-                '%s %s %s' % (self._escape(request.method),
1539
-                              self._escape(request.uri),
1540
-                              self._escape(request.clientproto)),
1541
-                request.code,
1542
-                request.sentLength or "-",
1543
-                self._escape(request.getHeader("referer") or "-"),
1544
-                self._escape(request.getHeader("user-agent") or "-"))
1545
-            self.logFile.write(line)