# -*- coding: utf-8 -*- """ head_request.py ~~~~~~~~~~~~~~~ A short example that demonstrates a client that makes HEAD requests to certain websites. This example is intended as a reproduction of nghttp2 issue 396, for the purposes of compatibility testing. """ from __future__ import print_function from twisted.internet import reactor from twisted.internet.endpoints import connectProtocol, SSL4ClientEndpoint from twisted.internet.protocol import Protocol from twisted.internet.ssl import optionsForClientTLS from hyperframe.frame import SettingsFrame from h2.connection import H2Connection from h2.events import ( ResponseReceived, DataReceived, StreamEnded, StreamReset, SettingsAcknowledged, ) AUTHORITY = u'nghttp2.org' PATH = '/httpbin/' SIZE = 4096 class H2Protocol(Protocol): def __init__(self): self.conn = H2Connection() self.known_proto = None self.request_made = False def connectionMade(self): self.conn.initiate_connection() # This reproduces the error in #396, by changing the header table size. self.conn.update_settings({SettingsFrame.HEADER_TABLE_SIZE: SIZE}) self.transport.write(self.conn.data_to_send()) def dataReceived(self, data): if not self.known_proto: self.known_proto = self.transport.negotiatedProtocol assert self.known_proto == b'h2' events = self.conn.receive_data(data) for event in events: if isinstance(event, ResponseReceived): self.handleResponse(event.headers, event.stream_id) elif isinstance(event, DataReceived): self.handleData(event.data, event.stream_id) elif isinstance(event, StreamEnded): self.endStream(event.stream_id) elif isinstance(event, SettingsAcknowledged): self.settingsAcked(event) elif isinstance(event, StreamReset): reactor.stop() raise RuntimeError("Stream reset: %d" % event.error_code) else: print(event) data = self.conn.data_to_send() if data: self.transport.write(data) def settingsAcked(self, event): # Having received the remote settings change, lets send our request. if not self.request_made: self.sendRequest() def handleResponse(self, response_headers, stream_id): for name, value in response_headers: print("%s: %s" % (name.decode('utf-8'), value.decode('utf-8'))) print("") def handleData(self, data, stream_id): print(data, end='') def endStream(self, stream_id): self.conn.close_connection() self.transport.write(self.conn.data_to_send()) self.transport.loseConnection() reactor.stop() def sendRequest(self): request_headers = [ (':method', 'HEAD'), (':authority', AUTHORITY), (':scheme', 'https'), (':path', PATH), ] self.conn.send_headers(1, request_headers, end_stream=True) self.request_made = True options = optionsForClientTLS( hostname=AUTHORITY, acceptableProtocols=[b'h2'], ) connectProtocol( SSL4ClientEndpoint(reactor, AUTHORITY, 443, options), H2Protocol() ) reactor.run()