Ticket #5192: 5192_2.patch
| File 5192_2.patch, 15.8 KB (added by darfire, 21 months ago) |
|---|
-
twisted/web/test/test_newclient.py
16 16 from twisted.internet.error import ConnectionDone 17 17 from twisted.internet.defer import Deferred, succeed, fail 18 18 from twisted.internet.protocol import Protocol 19 from twisted.internet import reactor 19 20 from twisted.trial.unittest import TestCase 20 21 from twisted.test.proto_helpers import StringTransport, AccumulatingProtocol 21 22 from twisted.web._newclient import UNKNOWN_LENGTH, STATUS, HEADER, BODY, DONE … … 27 28 from twisted.web._newclient import ConnectionAborted 28 29 from twisted.web._newclient import BadHeaders, ResponseDone, PotentialDataLoss, ExcessWrite 29 30 from twisted.web._newclient import TransportProxyProducer, LengthEnforcingConsumer, makeStatefulDispatcher 31 from twisted.web._newclient import TIMEOUT_100_CONTINUE 30 32 from twisted.web.http_headers import Headers 31 33 from twisted.web.http import _DataLoss 32 34 from twisted.web.iweb import IBodyProducer, IResponse … … 793 795 """ 794 796 method = 'GET' 795 797 stopped = False 798 headers = Headers() 796 799 797 800 def writeTo(self, transport): 798 801 self.finished = Deferred() … … 811 814 returns a succeeded L{Deferred}. This vaguely emulates the behavior of a 812 815 L{Request} with no body producer. 813 816 """ 817 headers = Headers() 814 818 def writeTo(self, transport): 815 819 transport.write('SOME BYTES') 816 820 return succeed(None) 817 821 818 822 823 class BufferProducer(object): 824 """ 825 A body producer that outputs a string with a known length. 826 """ 827 implements(IBodyProducer) 819 828 829 def __init__(self, data): 830 self.data = data 831 self.length = len(data) 832 833 def startProducing(self, consumer): 834 consumer.write(self.data) 835 return succeed(None) 836 837 def pauseProducing(self): 838 pass 839 840 def stopProducing(self): 841 pass 842 820 843 class HTTP11ClientProtocolTests(TestCase): 821 844 """ 822 845 Tests for the HTTP 1.1 client protocol implementation, … … 879 902 L{RequestGenerationFailed} wrapping the underlying failure. 880 903 """ 881 904 class BrokenRequest: 905 headers = Headers() 882 906 def writeTo(self, transport): 883 907 return fail(ArbitraryException()) 884 908 … … 901 925 a L{Failure} of L{RequestGenerationFailed} wrapping that exception. 902 926 """ 903 927 class BrokenRequest: 928 headers = Headers() 904 929 def writeTo(self, transport): 905 930 raise ArbitraryException() 906 931 … … 1312 1337 [ConnectionAborted, _DataLoss]) 1313 1338 return deferred.addCallback(checkError) 1314 1339 1340 def test_expect100ContinueGot100Continue(self): 1341 """ 1342 When we expect 100-Continue and we receive an 100-Continue L{Response} 1343 the user writes the L{Request} body and receives back the second(final) 1344 L{Response}. 1345 """ 1346 producer = BufferProducer('X' * 10) 1315 1347 1348 headers = Headers({'host': ['example.com'], 1349 'expect': ['100-Continue']}) 1316 1350 1351 d = self.protocol.request(Request('POST', '/foo', headers, producer)) 1352 1353 self.transport.clear() 1354 1355 def cb100Continue(response): 1356 self.assertEqual(response.code, 200) 1357 1358 d.addCallback(cb100Continue) 1359 1360 self.protocol.dataReceived( 1361 "HTTP/1.1 100 Continue\r\n" 1362 "Content-Length: 3\r\n" 1363 "\r\n" 1364 '123' 1365 ) 1366 1367 self.protocol.dataReceived( 1368 "HTTP/1.1 200 OK\r\n" 1369 "Content-Length: 0\r\n" 1370 "\r\n" 1371 ) 1372 1373 self.assertEqual(self.transport.value(), 'X' * 10) 1374 1375 return d 1376 1377 1378 def test_expect100ContinueGotFinalStatus(self): 1379 """ 1380 When we expect 100-Continue and we receive a final status L{Response} 1381 we don't write the L{Request} body anymore and the user receives the 1382 first L{Response}. 1383 """ 1384 producer = BufferProducer('X' * 10) 1385 1386 headers = Headers({'host': ['example.com'], 'expect': ['100-Continue']}) 1387 1388 d = self.protocol.request(Request('POST', '/foo', headers, producer)) 1389 1390 self.transport.clear() 1391 1392 def cb100Continue(response): 1393 self.assertEqual(response.code, 200) 1394 1395 d.addCallback(cb100Continue) 1396 1397 1398 self.protocol.dataReceived( 1399 "HTTP/1.1 200 OK\r\n" 1400 "Content-Length: 0\r\n" 1401 "\r\n" 1402 ) 1403 1404 self.assertEqual(self.transport.value(), '') 1405 1406 return d 1407 1408 def test_expect100ContinueServerNotSupporting(self): 1409 """ 1410 When we expect 100-continue and the server doesn't respond with a 1411 response for upto TIMEOUT_100_CONTINUE seconds (maybe because it does 1412 not correctly handle this expectation) we continue with sending the 1413 request body. 1414 """ 1415 producer = BufferProducer('X' * 10) 1416 1417 headers = Headers({'host': ['example.com'], 'expect': ['100-Continue']}) 1418 1419 d = self.protocol.request(Request('POST', '/foo', headers, producer)) 1420 1421 self.transport.clear() 1422 1423 def cbResponse(response): 1424 self.assertEqual(response.code, 200) 1425 1426 d.addCallback(cbResponse) 1427 1428 d2 = Deferred() 1429 1430 d.chainDeferred(d2) 1431 1432 def laterCall(): 1433 try: 1434 self.assertEqual(self.transport.value(), 'X'*10) 1435 except Exception, e: 1436 d2.errback(e) 1437 return 1438 1439 self.protocol.dataReceived( 1440 "HTTP/1.1 200 OK\r\n" 1441 "Content-Length: 0\r\n" 1442 "\r\n" 1443 ) 1444 1445 reactor.callLater(TIMEOUT_100_CONTINUE + 1.0, laterCall) 1446 1447 self.assertEqual(self.transport.value(), '') 1448 1449 return d2 1450 1451 1452 1317 1453 class StringProducer: 1318 1454 """ 1319 1455 L{StringProducer} is a dummy body producer. -
twisted/web/_newclient.py
37 37 from twisted.internet.interfaces import IConsumer, IPushProducer 38 38 from twisted.internet.error import ConnectionDone 39 39 from twisted.internet.defer import Deferred, succeed, fail, maybeDeferred 40 from twisted.internet import reactor 40 41 from twisted.internet.protocol import Protocol 41 42 from twisted.protocols.basic import LineReceiver 43 from twisted.protocols.wire import Discard 42 44 from twisted.web.iweb import UNKNOWN_LENGTH, IResponse 43 45 from twisted.web.http_headers import Headers 44 46 from twisted.web.http import NO_CONTENT, NOT_MODIFIED … … 51 53 BODY = 'BODY' 52 54 DONE = 'DONE' 53 55 56 # Interval to wait for a Response to a Request with 'Expect: 100-Continue' 57 TIMEOUT_100_CONTINUE = 1.0 54 58 59 55 60 class BadHeaders(Exception): 56 61 """ 57 62 Headers passed to L{Request} were in some way invalid. … … 516 521 self._responseDeferred.errback(Failure(ResponseFailed([reason]))) 517 522 del self._responseDeferred 518 523 524 def _hasResponse(self): 525 return hasattr(self, 'response') 519 526 520 527 528 521 529 class Request: 522 530 """ 523 531 A L{Request} instance describes an HTTP request to be sent to an HTTP … … 566 574 transport.writeSequence(requestLines) 567 575 568 576 569 def _write ToChunked(self, transport):577 def _writeBodyToChunked(self, transport): 570 578 """ 571 Write this request to the given transport using chunked579 Write this request's body to the given transport using chunked 572 580 transfer-encoding to frame the body. 573 581 """ 574 self._writeHeaders(transport, 'Transfer-Encoding: chunked\r\n')575 582 encoder = ChunkedEncoder(transport) 576 583 encoder.registerProducer(self.bodyProducer, True) 577 584 d = self.bodyProducer.startProducing(encoder) … … 590 597 return d 591 598 592 599 593 def _write ToContentLength(self, transport):600 def _writeBodyToContentLength(self, transport): 594 601 """ 595 Write this request to the given transport using content-length to frame596 t he body.602 Write this request's body to the given transport using content-length 603 to frame the body. 597 604 """ 598 self._writeHeaders(599 transport,600 'Content-Length: %d\r\n' % (self.bodyProducer.length,))601 605 602 606 # This Deferred is used to signal an error in the data written to the 603 607 # encoder below. It can only errback and it will only do so before too … … 709 713 been completely written to the transport or with a L{Failure} if 710 714 there is any problem generating the request bytes. 711 715 """ 716 717 self._writeHeadersTo(transport) 718 return self._writeBodyTo(transport) 719 720 def _writeHeadersTo(self, transport): 721 """ 722 Format this L{Request}'s headers as HTTP/1.1 and write them 723 synchronously to the given transport 724 """ 725 TEorCL = None 712 726 if self.bodyProducer is not None: 713 727 if self.bodyProducer.length is UNKNOWN_LENGTH: 714 return self._writeToChunked(transport)728 TEorCL = "Transfer-Encoding: chunked\r\n" 715 729 else: 716 return self._writeToContentLength(transport) 730 TEorCL = 'Content-Length: %d\r\n' % (self.bodyProducer.length,) 731 732 self._writeHeaders(transport, TEorCL) 733 734 def _writeBodyTo(self, transport): 735 """ 736 Write this L{Request}'s body to the given transport, framing it 737 according to the given headers('Transport-Encoding' or 738 'Content-Length'). 739 740 @return: A L{Deferred} which fires with C{None} when the request has 741 been completely written the transport or with a L{Failure} if there 742 is any problem generating the request body bytes. If bodyProducer 743 is None the returned L{Deferred} is already fired. 744 """ 745 if self.bodyProducer is not None: 746 if self.bodyProducer.length is UNKNOWN_LENGTH: 747 return self._writeBodyToChunked(transport) 748 else: 749 return self._writeBodyToContentLength(transport) 717 750 else: 718 self._writeHeaders(transport, None)719 751 return succeed(None) 720 752 721 722 753 def stopWriting(self): 723 754 """ 724 755 Stop writing this request to the transport. This can only be called … … 1158 1189 self._producer.pauseProducing() 1159 1190 1160 1191 1192 class DiscardWithDeferred(Protocol): 1193 """ 1194 A L{Protocol} that discards all received data and that fires a L{Deferred} 1195 when all data has been received. 1161 1196 1197 @ivar finishedDeferred: L{Deferred} which fires with C{None} when all data 1198 has been received and with L{Failure} on error. 1199 1200 """ 1201 1202 def __init__(self): 1203 self.finishedDeferred = Deferred() 1204 1205 def dataReceived(self, data): 1206 pass 1207 1208 def connectionLost(self, reason): 1209 if reason.type == ResponseDone: 1210 self.finishedDeferred.callback(None) 1211 else: 1212 self.finishedDeferred.errback(reason) 1213 1214 1162 1215 class HTTP11ClientProtocol(Protocol): 1163 1216 """ 1164 1217 L{HTTP11ClientProtocol} is an implementation of the HTTP 1.1 client … … 1243 1296 return fail(RequestNotSent()) 1244 1297 1245 1298 self._state = 'TRANSMITTING' 1246 _requestDeferred = maybeDeferred(request.writeTo, self.transport)1247 self._finishedRequest = Deferred()1248 1299 1249 1300 # Keep track of the Request object in case we need to call stopWriting 1250 1301 # on it. 1251 1302 self._currentRequest = request 1252 1303 1253 self._transportProxy = TransportProxyProducer(self.transport) 1254 self._parser = HTTPClientParser(request, self._finishResponse) 1255 self._parser.makeConnection(self._transportProxy) 1256 self._responseDeferred = self._parser._responseDeferred 1304 self._finishedRequest = Deferred() 1257 1305 1306 if request.headers.hasHeader('expect'): 1307 expectations = request.headers.getRawHeaders('expect') 1308 _expects100Continue = '100-continue' in [x.lower() for x in 1309 expectations] 1310 else: 1311 _expects100Continue = False 1312 1313 if _expects100Continue: 1314 # This is synchronous, so jump right into WAITING 1315 request._writeHeadersTo(self.transport) 1316 1317 self._state = 'WAITING' 1318 1319 self._setupParser(request) 1320 1321 _100ContinueResponseDeferred = self._parser._responseDeferred 1322 1323 self._responseDeferred = Deferred() 1324 1325 _requestDeferred = Deferred() 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 def brokenServerTimer(): 1345 # In case the parser hasn't received any data yet, send 1346 # ourselves a fake 100-Continue response. 1347 1348 if not self._parser._hasResponse(): 1349 self.dataReceived( 1350 "HTTP/1.1 100 Continue\r\n" 1351 "Content-length: 0\r\n" 1352 "\r\n") 1353 1354 _brokenServerDelayed = reactor.callLater(TIMEOUT_100_CONTINUE, 1355 brokenServerTimer) 1356 1357 def ebFirstResponse(err): 1358 if _brokenServerDelayed.active(): 1359 _brokenServerDelayed.cancel() 1360 1361 # Tell the producer we don't need its body and forward the 1362 # error 1363 request.stopWriting() 1364 self._finishedRequest.errback(err) 1365 1366 def cbFirstResponse(response): 1367 if _brokenServerDelayed.active(): 1368 _brokenServerDelayed.cancel() 1369 1370 if response.code == 100: 1371 # Read and discard the response body 1372 1373 discarder = DiscardWithDeferred() 1374 1375 response.deliverBody(discarder) 1376 1377 discarder.finishedDeferred.addCallbacks(cb100ContinueBody, 1378 ebFirstResponse) 1379 1380 else: 1381 # Tell the producer we don't need its body and forward the 1382 # response 1383 request.stopWriting() 1384 self._finishedRequest.callback(response) 1385 1386 _100ContinueResponseDeferred.addCallbacks( 1387 cbFirstResponse, ebFirstResponse) 1388 1389 else: 1390 _requestDeferred = maybeDeferred(request.writeTo, self.transport) 1391 1392 self._setupParser(request) 1393 1394 self._responseDeferred = self._parser._responseDeferred 1395 1258 1396 def cbRequestWrotten(ignored): 1259 1397 if self._state == 'TRANSMITTING': 1260 1398 self._state = 'WAITING' … … 1279 1417 return self._finishedRequest 1280 1418 1281 1419 1420 def _setupParser(self, request): 1421 """ 1422 Setup a L{HTTPClientParser} for a L{Response} to a L{Request}. If this 1423 is not the first parser associated with this protocol, call 1424 L{HTTP11ClientProtocol._disconnectParser} first. 1425 1426 @param request: L{Request} waiting for a L{Response} 1427 """ 1428 self._transportProxy = TransportProxyProducer(self.transport) 1429 self._parser = HTTPClientParser(request, self._finishResponse) 1430 self._parser.makeConnection(self._transportProxy) 1431 1432 1282 1433 def _finishResponse(self, rest): 1283 1434 """ 1284 1435 Called by an L{HTTPClientParser} to indicate that it has parsed a
