Ticket #5192: 100Continue.patch

File 100Continue.patch, 12.1 KB (added by darfire, 3 years ago)

Adds 100-Continue to HTTP11ClientProtocol

  • twisted/web/test/test_newclient.py

     
    793793    """ 
    794794    method = 'GET' 
    795795    stopped = False 
     796    headers = Headers() 
    796797 
    797798    def writeTo(self, transport): 
    798799        self.finished = Deferred() 
     
    811812    returns a succeeded L{Deferred}.  This vaguely emulates the behavior of a 
    812813    L{Request} with no body producer. 
    813814    """ 
     815    headers = Headers() 
    814816    def writeTo(self, transport): 
    815817        transport.write('SOME BYTES') 
    816818        return succeed(None) 
    817819 
    818820 
     821class BufferProducer(object): 
     822    """ 
     823    A body producer that outputs a string with a known length. 
     824    """ 
     825    implements(IBodyProducer) 
    819826 
     827    def __init__(self, data): 
     828        self.data = data 
     829        self.length = len(data) 
     830 
     831    def startProducing(self, consumer): 
     832        consumer.write(self.data) 
     833        return succeed(None) 
     834 
     835    def pauseProducing(self): 
     836        pass 
     837 
     838    def stopProducing(self): 
     839        pass 
     840 
    820841class HTTP11ClientProtocolTests(TestCase): 
    821842    """ 
    822843    Tests for the HTTP 1.1 client protocol implementation, 
     
    879900        L{RequestGenerationFailed} wrapping the underlying failure. 
    880901        """ 
    881902        class BrokenRequest: 
     903            headers = Headers() 
    882904            def writeTo(self, transport): 
    883905                return fail(ArbitraryException()) 
    884906 
     
    901923        a L{Failure} of L{RequestGenerationFailed} wrapping that exception. 
    902924        """ 
    903925        class BrokenRequest: 
     926            headers = Headers() 
    904927            def writeTo(self, transport): 
    905928                raise ArbitraryException() 
    906929 
     
    13061329        return assertResponseFailed(self, testResult, 
    13071330                                    [ConnectionAborted, _DataLoss]) 
    13081331 
     1332    def test_expect100ContinueGot100Continue(self): 
     1333        """ 
     1334        When we expect 100-Continue and we receive an 100-Continue L{Response} 
     1335        the user writes the L{Request} body and receives back the second(final) 
     1336        L{Response}. 
     1337        """ 
     1338        producer = BufferProducer('X' * 10) 
    13091339 
     1340        headers = Headers({'host': ['example.com'], 
     1341            'expect': ['100-Continue']}) 
    13101342 
     1343        d = self.protocol.request(Request('POST', '/foo', headers, producer)) 
     1344 
     1345        self.transport.clear() 
     1346 
     1347        def cb100Continue(response): 
     1348            self.assertEqual(response.code, 200) 
     1349 
     1350        d.addCallback(cb100Continue) 
     1351 
     1352        self.protocol.dataReceived( 
     1353                "HTTP/1.1 100 Continue\r\n" 
     1354                "Content-Length: 3\r\n" 
     1355                "\r\n" 
     1356                '123' 
     1357                ) 
     1358 
     1359        self.protocol.dataReceived( 
     1360                "HTTP/1.1 200 OK\r\n" 
     1361                "Content-Length: 0\r\n" 
     1362                "\r\n" 
     1363                ) 
     1364 
     1365        self.assertEqual(self.transport.value(), 'X' * 10) 
     1366 
     1367        return d 
     1368 
     1369 
     1370    def test_expect100ContinueGotFinalStatus(self): 
     1371        """ 
     1372        When we expect 100-Continue and we receive a final status L{Response} 
     1373        we don't write the L{Request} body anymore and the user receives the 
     1374        first L{Response}. 
     1375        """ 
     1376        producer = BufferProducer('X' * 10) 
     1377 
     1378        headers = Headers({'host': ['example.com'], 'expect': ['100-Continue']}) 
     1379 
     1380        d = self.protocol.request(Request('POST', '/foo', headers, producer)) 
     1381 
     1382        self.transport.clear() 
     1383 
     1384        def cb100Continue(response): 
     1385            self.assertEqual(response.code, 200) 
     1386 
     1387        d.addCallback(cb100Continue) 
     1388 
     1389 
     1390        self.protocol.dataReceived( 
     1391                "HTTP/1.1 200 OK\r\n" 
     1392                "Content-Length: 0\r\n" 
     1393                "\r\n" 
     1394                ) 
     1395 
     1396        self.assertEqual(self.transport.value(), '') 
     1397 
     1398        return d 
     1399 
     1400 
     1401 
    13111402class StringProducer: 
    13121403    """ 
    13131404    L{StringProducer} is a dummy body producer. 
  • twisted/web/_newclient.py

     
    3939from twisted.internet.defer import Deferred, succeed, fail, maybeDeferred 
    4040from twisted.internet.protocol import Protocol 
    4141from twisted.protocols.basic import LineReceiver 
     42from twisted.protocols.wire import Discard 
    4243from twisted.web.iweb import UNKNOWN_LENGTH, IResponse 
    4344from twisted.web.http_headers import Headers 
    4445from twisted.web.http import NO_CONTENT, NOT_MODIFIED 
     
    559560        transport.writeSequence(requestLines) 
    560561 
    561562 
    562     def _writeToChunked(self, transport): 
     563    def _writeBodyToChunked(self, transport): 
    563564        """ 
    564         Write this request to the given transport using chunked 
     565        Write this request's body to the given transport using chunked 
    565566        transfer-encoding to frame the body. 
    566567        """ 
    567         self._writeHeaders(transport, 'Transfer-Encoding: chunked\r\n') 
    568568        encoder = ChunkedEncoder(transport) 
    569569        encoder.registerProducer(self.bodyProducer, True) 
    570570        d = self.bodyProducer.startProducing(encoder) 
     
    583583        return d 
    584584 
    585585 
    586     def _writeToContentLength(self, transport): 
     586    def _writeBodyToContentLength(self, transport): 
    587587        """ 
    588         Write this request to the given transport using content-length to frame 
    589         the body. 
     588        Write this request's body to the given transport using content-length 
     589        to frame the body. 
    590590        """ 
    591         self._writeHeaders( 
    592             transport, 
    593             'Content-Length: %d\r\n' % (self.bodyProducer.length,)) 
    594591 
    595592        # This Deferred is used to signal an error in the data written to the 
    596593        # encoder below.  It can only errback and it will only do so before too 
     
    702699            been completely written to the transport or with a L{Failure} if 
    703700            there is any problem generating the request bytes. 
    704701        """ 
     702 
     703        self.writeHeadersTo(transport) 
     704        return self.writeBodyTo(transport) 
     705 
     706    def writeHeadersTo(self, transport): 
     707        """ 
     708        Format this L{Request}'s headers as HTTP/1.1 and write them 
     709        synchronously to the given transport  
     710        """ 
     711        TEorCL = None 
    705712        if self.bodyProducer is not None: 
    706713            if self.bodyProducer.length is UNKNOWN_LENGTH: 
    707                 return self._writeToChunked(transport) 
     714                TEorCL = "Transfer-Encoding: chunked\r\n" 
    708715            else: 
    709                 return self._writeToContentLength(transport) 
     716                TEorCL = 'Content-Length: %d\r\n' % (self.bodyProducer.length,) 
     717 
     718        self._writeHeaders(transport, TEorCL) 
     719 
     720    def writeBodyTo(self, transport): 
     721        """ 
     722        Write this L{Request}'s body to the given transport, framing it 
     723        according to the given headers('Transport-Encoding' or 
     724        'Content-Length'). 
     725 
     726        @return: A L{Deferred} which fires with C{None} when the request has 
     727            been completely written the transport or with a L{Failure} if there 
     728            is any problem generating the request body bytes. If bodyProducer 
     729            is None the returned L{Deferred} is already fired. 
     730        """ 
     731        if self.bodyProducer is not None: 
     732            if self.bodyProducer.length is UNKNOWN_LENGTH: 
     733                return self._writeBodyToChunked(transport) 
     734            else: 
     735                return self._writeBodyToContentLength(transport) 
    710736        else: 
    711             self._writeHeaders(transport, None) 
    712737            return succeed(None) 
    713738 
    714  
    715739    def stopWriting(self): 
    716740        """ 
    717741        Stop writing this request to the transport.  This can only be called 
     
    11511175            self._producer.pauseProducing() 
    11521176 
    11531177 
     1178class DiscardWithDeferred(Protocol): 
     1179    """ 
     1180    A L{Protocol} that discards all received data and that fires a L{Deferred} 
     1181    when all data has been received. 
    11541182 
     1183    @ivar finishedDeferred: L{Deferred} which fires with C{None} when all data 
     1184        has been received and with L{Failure} on error. 
     1185 
     1186    """ 
     1187 
     1188    def __init__(self): 
     1189        self.finishedDeferred = Deferred() 
     1190 
     1191    def dataReceived(self, data): 
     1192        pass 
     1193 
     1194    def connectionLost(self, reason): 
     1195        if reason.type == ResponseDone: 
     1196            self.finishedDeferred.callback(None) 
     1197        else: 
     1198            self.finishedDeferred.errback(reason) 
     1199 
     1200 
    11551201class HTTP11ClientProtocol(Protocol): 
    11561202    """ 
    11571203    L{HTTP11ClientProtocol} is an implementation of the HTTP 1.1 client 
     
    12361282            return fail(RequestNotSent()) 
    12371283 
    12381284        self._state = 'TRANSMITTING' 
    1239         _requestDeferred = maybeDeferred(request.writeTo, self.transport) 
    1240         self._finishedRequest = Deferred() 
    12411285 
    12421286        # Keep track of the Request object in case we need to call stopWriting 
    12431287        # on it. 
    12441288        self._currentRequest = request 
    12451289 
    1246         self._transportProxy = TransportProxyProducer(self.transport) 
    1247         self._parser = HTTPClientParser(request, self._finishResponse) 
    1248         self._parser.makeConnection(self._transportProxy) 
    1249         self._responseDeferred = self._parser._responseDeferred 
     1290        self._finishedRequest = Deferred() 
    12501291 
     1292        if request.headers.hasHeader('expect'): 
     1293            expectations = request.headers.getRawHeaders('expect') 
     1294            _expects100Continue = '100-continue' in [x.lower() for x in expectations] 
     1295        else: 
     1296            _expects100Continue = False 
     1297 
     1298        if _expects100Continue: 
     1299            # This is synchronous, so jump right into WAITING 
     1300            request.writeHeadersTo(self.transport) 
     1301 
     1302            self._state = 'WAITING' 
     1303 
     1304            self._setupParser(request) 
     1305 
     1306            _100ContinueResponseDeferred = self._parser._responseDeferred 
     1307 
     1308            self._responseDeferred = Deferred() 
     1309 
     1310            _requestDeferred = Deferred() 
     1311 
     1312            def ebFirstResponse(err): 
     1313                # Tell the producer we don't need its body and forward the 
     1314                # error 
     1315                request.stopWriting() 
     1316                self._finishedRequest.errback(err) 
     1317 
     1318            def cbFirstResponse(response): 
     1319                if response.code == 100: 
     1320                    # Read and discard the response body 
     1321 
     1322                    discarder = DiscardWithDeferred() 
     1323 
     1324                    response.deliverBody(discarder) 
     1325 
     1326 
     1327                    def cb100ContinueBody(ignored): 
     1328                        # Start sending the request body 
     1329 
     1330                        self._state = 'TRANSMITTING' 
     1331 
     1332                        self._disconnectParser(None) 
     1333 
     1334                        self._setupParser(request) 
     1335 
     1336                        _requestBodyDeferred = maybeDeferred( 
     1337                                request.writeBodyTo, self.transport) 
     1338 
     1339                        _requestBodyDeferred.chainDeferred(_requestDeferred) 
     1340 
     1341                        self._parser._responseDeferred.chainDeferred( 
     1342                                self._responseDeferred) 
     1343 
     1344                    discarder.finishedDeferred.addCallbacks(cb100ContinueBody, 
     1345                            ebFirstResponse) 
     1346 
     1347                else: 
     1348                    request.stopWriting() 
     1349                    self._finishedRequest.callback(response) 
     1350 
     1351            _100ContinueResponseDeferred.addCallbacks( 
     1352                    cbFirstResponse, ebFirstResponse) 
     1353 
     1354        else: 
     1355            _requestDeferred = maybeDeferred(request.writeTo, self.transport) 
     1356 
     1357            self._setupParser(request) 
     1358 
     1359            self._responseDeferred = self._parser._responseDeferred 
     1360 
    12511361        def cbRequestWrotten(ignored): 
    12521362            if self._state == 'TRANSMITTING': 
    12531363                self._state = 'WAITING' 
     
    12721382        return self._finishedRequest 
    12731383 
    12741384 
     1385    def _setupParser(self, request): 
     1386        """ 
     1387        Setup a L{HTTPClientParser} for a L{Response} to a L{Request}. If this 
     1388        is not the first parser associated with this protocol, call 
     1389        L{HTTP11ClientProtocol._disconnectParser} first. 
     1390 
     1391        @param request: L{Request} waiting for a L{Response} 
     1392        """ 
     1393        self._transportProxy = TransportProxyProducer(self.transport) 
     1394        self._parser = HTTPClientParser(request, self._finishResponse) 
     1395        self._parser.makeConnection(self._transportProxy) 
     1396 
     1397 
    12751398    def _finishResponse(self, rest): 
    12761399        """ 
    12771400        Called by an L{HTTPClientParser} to indicate that it has parsed a