Statistics
| Branch: | Tag: | Revision:

root / lib / http / __init__.py @ 13b63666

History | View | Annotate | Download (20.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""HTTP module.
22

23
"""
24

    
25
import logging
26
import mimetools
27
import OpenSSL
28
import select
29
import socket
30
import errno
31

    
32
from cStringIO import StringIO
33

    
34
from ganeti import constants
35
from ganeti import serializer
36
from ganeti import utils
37

    
38

    
39
HTTP_GANETI_VERSION = "Ganeti %s" % constants.RELEASE_VERSION
40

    
41
HTTP_OK = 200
42
HTTP_NO_CONTENT = 204
43
HTTP_NOT_MODIFIED = 304
44

    
45
HTTP_0_9 = "HTTP/0.9"
46
HTTP_1_0 = "HTTP/1.0"
47
HTTP_1_1 = "HTTP/1.1"
48

    
49
HTTP_GET = "GET"
50
HTTP_HEAD = "HEAD"
51
HTTP_POST = "POST"
52
HTTP_PUT = "PUT"
53
HTTP_DELETE = "DELETE"
54

    
55
HTTP_ETAG = "ETag"
56
HTTP_HOST = "Host"
57
HTTP_SERVER = "Server"
58
HTTP_DATE = "Date"
59
HTTP_USER_AGENT = "User-Agent"
60
HTTP_CONTENT_TYPE = "Content-Type"
61
HTTP_CONTENT_LENGTH = "Content-Length"
62
HTTP_CONNECTION = "Connection"
63
HTTP_KEEP_ALIVE = "Keep-Alive"
64

    
65
_SSL_UNEXPECTED_EOF = "Unexpected EOF"
66

    
67
# Socket operations
68
(SOCKOP_SEND,
69
 SOCKOP_RECV,
70
 SOCKOP_SHUTDOWN) = range(3)
71

    
72
# send/receive quantum
73
SOCK_BUF_SIZE = 32768
74

    
75

    
76
class HttpError(Exception):
77
  """Internal exception for HTTP errors.
78

79
  This should only be used for internal error reporting.
80

81
  """
82

    
83

    
84
class HttpSocketTimeout(Exception):
85
  """Internal exception for socket timeouts.
86

87
  This should only be used for internal error reporting.
88

89
  """
90

    
91

    
92
class HttpException(Exception):
93
  code = None
94
  message = None
95

    
96
  def __init__(self, message=None):
97
    Exception.__init__(self)
98
    if message is not None:
99
      self.message = message
100

    
101

    
102
class HttpBadRequest(HttpException):
103
  code = 400
104

    
105

    
106
class HttpForbidden(HttpException):
107
  code = 403
108

    
109

    
110
class HttpNotFound(HttpException):
111
  code = 404
112

    
113

    
114
class HttpGone(HttpException):
115
  code = 410
116

    
117

    
118
class HttpLengthRequired(HttpException):
119
  code = 411
120

    
121

    
122
class HttpInternalError(HttpException):
123
  code = 500
124

    
125

    
126
class HttpNotImplemented(HttpException):
127
  code = 501
128

    
129

    
130
class HttpServiceUnavailable(HttpException):
131
  code = 503
132

    
133

    
134
class HttpVersionNotSupported(HttpException):
135
  code = 505
136

    
137

    
138
class HttpJsonConverter:
139
  CONTENT_TYPE = "application/json"
140

    
141
  def Encode(self, data):
142
    return serializer.DumpJson(data)
143

    
144
  def Decode(self, data):
145
    return serializer.LoadJson(data)
146

    
147

    
148
def WaitForSocketCondition(poller, sock, event, timeout):
149
  """Waits for a condition to occur on the socket.
150

151
  @type poller: select.Poller
152
  @param poller: Poller object as created by select.poll()
153
  @type sock: socket
154
  @param socket: Wait for events on this socket
155
  @type event: int
156
  @param event: ORed condition (see select module)
157
  @type timeout: float or None
158
  @param timeout: Timeout in seconds
159
  @rtype: int or None
160
  @return: None for timeout, otherwise occured conditions
161

162
  """
163
  check = (event | select.POLLPRI |
164
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
165

    
166
  if timeout is not None:
167
    # Poller object expects milliseconds
168
    timeout *= 1000
169

    
170
  poller.register(sock, event)
171
  try:
172
    while True:
173
      # TODO: If the main thread receives a signal and we have no timeout, we
174
      # could wait forever. This should check a global "quit" flag or
175
      # something every so often.
176
      io_events = poller.poll(timeout)
177
      if not io_events:
178
        # Timeout
179
        return None
180
      for (evfd, evcond) in io_events:
181
        if evcond & check:
182
          return evcond
183
  finally:
184
    poller.unregister(sock)
185

    
186

    
187
def SocketOperation(poller, sock, op, arg1, timeout):
188
  """Wrapper around socket functions.
189

190
  This function abstracts error handling for socket operations, especially
191
  for the complicated interaction with OpenSSL.
192

193
  @type poller: select.Poller
194
  @param poller: Poller object as created by select.poll()
195
  @type sock: socket
196
  @param socket: Socket for the operation
197
  @type op: int
198
  @param op: Operation to execute (SOCKOP_* constants)
199
  @type arg1: any
200
  @param arg1: Parameter for function (if needed)
201
  @type timeout: None or float
202
  @param timeout: Timeout in seconds or None
203

204
  """
205
  # TODO: event_poll/event_check/override
206
  if op == SOCKOP_SEND:
207
    event_poll = select.POLLOUT
208
    event_check = select.POLLOUT
209

    
210
  elif op == SOCKOP_RECV:
211
    event_poll = select.POLLIN
212
    event_check = select.POLLIN | select.POLLPRI
213

    
214
  elif op == SOCKOP_SHUTDOWN:
215
    event_poll = None
216
    event_check = None
217

    
218
    # The timeout is only used when OpenSSL requests polling for a condition.
219
    # It is not advisable to have no timeout for shutdown.
220
    assert timeout
221

    
222
  else:
223
    raise AssertionError("Invalid socket operation")
224

    
225
  # No override by default
226
  event_override = 0
227

    
228
  while True:
229
    # Poll only for certain operations and when asked for by an override
230
    if event_override or op in (SOCKOP_SEND, SOCKOP_RECV):
231
      if event_override:
232
        wait_for_event = event_override
233
      else:
234
        wait_for_event = event_poll
235

    
236
      event = WaitForSocketCondition(poller, sock, wait_for_event, timeout)
237
      if event is None:
238
        raise HttpSocketTimeout()
239

    
240
      if (op == SOCKOP_RECV and
241
          event & (select.POLLNVAL | select.POLLHUP | select.POLLERR)):
242
        return ""
243

    
244
      if not event & wait_for_event:
245
        continue
246

    
247
    # Reset override
248
    event_override = 0
249

    
250
    try:
251
      try:
252
        if op == SOCKOP_SEND:
253
          return sock.send(arg1)
254

    
255
        elif op == SOCKOP_RECV:
256
          return sock.recv(arg1)
257

    
258
        elif op == SOCKOP_SHUTDOWN:
259
          if isinstance(sock, OpenSSL.SSL.ConnectionType):
260
            # PyOpenSSL's shutdown() doesn't take arguments
261
            return sock.shutdown()
262
          else:
263
            return sock.shutdown(arg1)
264

    
265
      except OpenSSL.SSL.WantWriteError:
266
        # OpenSSL wants to write, poll for POLLOUT
267
        event_override = select.POLLOUT
268
        continue
269

    
270
      except OpenSSL.SSL.WantReadError:
271
        # OpenSSL wants to read, poll for POLLIN
272
        event_override = select.POLLIN | select.POLLPRI
273
        continue
274

    
275
      except OpenSSL.SSL.WantX509LookupError:
276
        continue
277

    
278
      except OpenSSL.SSL.SysCallError, err:
279
        if op == SOCKOP_SEND:
280
          # arg1 is the data when writing
281
          if err.args and err.args[0] == -1 and arg1 == "":
282
            # errors when writing empty strings are expected
283
            # and can be ignored
284
            return 0
285

    
286
        elif op == SOCKOP_RECV:
287
          if err.args == (-1, _SSL_UNEXPECTED_EOF):
288
            return ""
289

    
290
        raise socket.error(err.args)
291

    
292
      except OpenSSL.SSL.Error, err:
293
        raise socket.error(err.args)
294

    
295
    except socket.error, err:
296
      if err.args and err.args[0] == errno.EAGAIN:
297
        # Ignore EAGAIN
298
        continue
299

    
300
      raise
301

    
302

    
303
def ShutdownConnection(poller, sock, close_timeout, write_timeout, msgreader,
304
                       force):
305
  """Closes the connection.
306

307
  """
308
  poller = select.poll()
309

    
310
  #print msgreader.peer_will_close, force
311
  if msgreader and msgreader.peer_will_close and not force:
312
    # Wait for peer to close
313
    try:
314
      # Check whether it's actually closed
315
      if not SocketOperation(poller, sock, SOCKOP_RECV, 1, close_timeout):
316
        return
317
    except (socket.error, HttpError, HttpSocketTimeout):
318
      # Ignore errors at this stage
319
      pass
320

    
321
  # Close the connection from our side
322
  try:
323
    SocketOperation(poller, sock, SOCKOP_SHUTDOWN, socket.SHUT_RDWR,
324
                    write_timeout)
325
  except HttpSocketTimeout:
326
    raise HttpError("Timeout while shutting down connection")
327
  except socket.error, err:
328
    raise HttpError("Error while shutting down connection: %s" % err)
329

    
330

    
331
class HttpSslParams(object):
332
  """Data class for SSL key and certificate.
333

334
  """
335
  def __init__(self, ssl_key_path, ssl_cert_path):
336
    """Initializes this class.
337

338
    @type ssl_key_path: string
339
    @param ssl_key_path: Path to file containing SSL key in PEM format
340
    @type ssl_cert_path: string
341
    @param ssl_cert_path: Path to file containing SSL certificate in PEM format
342

343
    """
344
    self.ssl_key_pem = utils.ReadFile(ssl_key_path)
345
    self.ssl_cert_pem = utils.ReadFile(ssl_cert_path)
346

    
347
  def GetKey(self):
348
    return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
349
                                          self.ssl_key_pem)
350

    
351
  def GetCertificate(self):
352
    return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
353
                                           self.ssl_cert_pem)
354

    
355

    
356
class HttpSocketBase(object):
357
  """Base class for HTTP server and client.
358

359
  """
360
  def __init__(self):
361
    self._using_ssl = None
362
    self._ssl_params = None
363
    self._ssl_key = None
364
    self._ssl_cert = None
365

    
366
  def _CreateSocket(self, ssl_params, ssl_verify_peer):
367
    """Creates a TCP socket and initializes SSL if needed.
368

369
    @type ssl_params: HttpSslParams
370
    @param ssl_params: SSL key and certificate
371
    @type ssl_verify_peer: bool
372
    @param ssl_verify_peer: Whether to require client certificate and compare
373
                            it with our certificate
374

375
    """
376
    self._ssl_params = ssl_params
377

    
378
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
379

    
380
    # Should we enable SSL?
381
    self._using_ssl = ssl_params is not None
382

    
383
    if not self._using_ssl:
384
      return sock
385

    
386
    self._ssl_key = ssl_params.GetKey()
387
    self._ssl_cert = ssl_params.GetCertificate()
388

    
389
    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
390
    ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
391

    
392
    ctx.use_privatekey(self._ssl_key)
393
    ctx.use_certificate(self._ssl_cert)
394
    ctx.check_privatekey()
395

    
396
    if ssl_verify_peer:
397
      ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
398
                     OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
399
                     self._SSLVerifyCallback)
400

    
401
    return OpenSSL.SSL.Connection(ctx, sock)
402

    
403
  def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
404
    """Verify the certificate provided by the peer
405

406
    We only compare fingerprints. The client must use the same certificate as
407
    we do on our side.
408

409
    """
410
    assert self._ssl_params, "SSL not initialized"
411

    
412
    return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and
413
            self._ssl_cert.digest("md5") == cert.digest("md5"))
414

    
415

    
416
class HttpMessage(object):
417
  """Data structure for HTTP message.
418

419
  """
420
  def __init__(self):
421
    self.start_line = None
422
    self.headers = None
423
    self.body = None
424
    self.decoded_body = None
425

    
426

    
427
class HttpClientToServerStartLine(object):
428
  """Data structure for HTTP request start line.
429

430
  """
431
  def __init__(self, method, path, version):
432
    self.method = method
433
    self.path = path
434
    self.version = version
435

    
436
  def __str__(self):
437
    return "%s %s %s" % (self.method, self.path, self.version)
438

    
439

    
440
class HttpServerToClientStartLine(object):
441
  """Data structure for HTTP response start line.
442

443
  """
444
  def __init__(self, version, code, reason):
445
    self.version = version
446
    self.code = code
447
    self.reason = reason
448

    
449
  def __str__(self):
450
    return "%s %s %s" % (self.version, self.code, self.reason)
451

    
452

    
453
class HttpMessageWriter(object):
454
  """Writes an HTTP message to a socket.
455

456
  """
457
  def __init__(self, sock, msg, write_timeout):
458
    self._msg = msg
459

    
460
    self._PrepareMessage()
461

    
462
    buf = self._FormatMessage()
463

    
464
    poller = select.poll()
465

    
466
    pos = 0
467
    end = len(buf)
468
    while pos < end:
469
      # Send only SOCK_BUF_SIZE bytes at a time
470
      data = buf[pos:pos+SOCK_BUF_SIZE]
471

    
472
      sent = SocketOperation(poller, sock, SOCKOP_SEND, data,
473
                             write_timeout)
474

    
475
      # Remove sent bytes
476
      pos += sent
477

    
478
    assert pos == end, "Message wasn't sent completely"
479

    
480
  def _PrepareMessage(self):
481
    """Prepares the HTTP message by setting mandatory headers.
482

483
    """
484
    # RFC2616, section 4.3: "The presence of a message-body in a request is
485
    # signaled by the inclusion of a Content-Length or Transfer-Encoding header
486
    # field in the request's message-headers."
487
    if self._msg.body:
488
      self._msg.headers[HTTP_CONTENT_LENGTH] = len(self._msg.body)
489

    
490
  def _FormatMessage(self):
491
    """Serializes the HTTP message into a string.
492

493
    """
494
    buf = StringIO()
495

    
496
    # Add start line
497
    buf.write(str(self._msg.start_line))
498
    buf.write("\r\n")
499

    
500
    # Add headers
501
    if self._msg.start_line.version != HTTP_0_9:
502
      for name, value in self._msg.headers.iteritems():
503
        buf.write("%s: %s\r\n" % (name, value))
504

    
505
    buf.write("\r\n")
506

    
507
    # Add message body if needed
508
    if self.HasMessageBody():
509
      buf.write(self._msg.body)
510

    
511
    elif self._msg.body:
512
      logging.warning("Ignoring message body")
513

    
514
    return buf.getvalue()
515

    
516
  def HasMessageBody(self):
517
    """Checks whether the HTTP message contains a body.
518

519
    Can be overriden by subclasses.
520

521
    """
522
    return bool(self._msg.body)
523

    
524

    
525
class HttpMessageReader(object):
526
  """Reads HTTP message from socket.
527

528
  """
529
  # Length limits
530
  START_LINE_LENGTH_MAX = None
531
  HEADER_LENGTH_MAX = None
532

    
533
  # Parser state machine
534
  PS_START_LINE = "start-line"
535
  PS_HEADERS = "headers"
536
  PS_BODY = "entity-body"
537
  PS_COMPLETE = "complete"
538

    
539
  def __init__(self, sock, msg, read_timeout):
540
    self.sock = sock
541
    self.msg = msg
542

    
543
    self.poller = select.poll()
544
    self.start_line_buffer = None
545
    self.header_buffer = StringIO()
546
    self.body_buffer = StringIO()
547
    self.parser_status = self.PS_START_LINE
548
    self.content_length = None
549
    self.peer_will_close = None
550

    
551
    buf = ""
552
    eof = False
553
    while self.parser_status != self.PS_COMPLETE:
554
      data = SocketOperation(self.poller, sock, SOCKOP_RECV, SOCK_BUF_SIZE,
555
                             read_timeout)
556

    
557
      if data:
558
        buf += data
559
      else:
560
        eof = True
561

    
562
      # Do some parsing and error checking while more data arrives
563
      buf = self._ContinueParsing(buf, eof)
564

    
565
      # Must be done only after the buffer has been evaluated
566
      # TODO: Connection-length < len(data read) and connection closed
567
      if (eof and
568
          self.parser_status in (self.PS_START_LINE,
569
                                 self.PS_HEADERS)):
570
        raise HttpError("Connection closed prematurely")
571

    
572
    # Parse rest
573
    buf = self._ContinueParsing(buf, True)
574

    
575
    assert self.parser_status == self.PS_COMPLETE
576
    assert not buf, "Parser didn't read full response"
577

    
578
    msg.body = self.body_buffer.getvalue()
579

    
580
    # TODO: Content-type, error handling
581
    if msg.body:
582
      msg.decoded_body = HttpJsonConverter().Decode(msg.body)
583
    else:
584
      msg.decoded_body = None
585

    
586
    if msg.decoded_body:
587
      logging.debug("Message body: %s", msg.decoded_body)
588

    
589
  def _ContinueParsing(self, buf, eof):
590
    """Main function for HTTP message state machine.
591

592
    @type buf: string
593
    @param buf: Receive buffer
594
    @type eof: bool
595
    @param eof: Whether we've reached EOF on the socket
596
    @rtype: string
597
    @return: Updated receive buffer
598

599
    """
600
    if self.parser_status == self.PS_START_LINE:
601
      # Expect start line
602
      while True:
603
        idx = buf.find("\r\n")
604

    
605
        # RFC2616, section 4.1: "In the interest of robustness, servers SHOULD
606
        # ignore any empty line(s) received where a Request-Line is expected.
607
        # In other words, if the server is reading the protocol stream at the
608
        # beginning of a message and receives a CRLF first, it should ignore
609
        # the CRLF."
610
        if idx == 0:
611
          # TODO: Limit number of CRLFs for safety?
612
          buf = buf[:2]
613
          continue
614

    
615
        if idx > 0:
616
          self.start_line_buffer = buf[:idx]
617

    
618
          self._CheckStartLineLength(len(self.start_line_buffer))
619

    
620
          # Remove status line, including CRLF
621
          buf = buf[idx + 2:]
622

    
623
          self.msg.start_line = self.ParseStartLine(self.start_line_buffer)
624

    
625
          self.parser_status = self.PS_HEADERS
626
        else:
627
          # Check whether incoming data is getting too large, otherwise we just
628
          # fill our read buffer.
629
          self._CheckStartLineLength(len(buf))
630

    
631
        break
632

    
633
    # TODO: Handle messages without headers
634
    if self.parser_status == self.PS_HEADERS:
635
      # Wait for header end
636
      idx = buf.find("\r\n\r\n")
637
      if idx >= 0:
638
        self.header_buffer.write(buf[:idx + 2])
639

    
640
        self._CheckHeaderLength(self.header_buffer.tell())
641

    
642
        # Remove headers, including CRLF
643
        buf = buf[idx + 4:]
644

    
645
        self._ParseHeaders()
646

    
647
        self.parser_status = self.PS_BODY
648
      else:
649
        # Check whether incoming data is getting too large, otherwise we just
650
        # fill our read buffer.
651
        self._CheckHeaderLength(len(buf))
652

    
653
    if self.parser_status == self.PS_BODY:
654
      # TODO: Implement max size for body_buffer
655
      self.body_buffer.write(buf)
656
      buf = ""
657

    
658
      # Check whether we've read everything
659
      #
660
      # RFC2616, section 4.4: "When a message-body is included with a message,
661
      # the transfer-length of that body is determined by one of the following
662
      # [...] 5. By the server closing the connection. (Closing the connection
663
      # cannot be used to indicate the end of a request body, since that would
664
      # leave no possibility for the server to send back a response.)"
665
      if (eof or
666
          self.content_length is None or
667
          (self.content_length is not None and
668
           self.body_buffer.tell() >= self.content_length)):
669
        self.parser_status = self.PS_COMPLETE
670

    
671
    return buf
672

    
673
  def _CheckStartLineLength(self, length):
674
    """Limits the start line buffer size.
675

676
    @type length: int
677
    @param length: Buffer size
678

679
    """
680
    if (self.START_LINE_LENGTH_MAX is not None and
681
        length > self.START_LINE_LENGTH_MAX):
682
      raise HttpError("Start line longer than %d chars" %
683
                       self.START_LINE_LENGTH_MAX)
684

    
685
  def _CheckHeaderLength(self, length):
686
    """Limits the header buffer size.
687

688
    @type length: int
689
    @param length: Buffer size
690

691
    """
692
    if (self.HEADER_LENGTH_MAX is not None and
693
        length > self.HEADER_LENGTH_MAX):
694
      raise HttpError("Headers longer than %d chars" % self.HEADER_LENGTH_MAX)
695

    
696
  def ParseStartLine(self, start_line):
697
    """Parses the start line of a message.
698

699
    Must be overriden by subclass.
700

701
    @type start_line: string
702
    @param start_line: Start line string
703

704
    """
705
    raise NotImplementedError()
706

    
707
  def _WillPeerCloseConnection(self):
708
    """Evaluate whether peer will close the connection.
709

710
    @rtype: bool
711
    @return: Whether peer will close the connection
712

713
    """
714
    # RFC2616, section 14.10: "HTTP/1.1 defines the "close" connection option
715
    # for the sender to signal that the connection will be closed after
716
    # completion of the response. For example,
717
    #
718
    #        Connection: close
719
    #
720
    # in either the request or the response header fields indicates that the
721
    # connection SHOULD NOT be considered `persistent' (section 8.1) after the
722
    # current request/response is complete."
723

    
724
    hdr_connection = self.msg.headers.get(HTTP_CONNECTION, None)
725
    if hdr_connection:
726
      hdr_connection = hdr_connection.lower()
727

    
728
    # An HTTP/1.1 server is assumed to stay open unless explicitly closed.
729
    if self.msg.start_line.version == HTTP_1_1:
730
      return (hdr_connection and "close" in hdr_connection)
731

    
732
    # Some HTTP/1.0 implementations have support for persistent connections,
733
    # using rules different than HTTP/1.1.
734

    
735
    # For older HTTP, Keep-Alive indicates persistent connection.
736
    if self.msg.headers.get(HTTP_KEEP_ALIVE):
737
      return False
738

    
739
    # At least Akamai returns a "Connection: Keep-Alive" header, which was
740
    # supposed to be sent by the client.
741
    if hdr_connection and "keep-alive" in hdr_connection:
742
      return False
743

    
744
    return True
745

    
746
  def _ParseHeaders(self):
747
    """Parses the headers.
748

749
    This function also adjusts internal variables based on header values.
750

751
    RFC2616, section 4.3: "The presence of a message-body in a request is
752
    signaled by the inclusion of a Content-Length or Transfer-Encoding header
753
    field in the request's message-headers."
754

755
    """
756
    # Parse headers
757
    self.header_buffer.seek(0, 0)
758
    self.msg.headers = mimetools.Message(self.header_buffer, 0)
759

    
760
    self.peer_will_close = self._WillPeerCloseConnection()
761

    
762
    # Do we have a Content-Length header?
763
    hdr_content_length = self.msg.headers.get(HTTP_CONTENT_LENGTH, None)
764
    if hdr_content_length:
765
      try:
766
        self.content_length = int(hdr_content_length)
767
      except ValueError:
768
        self.content_length = None
769
      if self.content_length is not None and self.content_length < 0:
770
        self.content_length = None
771

    
772
    # if the connection remains open and a content-length was not provided,
773
    # then assume that the connection WILL close.
774
    if self.content_length is None:
775
      self.peer_will_close = True