Statistics
| Branch: | Tag: | Revision:

root / lib / http / __init__.py @ 99b5ef90

History | View | Annotate | Download (20.7 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_CLIENT_THREADS = 10
40

    
41
HTTP_GANETI_VERSION = "Ganeti %s" % constants.RELEASE_VERSION
42

    
43
HTTP_OK = 200
44
HTTP_NO_CONTENT = 204
45
HTTP_NOT_MODIFIED = 304
46

    
47
HTTP_0_9 = "HTTP/0.9"
48
HTTP_1_0 = "HTTP/1.0"
49
HTTP_1_1 = "HTTP/1.1"
50

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

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

    
67
_SSL_UNEXPECTED_EOF = "Unexpected EOF"
68

    
69
# Socket operations
70
(SOCKOP_SEND,
71
 SOCKOP_RECV,
72
 SOCKOP_SHUTDOWN) = range(3)
73

    
74

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

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

80
  """
81

    
82

    
83
class _HttpClientError(Exception):
84
  """Internal exception for HTTP client errors.
85

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

88
  """
89

    
90

    
91
class HttpSocketTimeout(Exception):
92
  """Internal exception for socket timeouts.
93

94
  This should only be used for internal error reporting.
95

96
  """
97

    
98

    
99
class HttpException(Exception):
100
  code = None
101
  message = None
102

    
103
  def __init__(self, message=None):
104
    Exception.__init__(self)
105
    if message is not None:
106
      self.message = message
107

    
108

    
109
class HttpBadRequest(HttpException):
110
  code = 400
111

    
112

    
113
class HttpForbidden(HttpException):
114
  code = 403
115

    
116

    
117
class HttpNotFound(HttpException):
118
  code = 404
119

    
120

    
121
class HttpGone(HttpException):
122
  code = 410
123

    
124

    
125
class HttpLengthRequired(HttpException):
126
  code = 411
127

    
128

    
129
class HttpInternalError(HttpException):
130
  code = 500
131

    
132

    
133
class HttpNotImplemented(HttpException):
134
  code = 501
135

    
136

    
137
class HttpServiceUnavailable(HttpException):
138
  code = 503
139

    
140

    
141
class HttpVersionNotSupported(HttpException):
142
  code = 505
143

    
144

    
145
class HttpJsonConverter:
146
  CONTENT_TYPE = "application/json"
147

    
148
  def Encode(self, data):
149
    return serializer.DumpJson(data)
150

    
151
  def Decode(self, data):
152
    return serializer.LoadJson(data)
153

    
154

    
155
def WaitForSocketCondition(poller, sock, event, timeout):
156
  """Waits for a condition to occur on the socket.
157

158
  @type poller: select.Poller
159
  @param poller: Poller object as created by select.poll()
160
  @type sock: socket
161
  @param socket: Wait for events on this socket
162
  @type event: int
163
  @param event: ORed condition (see select module)
164
  @type timeout: float or None
165
  @param timeout: Timeout in seconds
166
  @rtype: int or None
167
  @return: None for timeout, otherwise occured conditions
168

169
  """
170
  check = (event | select.POLLPRI |
171
           select.POLLNVAL | select.POLLHUP | select.POLLERR)
172

    
173
  if timeout is not None:
174
    # Poller object expects milliseconds
175
    timeout *= 1000
176

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

    
193

    
194
def SocketOperation(poller, sock, op, arg1, timeout):
195
  """Wrapper around socket functions.
196

197
  This function abstracts error handling for socket operations, especially
198
  for the complicated interaction with OpenSSL.
199

200
  @type poller: select.Poller
201
  @param poller: Poller object as created by select.poll()
202
  @type sock: socket
203
  @param socket: Socket for the operation
204
  @type op: int
205
  @param op: Operation to execute (SOCKOP_* constants)
206
  @type arg1: any
207
  @param arg1: Parameter for function (if needed)
208
  @type timeout: None or float
209
  @param timeout: Timeout in seconds or None
210

211
  """
212
  # TODO: event_poll/event_check/override
213
  if op == SOCKOP_SEND:
214
    event_poll = select.POLLOUT
215
    event_check = select.POLLOUT
216

    
217
  elif op == SOCKOP_RECV:
218
    event_poll = select.POLLIN
219
    event_check = select.POLLIN | select.POLLPRI
220

    
221
  elif op == SOCKOP_SHUTDOWN:
222
    event_poll = None
223
    event_check = None
224

    
225
    # The timeout is only used when OpenSSL requests polling for a condition.
226
    # It is not advisable to have no timeout for shutdown.
227
    assert timeout
228

    
229
  else:
230
    raise AssertionError("Invalid socket operation")
231

    
232
  # No override by default
233
  event_override = 0
234

    
235
  while True:
236
    # Poll only for certain operations and when asked for by an override
237
    if event_override or op in (SOCKOP_SEND, SOCKOP_RECV):
238
      if event_override:
239
        wait_for_event = event_override
240
      else:
241
        wait_for_event = event_poll
242

    
243
      event = WaitForSocketCondition(poller, sock, wait_for_event, timeout)
244
      if event is None:
245
        raise HttpSocketTimeout()
246

    
247
      if (op == SOCKOP_RECV and
248
          event & (select.POLLNVAL | select.POLLHUP | select.POLLERR)):
249
        return ""
250

    
251
      if not event & wait_for_event:
252
        continue
253

    
254
    # Reset override
255
    event_override = 0
256

    
257
    try:
258
      try:
259
        if op == SOCKOP_SEND:
260
          return sock.send(arg1)
261

    
262
        elif op == SOCKOP_RECV:
263
          return sock.recv(arg1)
264

    
265
        elif op == SOCKOP_SHUTDOWN:
266
          if isinstance(sock, OpenSSL.SSL.ConnectionType):
267
            # PyOpenSSL's shutdown() doesn't take arguments
268
            return sock.shutdown()
269
          else:
270
            return sock.shutdown(arg1)
271

    
272
      except OpenSSL.SSL.WantWriteError:
273
        # OpenSSL wants to write, poll for POLLOUT
274
        event_override = select.POLLOUT
275
        continue
276

    
277
      except OpenSSL.SSL.WantReadError:
278
        # OpenSSL wants to read, poll for POLLIN
279
        event_override = select.POLLIN | select.POLLPRI
280
        continue
281

    
282
      except OpenSSL.SSL.WantX509LookupError:
283
        continue
284

    
285
      except OpenSSL.SSL.SysCallError, err:
286
        if op == SOCKOP_SEND:
287
          # arg1 is the data when writing
288
          if err.args and err.args[0] == -1 and arg1 == "":
289
            # errors when writing empty strings are expected
290
            # and can be ignored
291
            return 0
292

    
293
        elif op == SOCKOP_RECV:
294
          if err.args == (-1, _SSL_UNEXPECTED_EOF):
295
            return ""
296

    
297
        raise socket.error(err.args)
298

    
299
      except OpenSSL.SSL.Error, err:
300
        raise socket.error(err.args)
301

    
302
    except socket.error, err:
303
      if err.args and err.args[0] == errno.EAGAIN:
304
        # Ignore EAGAIN
305
        continue
306

    
307
      raise
308

    
309

    
310
def ShutdownConnection(poller, sock, close_timeout, write_timeout, msgreader,
311
                       force):
312
  """Closes the connection.
313

314
  """
315
  poller = select.poll()
316

    
317
  #print msgreader.peer_will_close, force
318
  if msgreader and msgreader.peer_will_close and not force:
319
    # Wait for peer to close
320
    try:
321
      # Check whether it's actually closed
322
      if not SocketOperation(poller, sock, SOCKOP_RECV, 1, close_timeout):
323
        return
324
    except (socket.error, HttpError, HttpSocketTimeout):
325
      # Ignore errors at this stage
326
      pass
327

    
328
  # Close the connection from our side
329
  try:
330
    SocketOperation(poller, sock, SOCKOP_SHUTDOWN, socket.SHUT_RDWR,
331
                    write_timeout)
332
  except HttpSocketTimeout:
333
    raise HttpError("Timeout while shutting down connection")
334
  except socket.error, err:
335
    raise HttpError("Error while shutting down connection: %s" % err)
336

    
337

    
338
class HttpSslParams(object):
339
  """Data class for SSL key and certificate.
340

341
  """
342
  def __init__(self, ssl_key_path, ssl_cert_path):
343
    """Initializes this class.
344

345
    @type ssl_key_path: string
346
    @param ssl_key_path: Path to file containing SSL key in PEM format
347
    @type ssl_cert_path: string
348
    @param ssl_cert_path: Path to file containing SSL certificate in PEM format
349

350
    """
351
    self.ssl_key_pem = utils.ReadFile(ssl_key_path)
352
    self.ssl_cert_pem = utils.ReadFile(ssl_cert_path)
353

    
354
  def GetKey(self):
355
    return OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
356
                                          self.ssl_key_pem)
357

    
358
  def GetCertificate(self):
359
    return OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
360
                                           self.ssl_cert_pem)
361

    
362

    
363
class HttpSocketBase(object):
364
  """Base class for HTTP server and client.
365

366
  """
367
  def __init__(self):
368
    self._using_ssl = None
369
    self._ssl_params = None
370
    self._ssl_key = None
371
    self._ssl_cert = None
372

    
373
  def _CreateSocket(self, ssl_params, ssl_verify_peer):
374
    """Creates a TCP socket and initializes SSL if needed.
375

376
    @type ssl_params: HttpSslParams
377
    @param ssl_params: SSL key and certificate
378
    @type ssl_verify_peer: bool
379
    @param ssl_verify_peer: Whether to require client certificate and compare
380
                            it with our certificate
381

382
    """
383
    self._ssl_params = ssl_params
384

    
385
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
386

    
387
    # Should we enable SSL?
388
    self._using_ssl = ssl_params is not None
389

    
390
    if not self._using_ssl:
391
      return sock
392

    
393
    self._ssl_key = ssl_params.GetKey()
394
    self._ssl_cert = ssl_params.GetCertificate()
395

    
396
    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
397
    ctx.set_options(OpenSSL.SSL.OP_NO_SSLv2)
398

    
399
    ctx.use_privatekey(self._ssl_key)
400
    ctx.use_certificate(self._ssl_cert)
401
    ctx.check_privatekey()
402

    
403
    if ssl_verify_peer:
404
      ctx.set_verify(OpenSSL.SSL.VERIFY_PEER |
405
                     OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
406
                     self._SSLVerifyCallback)
407

    
408
    return OpenSSL.SSL.Connection(ctx, sock)
409

    
410
  def _SSLVerifyCallback(self, conn, cert, errnum, errdepth, ok):
411
    """Verify the certificate provided by the peer
412

413
    We only compare fingerprints. The client must use the same certificate as
414
    we do on our side.
415

416
    """
417
    assert self._ssl_params, "SSL not initialized"
418

    
419
    return (self._ssl_cert.digest("sha1") == cert.digest("sha1") and
420
            self._ssl_cert.digest("md5") == cert.digest("md5"))
421

    
422

    
423
class HttpMessage(object):
424
  """Data structure for HTTP message.
425

426
  """
427
  def __init__(self):
428
    self.start_line = None
429
    self.headers = None
430
    self.body = None
431
    self.decoded_body = None
432

    
433

    
434
class HttpClientToServerStartLine(object):
435
  """Data structure for HTTP request start line.
436

437
  """
438
  def __init__(self, method, path, version):
439
    self.method = method
440
    self.path = path
441
    self.version = version
442

    
443
  def __str__(self):
444
    return "%s %s %s" % (self.method, self.path, self.version)
445

    
446

    
447
class HttpServerToClientStartLine(object):
448
  """Data structure for HTTP response start line.
449

450
  """
451
  def __init__(self, version, code, reason):
452
    self.version = version
453
    self.code = code
454
    self.reason = reason
455

    
456
  def __str__(self):
457
    return "%s %s %s" % (self.version, self.code, self.reason)
458

    
459

    
460
class HttpMessageWriter(object):
461
  """Writes an HTTP message to a socket.
462

463
  """
464
  def __init__(self, sock, msg, write_timeout):
465
    self._msg = msg
466

    
467
    self._PrepareMessage()
468

    
469
    buf = self._FormatMessage()
470

    
471
    poller = select.poll()
472
    while buf:
473
      # Send only 4 KB at a time
474
      data = buf[:4096]
475

    
476
      sent = SocketOperation(poller, sock, SOCKOP_SEND, data,
477
                             write_timeout)
478

    
479
      # Remove sent bytes
480
      buf = buf[sent:]
481

    
482
    assert not buf, "Message wasn't sent completely"
483

    
484
  def _PrepareMessage(self):
485
    """Prepares the HTTP message by setting mandatory headers.
486

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

    
494
  def _FormatMessage(self):
495
    """Serializes the HTTP message into a string.
496

497
    """
498
    buf = StringIO()
499

    
500
    # Add start line
501
    buf.write(str(self._msg.start_line))
502
    buf.write("\r\n")
503

    
504
    # Add headers
505
    if self._msg.start_line.version != HTTP_0_9:
506
      for name, value in self._msg.headers.iteritems():
507
        buf.write("%s: %s\r\n" % (name, value))
508

    
509
    buf.write("\r\n")
510

    
511
    # Add message body if needed
512
    if self.HasMessageBody():
513
      buf.write(self._msg.body)
514

    
515
    elif self._msg.body:
516
      logging.warning("Ignoring message body")
517

    
518
    return buf.getvalue()
519

    
520
  def HasMessageBody(self):
521
    """Checks whether the HTTP message contains a body.
522

523
    Can be overriden by subclasses.
524

525
    """
526
    return bool(self._msg.body)
527

    
528

    
529
class HttpMessageReader(object):
530
  """Reads HTTP message from socket.
531

532
  """
533
  # Length limits
534
  START_LINE_LENGTH_MAX = None
535
  HEADER_LENGTH_MAX = None
536

    
537
  # Parser state machine
538
  PS_START_LINE = "start-line"
539
  PS_HEADERS = "headers"
540
  PS_BODY = "entity-body"
541
  PS_COMPLETE = "complete"
542

    
543
  def __init__(self, sock, msg, read_timeout):
544
    self.sock = sock
545
    self.msg = msg
546

    
547
    self.poller = select.poll()
548
    self.start_line_buffer = None
549
    self.header_buffer = StringIO()
550
    self.body_buffer = StringIO()
551
    self.parser_status = self.PS_START_LINE
552
    self.content_length = None
553
    self.peer_will_close = None
554

    
555
    buf = ""
556
    eof = False
557
    while self.parser_status != self.PS_COMPLETE:
558
      data = SocketOperation(self.poller, sock, SOCKOP_RECV, 4096,
559
                             read_timeout)
560

    
561
      if data:
562
        buf += data
563
      else:
564
        eof = True
565

    
566
      # Do some parsing and error checking while more data arrives
567
      buf = self._ContinueParsing(buf, eof)
568

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

    
576
    # Parse rest
577
    buf = self._ContinueParsing(buf, True)
578

    
579
    assert self.parser_status == self.PS_COMPLETE
580
    assert not buf, "Parser didn't read full response"
581

    
582
    msg.body = self.body_buffer.getvalue()
583

    
584
    # TODO: Content-type, error handling
585
    if msg.body:
586
      msg.decoded_body = HttpJsonConverter().Decode(msg.body)
587
    else:
588
      msg.decoded_body = None
589

    
590
    if msg.decoded_body:
591
      logging.debug("Message body: %s", msg.decoded_body)
592

    
593
  def _ContinueParsing(self, buf, eof):
594
    """Main function for HTTP message state machine.
595

596
    @type buf: string
597
    @param buf: Receive buffer
598
    @type eof: bool
599
    @param eof: Whether we've reached EOF on the socket
600
    @rtype: string
601
    @return: Updated receive buffer
602

603
    """
604
    if self.parser_status == self.PS_START_LINE:
605
      # Expect start line
606
      while True:
607
        idx = buf.find("\r\n")
608

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

    
619
        if idx > 0:
620
          self.start_line_buffer = buf[:idx]
621

    
622
          self._CheckStartLineLength(len(self.start_line_buffer))
623

    
624
          # Remove status line, including CRLF
625
          buf = buf[idx + 2:]
626

    
627
          self.msg.start_line = self.ParseStartLine(self.start_line_buffer)
628

    
629
          self.parser_status = self.PS_HEADERS
630
        else:
631
          # Check whether incoming data is getting too large, otherwise we just
632
          # fill our read buffer.
633
          self._CheckStartLineLength(len(buf))
634

    
635
        break
636

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

    
644
        self._CheckHeaderLength(self.header_buffer.tell())
645

    
646
        # Remove headers, including CRLF
647
        buf = buf[idx + 4:]
648

    
649
        self._ParseHeaders()
650

    
651
        self.parser_status = self.PS_BODY
652
      else:
653
        # Check whether incoming data is getting too large, otherwise we just
654
        # fill our read buffer.
655
        self._CheckHeaderLength(len(buf))
656

    
657
    if self.parser_status == self.PS_BODY:
658
      # TODO: Implement max size for body_buffer
659
      self.body_buffer.write(buf)
660
      buf = ""
661

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

    
675
    return buf
676

    
677
  def _CheckStartLineLength(self, length):
678
    """Limits the start line buffer size.
679

680
    @type length: int
681
    @param length: Buffer size
682

683
    """
684
    if (self.START_LINE_LENGTH_MAX is not None and
685
        length > self.START_LINE_LENGTH_MAX):
686
      raise HttpError("Start line longer than %d chars" %
687
                       self.START_LINE_LENGTH_MAX)
688

    
689
  def _CheckHeaderLength(self, length):
690
    """Limits the header buffer size.
691

692
    @type length: int
693
    @param length: Buffer size
694

695
    """
696
    if (self.HEADER_LENGTH_MAX is not None and
697
        length > self.HEADER_LENGTH_MAX):
698
      raise HttpError("Headers longer than %d chars" % self.HEADER_LENGTH_MAX)
699

    
700
  def ParseStartLine(self, start_line):
701
    """Parses the start line of a message.
702

703
    Must be overriden by subclass.
704

705
    @type start_line: string
706
    @param start_line: Start line string
707

708
    """
709
    raise NotImplementedError()
710

    
711
  def _WillPeerCloseConnection(self):
712
    """Evaluate whether peer will close the connection.
713

714
    @rtype: bool
715
    @return: Whether peer will close the connection
716

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

    
728
    hdr_connection = self.msg.headers.get(HTTP_CONNECTION, None)
729
    if hdr_connection:
730
      hdr_connection = hdr_connection.lower()
731

    
732
    # An HTTP/1.1 server is assumed to stay open unless explicitly closed.
733
    if self.msg.start_line.version == HTTP_1_1:
734
      return (hdr_connection and "close" in hdr_connection)
735

    
736
    # Some HTTP/1.0 implementations have support for persistent connections,
737
    # using rules different than HTTP/1.1.
738

    
739
    # For older HTTP, Keep-Alive indicates persistent connection.
740
    if self.msg.headers.get(HTTP_KEEP_ALIVE):
741
      return False
742

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

    
748
    return True
749

    
750
  def _ParseHeaders(self):
751
    """Parses the headers.
752

753
    This function also adjusts internal variables based on header values.
754

755
    RFC2616, section 4.3: "The presence of a message-body in a request is
756
    signaled by the inclusion of a Content-Length or Transfer-Encoding header
757
    field in the request's message-headers."
758

759
    """
760
    # Parse headers
761
    self.header_buffer.seek(0, 0)
762
    self.msg.headers = mimetools.Message(self.header_buffer, 0)
763

    
764
    self.peer_will_close = self._WillPeerCloseConnection()
765

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

    
776
    # if the connection remains open and a content-length was not provided,
777
    # then assume that the connection WILL close.
778
    if self.content_length is None:
779
      self.peer_will_close = True