Commit 0f476d49 authored by Senthil Kumaran's avatar Senthil Kumaran

Issue1491 - BaseHTTPServer incorrectly implements response code 100

parent 748cacee
......@@ -155,6 +155,17 @@ of which this module provides three different variants:
This method will parse and dispatch the request to the appropriate
:meth:`do_\*` method. You should never need to override it.
.. method:: handle_expect_100()
When a HTTP/1.1 compliant server receives a ``Expect: 100-continue``
request header it responds back with a ``100 Continue`` followed by ``200
OK`` headers.
This method can be overridden to raise an error if the server does not
want the client to continue. For e.g. server can chose to send ``417
Expectation Failed`` as a response header and ``return False``.
.. versionadded:: 3.2
.. method:: send_error(code, message=None)
Sends and logs a complete error reply to the client. The numeric *code*
......@@ -174,6 +185,15 @@ of which this module provides three different variants:
Writes a specific HTTP header to the output stream. *keyword* should
specify the header keyword, with *value* specifying its value.
.. method:: send_response_only(code, message=None)
Sends the reponse header only, used for the purposes when ``100
Continue`` response is sent by the server to the client. If the *message*
is not specified, the HTTP message corresponding the response *code* is
sent.
.. versionadded:: 3.2
.. method:: end_headers()
Sends a blank line, indicating the end of the HTTP headers in the
......
......@@ -322,6 +322,30 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
elif (conntype.lower() == 'keep-alive' and
self.protocol_version >= "HTTP/1.1"):
self.close_connection = 0
# Examine the headers and look for an Expect directive
expect = self.headers.get('Expect', "")
if (expect.lower() == "100-continue" and
self.protocol_version >= "HTTP/1.1" and
self.request_version >= "HTTP/1.1"):
if not self.handle_expect_100():
return False
return True
def handle_expect_100(self):
"""Decide what to do with an "Expect: 100-continue" header.
If the client is expecting a 100 Continue response, we must
respond with either a 100 Continue or a final response before
waiting for the request body. The default is to always respond
with a 100 Continue. You can behave differently (for example,
reject unauthorized requests) by overriding this method.
This method should either return True (possibly after sending
a 100 Continue response) or send an error response and return
False.
"""
self.send_response_only(100)
return True
def handle_one_request(self):
......@@ -400,6 +424,12 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""
self.log_request(code)
self.send_response_only(code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
def send_response_only(self, code, message=None):
"""Send the response header only."""
if message is None:
if code in self.responses:
message = self.responses[code][0]
......@@ -408,9 +438,6 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
if self.request_version != 'HTTP/0.9':
self.wfile.write(("%s %d %s\r\n" %
(self.protocol_version, code, message)).encode('ASCII', 'strict'))
# print (self.protocol_version, code, message)
self.send_header('Server', self.version_string())
self.send_header('Date', self.date_time_string())
def send_header(self, keyword, value):
"""Send a MIME header."""
......
......@@ -10,11 +10,13 @@ from http import server
import os
import sys
import re
import base64
import shutil
import urllib.parse
import http.client
import tempfile
from io import BytesIO
import unittest
from test import support
......@@ -403,8 +405,103 @@ class CGIHTTPServerTestCase(BaseTestCase):
class SocketlessRequestHandler(SimpleHTTPRequestHandler):
def __init__(self):
self.get_called = False
self.protocol_version = "HTTP/1.1"
def do_GET(self):
self.get_called = True
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.end_headers()
self.wfile.write(b'<html><body>Data</body></html>\r\n')
def log_message(self, format, *args):
pass
class RejectingSocketlessRequestHandler(SocketlessRequestHandler):
def handle_expect_100(self):
self.send_error(417)
return False
class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
"""Test the functionaility of the BaseHTTPServer.
Test the support for the Expect 100-continue header.
"""
HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK')
def setUp (self):
self.handler = SocketlessRequestHandler()
def send_typical_request(self, message):
input = BytesIO(message)
output = BytesIO()
self.handler.rfile = input
self.handler.wfile = output
self.handler.handle_one_request()
output.seek(0)
return output.readlines()
def verify_get_called(self):
self.assertTrue(self.handler.get_called)
def verify_expected_headers(self, headers):
for fieldName in b'Server: ', b'Date: ', b'Content-Type: ':
self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1)
def verify_http_server_response(self, response):
match = self.HTTPResponseMatch.search(response)
self.assertTrue(match is not None)
def test_http_1_1(self):
result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n')
self.verify_http_server_response(result[0])
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
def test_http_1_0(self):
result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n')
self.verify_http_server_response(result[0])
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
def test_http_0_9(self):
result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n')
self.assertEqual(len(result), 1)
self.assertEqual(result[0], b'<html><body>Data</body></html>\r\n')
self.verify_get_called()
def test_with_continue_1_0(self):
result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n')
self.verify_http_server_response(result[0])
self.verify_expected_headers(result[1:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
def test_with_continue_1_1(self):
result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n')
self.assertEqual(result[0], b'HTTP/1.1 100 Continue\r\n')
self.assertEqual(result[1], b'HTTP/1.1 200 OK\r\n')
self.verify_expected_headers(result[2:-1])
self.verify_get_called()
self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n')
def test_with_continue_rejected(self):
usual_handler = self.handler # Save to avoid breaking any subsequent tests.
self.handler = RejectingSocketlessRequestHandler()
result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n')
self.assertEqual(result[0], b'HTTP/1.1 417 Expectation Failed\r\n')
self.verify_expected_headers(result[1:-1])
# The expect handler should short circuit the usual get method by
# returning false here, so get_called should be false
self.assertFalse(self.handler.get_called)
self.assertEqual(sum(r == b'Connection: close\r\n' for r in result[1:-1]), 1)
self.handler = usual_handler # Restore to avoid breaking any subsequent tests.
class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
""" Test url parsing """
def setUp(self):
......@@ -431,6 +528,7 @@ def test_main(verbose=None):
cwd = os.getcwd()
try:
support.run_unittest(
BaseHTTPRequestHandlerTestCase,
BaseHTTPServerTestCase,
SimpleHTTPServerTestCase,
CGIHTTPServerTestCase,
......
......@@ -76,6 +76,9 @@ Core and Builtins
Library
-------
- Issue #1491: BaseHTTPServer nows send a 100 Continue response before sending
a 200 OK for the Expect: 100-continue request header.
- Issue #9360: Cleanup and improvements to the nntplib module. The API
now conforms to the philosophy of bytes and unicode separation in Python 3.
A test suite has also been added.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment