@@ -26,55 +26,80 @@ class HTTP2Protocol(object):
26
26
)
27
27
28
28
# "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
29
- CLIENT_CONNECTION_PREFACE = '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'
29
+ CLIENT_CONNECTION_PREFACE = \
30
+ '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a' .decode ('hex' )
30
31
31
32
ALPN_PROTO_H2 = 'h2'
32
33
33
- def __init__ (self , tcp_client ):
34
- self .tcp_client = tcp_client
34
+ def __init__ (self , tcp_handler , is_server = False ):
35
+ self .tcp_handler = tcp_handler
36
+ self .is_server = is_server
35
37
36
38
self .http2_settings = frame .HTTP2_DEFAULT_SETTINGS .copy ()
37
39
self .current_stream_id = None
38
40
self .encoder = Encoder ()
39
41
self .decoder = Decoder ()
42
+ self .connection_preface_performed = False
40
43
41
44
def check_alpn (self ):
42
- alp = self .tcp_client .get_alpn_proto_negotiated ()
45
+ alp = self .tcp_handler .get_alpn_proto_negotiated ()
43
46
if alp != self .ALPN_PROTO_H2 :
44
47
raise NotImplementedError (
45
48
"HTTP2Protocol can not handle unknown ALP: %s" % alp )
46
49
return True
47
50
48
- def perform_connection_preface (self ):
49
- self .tcp_client .wfile .write (
50
- bytes (self .CLIENT_CONNECTION_PREFACE .decode ('hex' )))
51
- self .send_frame (frame .SettingsFrame (state = self ))
52
-
53
- # read server settings frame
54
- frm = frame .Frame .from_file (self .tcp_client .rfile , self )
51
+ def _receive_settings (self ):
52
+ frm = frame .Frame .from_file (self .tcp_handler .rfile , self )
55
53
assert isinstance (frm , frame .SettingsFrame )
56
54
self ._apply_settings (frm .settings )
57
55
58
- # read setting ACK frame
56
+ def _read_settings_ack ( self ):
59
57
settings_ack_frame = self .read_frame ()
60
58
assert isinstance (settings_ack_frame , frame .SettingsFrame )
61
59
assert settings_ack_frame .flags & frame .Frame .FLAG_ACK
62
60
assert len (settings_ack_frame .settings ) == 0
63
61
62
+ def perform_server_connection_preface (self , force = False ):
63
+ if force or not self .connection_preface_performed :
64
+ self .connection_preface_performed = True
65
+
66
+ magic_length = len (self .CLIENT_CONNECTION_PREFACE )
67
+ magic = self .tcp_handler .rfile .safe_read (magic_length )
68
+ assert magic == self .CLIENT_CONNECTION_PREFACE
69
+
70
+ self .send_frame (frame .SettingsFrame (state = self ))
71
+ self ._receive_settings ()
72
+ self ._read_settings_ack ()
73
+
74
+ def perform_client_connection_preface (self , force = False ):
75
+ if force or not self .connection_preface_performed :
76
+ self .connection_preface_performed = True
77
+
78
+ self .tcp_handler .wfile .write (self .CLIENT_CONNECTION_PREFACE )
79
+
80
+ self .send_frame (frame .SettingsFrame (state = self ))
81
+ self ._receive_settings ()
82
+ self ._read_settings_ack ()
83
+
64
84
def next_stream_id (self ):
65
85
if self .current_stream_id is None :
66
- self .current_stream_id = 1
86
+ if self .is_server :
87
+ # servers must use even stream ids
88
+ self .current_stream_id = 2
89
+ else :
90
+ # clients must use odd stream ids
91
+ self .current_stream_id = 1
67
92
else :
68
93
self .current_stream_id += 2
69
94
return self .current_stream_id
70
95
71
96
def send_frame (self , frame ):
72
97
raw_bytes = frame .to_bytes ()
73
- self .tcp_client .wfile .write (raw_bytes )
74
- self .tcp_client .wfile .flush ()
98
+ self .tcp_handler .wfile .write (raw_bytes )
99
+ self .tcp_handler .wfile .flush ()
75
100
76
101
def read_frame (self ):
77
- frm = frame .Frame .from_file (self .tcp_client .rfile , self )
102
+ frm = frame .Frame .from_file (self .tcp_handler .rfile , self )
78
103
if isinstance (frm , frame .SettingsFrame ):
79
104
self ._apply_settings (frm .settings )
80
105
@@ -127,10 +152,13 @@ def create_request(self, method, path, headers=None, body=None):
127
152
if headers is None :
128
153
headers = []
129
154
155
+ authority = self .tcp_handler .sni if self .tcp_handler .sni else self .tcp_handler .address .host
130
156
headers = [
131
157
(b':method' , bytes (method )),
132
158
(b':path' , bytes (path )),
133
- (b':scheme' , b'https' )] + headers
159
+ (b':scheme' , b'https' ),
160
+ (b':authority' , authority ),
161
+ ] + headers
134
162
135
163
stream_id = self .next_stream_id ()
136
164
@@ -139,25 +167,50 @@ def create_request(self, method, path, headers=None, body=None):
139
167
self ._create_body (body , stream_id )))
140
168
141
169
def read_response (self ):
170
+ headers , body = self ._receive_transmission ()
171
+ return headers [':status' ], headers , body
172
+
173
+ def read_request (self ):
174
+ return self ._receive_transmission ()
175
+
176
+ def _receive_transmission (self ):
177
+ body_expected = True
178
+
142
179
header_block_fragment = b''
143
180
body = b''
144
181
145
182
while True :
146
183
frm = self .read_frame ()
147
- if isinstance (frm , frame .HeadersFrame ):
184
+ if isinstance (frm , frame .HeadersFrame )\
185
+ or isinstance (frm , frame .ContinuationFrame ):
148
186
header_block_fragment += frm .header_block_fragment
149
- if frm .flags | frame .Frame .FLAG_END_HEADERS :
187
+ if frm .flags & frame .Frame .FLAG_END_HEADERS :
188
+ if frm .flags & frame .Frame .FLAG_END_STREAM :
189
+ body_expected = False
150
190
break
151
191
152
- while True :
192
+ while body_expected :
153
193
frm = self .read_frame ()
154
194
if isinstance (frm , frame .DataFrame ):
155
195
body += frm .payload
156
- if frm .flags | frame .Frame .FLAG_END_STREAM :
196
+ if frm .flags & frame .Frame .FLAG_END_STREAM :
157
197
break
198
+ # TODO: implement window update & flow
158
199
159
200
headers = {}
160
201
for header , value in self .decoder .decode (header_block_fragment ):
161
202
headers [header ] = value
162
203
163
- return headers [':status' ], headers , body
204
+ return headers , body
205
+
206
+ def create_response (self , code , headers = None , body = None ):
207
+ if headers is None :
208
+ headers = []
209
+
210
+ headers = [(b':status' , bytes (str (code )))] + headers
211
+
212
+ stream_id = self .next_stream_id ()
213
+
214
+ return list (itertools .chain (
215
+ self ._create_headers (headers , stream_id , end_stream = (body is None )),
216
+ self ._create_body (body , stream_id )))
0 commit comments