Ticket #5192: 100Continue.patch
| File 100Continue.patch, 12.1 KB (added by darfire, 22 months ago) |
|---|
-
twisted/web/test/test_newclient.py
793 793 """ 794 794 method = 'GET' 795 795 stopped = False 796 headers = Headers() 796 797 797 798 def writeTo(self, transport): 798 799 self.finished = Deferred() … … 811 812 returns a succeeded L{Deferred}. This vaguely emulates the behavior of a 812 813 L{Request} with no body producer. 813 814 """ 815 headers = Headers() 814 816 def writeTo(self, transport): 815 817 transport.write('SOME BYTES') 816 818 return succeed(None) 817 819 818 820 821 class BufferProducer(object): 822 """ 823 A body producer that outputs a string with a known length. 824 """ 825 implements(IBodyProducer) 819 826 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 820 841 class HTTP11ClientProtocolTests(TestCase): 821 842 """ 822 843 Tests for the HTTP 1.1 client protocol implementation, … … 879 900 L{RequestGenerationFailed} wrapping the underlying failure. 880 901 """ 881 902 class BrokenRequest: 903 headers = Headers() 882 904 def writeTo(self, transport): 883 905 return fail(ArbitraryException()) 884 906 … … 901 923 a L{Failure} of L{RequestGenerationFailed} wrapping that exception. 902 924 """ 903 925 class BrokenRequest: 926 headers = Headers() 904 927 def writeTo(self, transport): 905 928 raise ArbitraryException() 906 929 … … 1306 1329 return assertResponseFailed(self, testResult, 1307 1330 [ConnectionAborted, _DataLoss]) 1308 1331 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) 1309 1339 1340 headers = Headers({'host': ['example.com'], 1341 'expect': ['100-Continue']}) 1310 1342 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 1311 1402 class StringProducer: 1312 1403 """ 1313 1404 L{StringProducer} is a dummy body producer. -
twisted/web/_newclient.py
39 39 from twisted.internet.defer import Deferred, succeed, fail, maybeDeferred 40 40 from twisted.internet.protocol import Protocol 41 41 from twisted.protocols.basic import LineReceiver 42 from twisted.protocols.wire import Discard 42 43 from twisted.web.iweb import UNKNOWN_LENGTH, IResponse 43 44 from twisted.web.http_headers import Headers 44 45 from twisted.web.http import NO_CONTENT, NOT_MODIFIED … … 559 560 transport.writeSequence(requestLines) 560 561 561 562 562 def _write ToChunked(self, transport):563 def _writeBodyToChunked(self, transport): 563 564 """ 564 Write this request to the given transport using chunked565 Write this request's body to the given transport using chunked 565 566 transfer-encoding to frame the body. 566 567 """ 567 self._writeHeaders(transport, 'Transfer-Encoding: chunked\r\n')568 568 encoder = ChunkedEncoder(transport) 569 569 encoder.registerProducer(self.bodyProducer, True) 570 570 d = self.bodyProducer.startProducing(encoder) … … 583 583 return d 584 584 585 585 586 def _write ToContentLength(self, transport):586 def _writeBodyToContentLength(self, transport): 587 587 """ 588 Write this request to the given transport using content-length to frame589 t he body.588 Write this request's body to the given transport using content-length 589 to frame the body. 590 590 """ 591 self._writeHeaders(592 transport,593 'Content-Length: %d\r\n' % (self.bodyProducer.length,))594 591 595 592 # This Deferred is used to signal an error in the data written to the 596 593 # encoder below. It can only errback and it will only do so before too … … 702 699 been completely written to the transport or with a L{Failure} if 703 700 there is any problem generating the request bytes. 704 701 """ 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 705 712 if self.bodyProducer is not None: 706 713 if self.bodyProducer.length is UNKNOWN_LENGTH: 707 return self._writeToChunked(transport)714 TEorCL = "Transfer-Encoding: chunked\r\n" 708 715 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) 710 736 else: 711 self._writeHeaders(transport, None)712 737 return succeed(None) 713 738 714 715 739 def stopWriting(self): 716 740 """ 717 741 Stop writing this request to the transport. This can only be called … … 1151 1175 self._producer.pauseProducing() 1152 1176 1153 1177 1178 class 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. 1154 1182 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 1155 1201 class HTTP11ClientProtocol(Protocol): 1156 1202 """ 1157 1203 L{HTTP11ClientProtocol} is an implementation of the HTTP 1.1 client … … 1236 1282 return fail(RequestNotSent()) 1237 1283 1238 1284 self._state = 'TRANSMITTING' 1239 _requestDeferred = maybeDeferred(request.writeTo, self.transport)1240 self._finishedRequest = Deferred()1241 1285 1242 1286 # Keep track of the Request object in case we need to call stopWriting 1243 1287 # on it. 1244 1288 self._currentRequest = request 1245 1289 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() 1250 1291 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 1251 1361 def cbRequestWrotten(ignored): 1252 1362 if self._state == 'TRANSMITTING': 1253 1363 self._state = 'WAITING' … … 1272 1382 return self._finishedRequest 1273 1383 1274 1384 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 1275 1398 def _finishResponse(self, rest): 1276 1399 """ 1277 1400 Called by an L{HTTPClientParser} to indicate that it has parsed a
