1 # nghttp2 - HTTP/2 C Library
3 # Copyright (c) 2013 Tatsuhiro Tsujikawa
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 from libc.stdlib cimport malloc, free
26 from libc.string cimport memcpy, memset
27 from libc.stdint cimport uint8_t, uint16_t, uint32_t, int32_t
31 DEFAULT_HEADER_TABLE_SIZE = cnghttp2.NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
32 DEFLATE_MAX_HEADER_TABLE_SIZE = 4096
34 HD_ENTRY_OVERHEAD = 32
38 def __init__(self, name, namelen, value, valuelen):
40 self.namelen = namelen
42 self.valuelen = valuelen
45 return self.namelen + self.valuelen + HD_ENTRY_OVERHEAD
47 cdef _get_pybytes(uint8_t *b, uint16_t blen):
50 cdef class HDDeflater:
51 '''Performs header compression. The constructor takes
52 |hd_table_bufsize_max| parameter, which limits the usage of header
53 table in the given amount of bytes. This is necessary because the
54 header compressor and decompressor share the same amount of
55 header table and the decompressor decides that number. The
56 compressor may not want to use all header table size because of
57 limited memory availability. In that case, the
58 |hd_table_bufsize_max| can be used to cap the upper limit of table
59 size whatever the header table size is chosen by the decompressor.
60 The default value of |hd_table_bufsize_max| is 4096 bytes.
62 The following example shows how to compress request header sets:
64 import binascii, nghttp2
66 deflater = nghttp2.HDDeflater()
67 res = deflater.deflate([(b'foo', b'bar'),
69 print(binascii.b2a_hex(res))
73 cdef cnghttp2.nghttp2_hd_deflater *_deflater
75 def __cinit__(self, hd_table_bufsize_max = DEFLATE_MAX_HEADER_TABLE_SIZE):
76 rv = cnghttp2.nghttp2_hd_deflate_new(&self._deflater,
79 raise Exception(_strerror(rv))
81 def __dealloc__(self):
82 cnghttp2.nghttp2_hd_deflate_del(self._deflater)
84 def deflate(self, headers):
85 '''Compresses the |headers|. The |headers| must be sequence of tuple
86 of name/value pair, which are sequence of bytes (not unicode
89 This function returns the encoded header block in byte string.
90 An exception will be raised on error.
93 cdef cnghttp2.nghttp2_nv *nva = <cnghttp2.nghttp2_nv*>\
94 malloc(sizeof(cnghttp2.nghttp2_nv)*\
96 cdef cnghttp2.nghttp2_nv *nvap = nva
100 nvap[0].namelen = len(k)
102 nvap[0].valuelen = len(v)
103 nvap[0].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
106 cdef size_t outcap = 0
111 outlen = cnghttp2.nghttp2_hd_deflate_bound(self._deflater,
114 out = <uint8_t*>malloc(outlen)
116 rv = cnghttp2.nghttp2_hd_deflate_hd(self._deflater, out, outlen,
123 raise Exception(_strerror(rv))
134 def change_table_size(self, hd_table_bufsize_max):
135 '''Changes header table size to |hd_table_bufsize_max| byte.
137 An exception will be raised on error.
141 rv = cnghttp2.nghttp2_hd_deflate_change_table_size(self._deflater,
142 hd_table_bufsize_max)
144 raise Exception(_strerror(rv))
146 def get_hd_table(self):
147 '''Returns copy of current dynamic header table.'''
148 cdef size_t length = cnghttp2.nghttp2_hd_deflate_get_num_table_entries(
150 cdef const cnghttp2.nghttp2_nv *nv
152 for i in range(62, length + 1):
153 nv = cnghttp2.nghttp2_hd_deflate_get_table_entry(self._deflater, i)
154 k = _get_pybytes(nv.name, nv.namelen)
155 v = _get_pybytes(nv.value, nv.valuelen)
156 res.append(HDTableEntry(k, nv.namelen, v, nv.valuelen))
159 cdef class HDInflater:
160 '''Performs header decompression.
162 The following example shows how to compress request header sets:
164 data = b'0082c5ad82bd0f000362617a0362757a'
165 inflater = nghttp2.HDInflater()
166 hdrs = inflater.inflate(data)
171 cdef cnghttp2.nghttp2_hd_inflater *_inflater
174 rv = cnghttp2.nghttp2_hd_inflate_new(&self._inflater)
176 raise Exception(_strerror(rv))
178 def __dealloc__(self):
179 cnghttp2.nghttp2_hd_inflate_del(self._inflater)
181 def inflate(self, data):
182 '''Decompresses the compressed header block |data|. The |data| must be
183 byte string (not unicode string).
186 cdef cnghttp2.nghttp2_nv nv
187 cdef int inflate_flags
189 cdef uint8_t *buf = data
190 cdef size_t buflen = len(data)
194 rv = cnghttp2.nghttp2_hd_inflate_hd2(self._inflater, &nv,
198 raise Exception(_strerror(rv))
201 if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_EMIT:
203 res.append((nv.name[:nv.namelen], nv.value[:nv.valuelen]))
204 if inflate_flags & cnghttp2.NGHTTP2_HD_INFLATE_FINAL:
207 cnghttp2.nghttp2_hd_inflate_end_headers(self._inflater)
210 def change_table_size(self, hd_table_bufsize_max):
211 '''Changes header table size to |hd_table_bufsize_max| byte.
213 An exception will be raised on error.
217 rv = cnghttp2.nghttp2_hd_inflate_change_table_size(self._inflater,
218 hd_table_bufsize_max)
220 raise Exception(_strerror(rv))
222 def get_hd_table(self):
223 '''Returns copy of current dynamic header table.'''
224 cdef size_t length = cnghttp2.nghttp2_hd_inflate_get_num_table_entries(
226 cdef const cnghttp2.nghttp2_nv *nv
228 for i in range(62, length + 1):
229 nv = cnghttp2.nghttp2_hd_inflate_get_table_entry(self._inflater, i)
230 k = _get_pybytes(nv.name, nv.namelen)
231 v = _get_pybytes(nv.value, nv.valuelen)
232 res.append(HDTableEntry(k, nv.namelen, v, nv.valuelen))
235 cdef _strerror(int liberror_code):
236 return cnghttp2.nghttp2_strerror(liberror_code).decode('utf-8')
238 def print_hd_table(hdtable):
239 '''Convenient function to print |hdtable| to the standard output. This
240 function does not work if header name/value cannot be decoded using
243 s=N means the entry occupies N bytes in header table.
247 for entry in hdtable:
249 print('[{}] (s={}) {}: {}'\
250 .format(idx, entry.space(),
251 entry.name.decode('utf-8'),
252 entry.value.decode('utf-8')))
264 from urllib.parse import urlparse
268 # body generator flags
273 class _ByteIOWrapper:
275 def __init__(self, b):
278 def generate(self, n):
279 data = self.b.read1(n)
281 return None, DATA_EOF
287 elif isinstance(body, str):
288 return _ByteIOWrapper(io.BytesIO(body.encode('utf-8'))).generate
289 elif isinstance(body, bytes):
290 return _ByteIOWrapper(io.BytesIO(body)).generate
291 elif isinstance(body, io.IOBase):
292 return _ByteIOWrapper(body).generate
294 # assume that callable in the form f(n) returning tuple byte
298 def negotiated_protocol(ssl_obj):
299 protocol = ssl_obj.selected_alpn_protocol()
301 logging.info('alpn, protocol:%s', protocol)
304 protocol = ssl_obj.selected_npn_protocol()
306 logging.info('npn, protocol:%s', protocol)
311 def set_application_protocol(ssl_ctx):
312 app_protos = [cnghttp2.NGHTTP2_PROTO_VERSION_ID.decode('utf-8')]
313 ssl_ctx.set_npn_protocols(app_protos)
315 ssl_ctx.set_alpn_protocols(app_protos)
317 cdef _get_stream_user_data(cnghttp2.nghttp2_session *session,
319 cdef void *stream_user_data
321 stream_user_data = cnghttp2.nghttp2_session_get_stream_user_data\
323 if stream_user_data == NULL:
326 return <object>stream_user_data
328 cdef size_t _make_nva(cnghttp2.nghttp2_nv **nva_ptr, headers):
329 cdef cnghttp2.nghttp2_nv *nva
333 nva = <cnghttp2.nghttp2_nv*>malloc(sizeof(cnghttp2.nghttp2_nv) * nvlen)
334 for i, (k, v) in enumerate(headers):
336 nva[i].namelen = len(k)
338 nva[i].valuelen = len(v)
339 nva[i].flags = cnghttp2.NGHTTP2_NV_FLAG_NONE
345 cdef int server_on_header(cnghttp2.nghttp2_session *session,
346 const cnghttp2.nghttp2_frame *frame,
347 const uint8_t *name, size_t namelen,
348 const uint8_t *value, size_t valuelen,
351 cdef http2 = <_HTTP2SessionCoreBase>user_data
352 logging.debug('server_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
354 handler = _get_stream_user_data(session, frame.hd.stream_id)
355 return on_header(name, namelen, value, valuelen, flags, handler)
357 cdef int client_on_header(cnghttp2.nghttp2_session *session,
358 const cnghttp2.nghttp2_frame *frame,
359 const uint8_t *name, size_t namelen,
360 const uint8_t *value, size_t valuelen,
363 cdef http2 = <_HTTP2SessionCoreBase>user_data
364 logging.debug('client_on_header, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
366 if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
367 handler = _get_stream_user_data(session, frame.hd.stream_id)
368 elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
369 handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
371 return on_header(name, namelen, value, valuelen, flags, handler)
374 cdef int on_header(const uint8_t *name, size_t namelen,
375 const uint8_t *value, size_t valuelen,
382 values = value[:valuelen].split(b'\x00')
383 if key == b':scheme':
384 handler.scheme = values[0]
385 elif key == b':method':
386 handler.method = values[0]
387 elif key == b':authority' or key == b'host':
388 handler.host = values[0]
389 elif key == b':path':
390 handler.path = values[0]
391 elif key == b':status':
392 handler.status = values[0]
395 handler.cookies.extend(values)
398 handler.headers.append((key, v))
402 cdef int server_on_begin_request_headers(cnghttp2.nghttp2_session *session,
403 const cnghttp2.nghttp2_frame *frame,
405 cdef http2 = <_HTTP2SessionCore>user_data
407 handler = http2._make_handler(frame.hd.stream_id)
408 cnghttp2.nghttp2_session_set_stream_user_data(session, frame.hd.stream_id,
413 cdef int server_on_begin_headers(cnghttp2.nghttp2_session *session,
414 const cnghttp2.nghttp2_frame *frame,
416 if frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
417 if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
418 return server_on_begin_request_headers(session, frame, user_data)
422 cdef int server_on_frame_recv(cnghttp2.nghttp2_session *session,
423 const cnghttp2.nghttp2_frame *frame,
425 cdef http2 = <_HTTP2SessionCore>user_data
426 logging.debug('server_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
428 if frame.hd.type == cnghttp2.NGHTTP2_DATA:
429 if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
430 handler = _get_stream_user_data(session, frame.hd.stream_id)
434 handler.on_request_done()
436 sys.stderr.write(traceback.format_exc())
437 return http2._rst_stream(frame.hd.stream_id)
438 elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
439 if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_REQUEST:
440 handler = _get_stream_user_data(session, frame.hd.stream_id)
444 handler.headers.append((b'cookie',
445 b'; '.join(handler.cookies)))
446 handler.cookies = None
449 if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
450 handler.on_request_done()
452 sys.stderr.write(traceback.format_exc())
453 return http2._rst_stream(frame.hd.stream_id)
454 elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
455 if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK):
456 http2._stop_settings_timer()
460 cdef int on_data_chunk_recv(cnghttp2.nghttp2_session *session,
462 int32_t stream_id, const uint8_t *data,
463 size_t length, void *user_data):
464 cdef http2 = <_HTTP2SessionCoreBase>user_data
466 handler = _get_stream_user_data(session, stream_id)
471 handler.on_data(data[:length])
473 sys.stderr.write(traceback.format_exc())
474 return http2._rst_stream(stream_id)
478 cdef int server_on_frame_send(cnghttp2.nghttp2_session *session,
479 const cnghttp2.nghttp2_frame *frame,
481 cdef http2 = <_HTTP2SessionCore>user_data
482 logging.debug('server_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
484 if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
485 # For PUSH_PROMISE, send push response immediately
486 handler = _get_stream_user_data\
487 (session, frame.push_promise.promised_stream_id)
491 http2.send_response(handler)
492 elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
493 if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0:
495 http2._start_settings_timer()
496 elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
497 if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM) and \
498 cnghttp2.nghttp2_session_check_server_session(session):
499 # Send RST_STREAM if remote is not closed yet
500 if cnghttp2.nghttp2_session_get_stream_remote_close(
501 session, frame.hd.stream_id) == 0:
502 http2._rst_stream(frame.hd.stream_id, cnghttp2.NGHTTP2_NO_ERROR)
504 cdef int server_on_frame_not_send(cnghttp2.nghttp2_session *session,
505 const cnghttp2.nghttp2_frame *frame,
508 cdef http2 = <_HTTP2SessionCore>user_data
509 logging.debug('server_on_frame_not_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
511 if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
512 # We have to remove handler here. Without this, it is not
513 # removed until session is terminated.
514 handler = _get_stream_user_data\
515 (session, frame.push_promise.promised_stream_id)
518 http2._remove_handler(handler)
520 cdef int on_stream_close(cnghttp2.nghttp2_session *session,
524 cdef http2 = <_HTTP2SessionCoreBase>user_data
525 logging.debug('on_stream_close, stream_id:%s', stream_id)
527 handler = _get_stream_user_data(session, stream_id)
532 handler.on_close(error_code)
534 sys.stderr.write(traceback.format_exc())
536 http2._remove_handler(handler)
540 cdef ssize_t data_source_read(cnghttp2.nghttp2_session *session,
542 uint8_t *buf, size_t length,
543 uint32_t *data_flags,
544 cnghttp2.nghttp2_data_source *source,
546 cdef http2 = <_HTTP2SessionCoreBase>user_data
547 generator = <object>source.ptr
549 http2.enter_callback()
551 data, flag = generator(length)
553 sys.stderr.write(traceback.format_exc())
554 return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
556 http2.leave_callback()
558 if flag == DATA_DEFERRED:
559 return cnghttp2.NGHTTP2_ERR_DEFERRED
563 memcpy(buf, <uint8_t*>data, nread)
568 data_flags[0] = cnghttp2.NGHTTP2_DATA_FLAG_EOF
569 if cnghttp2.nghttp2_session_check_server_session(session):
570 # Send RST_STREAM if remote is not closed yet
571 if cnghttp2.nghttp2_session_get_stream_remote_close(
572 session, stream_id) == 0:
573 http2._rst_stream(stream_id, cnghttp2.NGHTTP2_NO_ERROR)
574 elif flag != DATA_OK:
575 return cnghttp2.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
579 cdef int client_on_begin_headers(cnghttp2.nghttp2_session *session,
580 const cnghttp2.nghttp2_frame *frame,
582 cdef http2 = <_HTTP2ClientSessionCore>user_data
584 if frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
585 # Generate a temporary handler until the headers are all received
586 push_handler = BaseResponseHandler()
587 http2._add_handler(push_handler, frame.push_promise.promised_stream_id)
588 cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
593 cdef int client_on_frame_recv(cnghttp2.nghttp2_session *session,
594 const cnghttp2.nghttp2_frame *frame,
596 cdef http2 = <_HTTP2ClientSessionCore>user_data
597 logging.debug('client_on_frame_recv, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
599 if frame.hd.type == cnghttp2.NGHTTP2_DATA:
600 if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
601 handler = _get_stream_user_data(session, frame.hd.stream_id)
605 handler.on_response_done()
607 sys.stderr.write(traceback.format_exc())
608 return http2._rst_stream(frame.hd.stream_id)
609 elif frame.hd.type == cnghttp2.NGHTTP2_HEADERS:
610 if frame.headers.cat == cnghttp2.NGHTTP2_HCAT_RESPONSE or frame.headers.cat == cnghttp2.NGHTTP2_HCAT_PUSH_RESPONSE:
611 handler = _get_stream_user_data(session, frame.hd.stream_id)
615 # TODO handle 1xx non-final response
617 handler.headers.append((b'cookie',
618 b'; '.join(handler.cookies)))
619 handler.cookies = None
622 if frame.hd.flags & cnghttp2.NGHTTP2_FLAG_END_STREAM:
623 handler.on_response_done()
625 sys.stderr.write(traceback.format_exc())
626 return http2._rst_stream(frame.hd.stream_id)
627 elif frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
628 if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK):
629 http2._stop_settings_timer()
630 elif frame.hd.type == cnghttp2.NGHTTP2_PUSH_PROMISE:
631 handler = _get_stream_user_data(session, frame.hd.stream_id)
634 # Get the temporary push_handler which now should have all of the header data
635 push_handler = _get_stream_user_data(session, frame.push_promise.promised_stream_id)
638 # Remove the temporary handler
639 http2._remove_handler(push_handler)
640 cnghttp2.nghttp2_session_set_stream_user_data(session, frame.push_promise.promised_stream_id,
644 handler.on_push_promise(push_handler)
646 sys.stderr.write(traceback.format_exc())
647 return http2._rst_stream(frame.hd.stream_id)
651 cdef int client_on_frame_send(cnghttp2.nghttp2_session *session,
652 const cnghttp2.nghttp2_frame *frame,
654 cdef http2 = <_HTTP2ClientSessionCore>user_data
655 logging.debug('client_on_frame_send, type:%s, stream_id:%s', frame.hd.type, frame.hd.stream_id)
657 if frame.hd.type == cnghttp2.NGHTTP2_SETTINGS:
658 if (frame.hd.flags & cnghttp2.NGHTTP2_FLAG_ACK) != 0:
660 http2._start_settings_timer()
662 cdef class _HTTP2SessionCoreBase:
663 cdef cnghttp2.nghttp2_session *session
670 def __cinit__(self, transport, handler_class=None):
672 self.transport = transport
673 self.handler_class = handler_class
674 self.handlers = set()
675 self.settings_timer = None
676 self.inside_callback = False
678 def __dealloc__(self):
679 cnghttp2.nghttp2_session_del(self.session)
681 def data_received(self, data):
684 rv = cnghttp2.nghttp2_session_mem_recv(self.session, data, len(data))
686 raise Exception('nghttp2_session_mem_recv failed: {}'.format\
691 SETTINGS_TIMEOUT = 5.0
694 cdef ssize_t outbuflen
695 cdef const uint8_t *outbuf
698 if self.transport.get_write_buffer_size() > self.OUTBUF_MAX:
700 outbuflen = cnghttp2.nghttp2_session_mem_send(self.session, &outbuf)
704 raise Exception('nghttp2_session_mem_send faild: {}'.format\
705 (_strerror(outbuflen)))
706 self.transport.write(outbuf[:outbuflen])
708 if self.transport.get_write_buffer_size() == 0 and \
709 cnghttp2.nghttp2_session_want_read(self.session) == 0 and \
710 cnghttp2.nghttp2_session_want_write(self.session) == 0:
711 self.transport.close()
713 def resume(self, stream_id):
714 cnghttp2.nghttp2_session_resume_data(self.session, stream_id)
715 if not self.inside_callback:
718 def enter_callback(self):
719 self.inside_callback = True
721 def leave_callback(self):
722 self.inside_callback = False
724 def _make_handler(self, stream_id):
725 logging.debug('_make_handler, stream_id:%s', stream_id)
726 handler = self.handler_class(self, stream_id)
727 self.handlers.add(handler)
730 def _remove_handler(self, handler):
731 logging.debug('_remove_handler, stream_id:%s', handler.stream_id)
732 self.handlers.remove(handler)
734 def _add_handler(self, handler, stream_id):
735 logging.debug('_add_handler, stream_id:%s', stream_id)
736 handler.stream_id = stream_id
738 handler.remote_address = self._get_remote_address()
739 handler.client_certificate = self._get_client_certificate()
740 self.handlers.add(handler)
742 def _rst_stream(self, stream_id,
743 error_code=cnghttp2.NGHTTP2_INTERNAL_ERROR):
746 rv = cnghttp2.nghttp2_submit_rst_stream\
747 (self.session, cnghttp2.NGHTTP2_FLAG_NONE,
748 stream_id, error_code)
752 def _get_remote_address(self):
753 return self.transport.get_extra_info('peername')
755 def _get_client_certificate(self):
756 sock = self.transport.get_extra_info('socket')
758 return sock.getpeercert()
759 except AttributeError:
762 def _start_settings_timer(self):
763 loop = asyncio.get_event_loop()
764 self.settings_timer = loop.call_later(self.SETTINGS_TIMEOUT,
765 self._settings_timeout)
767 def _stop_settings_timer(self):
768 if self.settings_timer:
769 self.settings_timer.cancel()
770 self.settings_timer = None
772 def _settings_timeout(self):
775 logging.debug('_settings_timeout')
777 self.settings_timer = None
779 rv = cnghttp2.nghttp2_session_terminate_session\
780 (self.session, cnghttp2.NGHTTP2_SETTINGS_TIMEOUT)
783 except Exception as err:
784 sys.stderr.write(traceback.format_exc())
785 self.transport.close()
788 def _log_request(self, handler):
789 now = datetime.datetime.now()
790 tv = time.mktime(now.timetuple())
791 datestr = email.utils.formatdate(timeval=tv, localtime=False,
794 method = handler.method.decode('utf-8')
796 method = handler.method
798 path = handler.path.decode('utf-8')
801 logging.info('%s - - [%s] "%s %s HTTP/2" %s - %s', handler.remote_address[0],
802 datestr, method, path, handler.status,
803 'P' if handler.pushed else '-')
806 rv = cnghttp2.nghttp2_session_terminate_session\
807 (self.session, cnghttp2.NGHTTP2_NO_ERROR)
810 except Exception as err:
811 sys.stderr.write(traceback.format_exc())
812 self.transport.close()
815 cdef class _HTTP2SessionCore(_HTTP2SessionCoreBase):
816 def __cinit__(self, *args, **kwargs):
817 cdef cnghttp2.nghttp2_session_callbacks *callbacks
818 cdef cnghttp2.nghttp2_settings_entry iv[2]
821 super(_HTTP2SessionCore, self).__init__(*args, **kwargs)
823 rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks)
826 raise Exception('nghttp2_session_callbacks_new failed: {}'.format\
829 cnghttp2.nghttp2_session_callbacks_set_on_header_callback(
830 callbacks, server_on_header)
831 cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback(
832 callbacks, server_on_begin_headers)
833 cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback(
834 callbacks, server_on_frame_recv)
835 cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback(
836 callbacks, on_stream_close)
837 cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback(
838 callbacks, server_on_frame_send)
839 cnghttp2.nghttp2_session_callbacks_set_on_frame_not_send_callback(
840 callbacks, server_on_frame_not_send)
841 cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
842 callbacks, on_data_chunk_recv)
844 rv = cnghttp2.nghttp2_session_server_new(&self.session, callbacks,
847 cnghttp2.nghttp2_session_callbacks_del(callbacks)
850 raise Exception('nghttp2_session_server_new failed: {}'.format\
853 iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
855 iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
856 iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE
858 rv = cnghttp2.nghttp2_submit_settings(self.session,
859 cnghttp2.NGHTTP2_FLAG_NONE,
860 iv, sizeof(iv) / sizeof(iv[0]))
863 raise Exception('nghttp2_submit_settings failed: {}'.format\
866 def send_response(self, handler):
867 cdef cnghttp2.nghttp2_data_provider prd
868 cdef cnghttp2.nghttp2_data_provider *prd_ptr
869 cdef cnghttp2.nghttp2_nv *nva
873 logging.debug('send_response, stream_id:%s', handler.stream_id)
876 nvlen = _make_nva(&nva, handler.response_headers)
878 if handler.response_body:
879 prd.source.ptr = <void*>handler.response_body
880 prd.read_callback = data_source_read
885 rv = cnghttp2.nghttp2_submit_response(self.session, handler.stream_id,
891 # TODO Ignore return value
892 self._rst_stream(handler.stream_id)
893 raise Exception('nghttp2_submit_response failed: {}'.format\
896 self._log_request(handler)
898 def push(self, handler, promised_handler):
899 cdef cnghttp2.nghttp2_nv *nva
901 cdef int32_t promised_stream_id
903 self.handlers.add(promised_handler)
906 nvlen = _make_nva(&nva, promised_handler.headers)
908 promised_stream_id = cnghttp2.nghttp2_submit_push_promise\
910 cnghttp2.NGHTTP2_FLAG_NONE,
913 <void*>promised_handler)
914 if promised_stream_id < 0:
915 raise Exception('nghttp2_submit_push_promise failed: {}'.format\
916 (_strerror(promised_stream_id)))
918 promised_handler.stream_id = promised_stream_id
920 logging.debug('push, stream_id:%s', promised_stream_id)
922 return promised_handler
924 def connection_lost(self):
925 self._stop_settings_timer()
927 for handler in self.handlers:
928 handler.on_close(cnghttp2.NGHTTP2_INTERNAL_ERROR)
929 self.handlers = set()
931 cdef class _HTTP2ClientSessionCore(_HTTP2SessionCoreBase):
932 def __cinit__(self, *args, **kwargs):
933 cdef cnghttp2.nghttp2_session_callbacks *callbacks
934 cdef cnghttp2.nghttp2_settings_entry iv[2]
937 super(_HTTP2ClientSessionCore, self).__init__(*args, **kwargs)
939 rv = cnghttp2.nghttp2_session_callbacks_new(&callbacks)
942 raise Exception('nghttp2_session_callbacks_new failed: {}'.format\
945 cnghttp2.nghttp2_session_callbacks_set_on_header_callback(
946 callbacks, client_on_header)
947 cnghttp2.nghttp2_session_callbacks_set_on_begin_headers_callback(
948 callbacks, client_on_begin_headers)
949 cnghttp2.nghttp2_session_callbacks_set_on_frame_recv_callback(
950 callbacks, client_on_frame_recv)
951 cnghttp2.nghttp2_session_callbacks_set_on_stream_close_callback(
952 callbacks, on_stream_close)
953 cnghttp2.nghttp2_session_callbacks_set_on_frame_send_callback(
954 callbacks, client_on_frame_send)
955 cnghttp2.nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
956 callbacks, on_data_chunk_recv)
958 rv = cnghttp2.nghttp2_session_client_new(&self.session, callbacks,
961 cnghttp2.nghttp2_session_callbacks_del(callbacks)
964 raise Exception('nghttp2_session_client_new failed: {}'.format\
967 iv[0].settings_id = cnghttp2.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
969 iv[1].settings_id = cnghttp2.NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE
970 iv[1].value = cnghttp2.NGHTTP2_INITIAL_WINDOW_SIZE
972 rv = cnghttp2.nghttp2_submit_settings(self.session,
973 cnghttp2.NGHTTP2_FLAG_NONE,
974 iv, sizeof(iv) / sizeof(iv[0]))
977 raise Exception('nghttp2_submit_settings failed: {}'.format\
980 def send_request(self, method, scheme, host, path, headers, body, handler):
981 cdef cnghttp2.nghttp2_data_provider prd
982 cdef cnghttp2.nghttp2_data_provider *prd_ptr
983 cdef cnghttp2.nghttp2_priority_spec *pri_ptr
984 cdef cnghttp2.nghttp2_nv *nva
986 cdef int32_t stream_id
988 body = wrap_body(body)
990 custom_headers = _encode_headers(headers)
992 (b':method', method.encode('utf-8')),
993 (b':scheme', scheme.encode('utf-8')),
994 (b':authority', host.encode('utf-8')),
995 (b':path', path.encode('utf-8'))
997 headers.extend(custom_headers)
1000 nvlen = _make_nva(&nva, headers)
1003 prd.source.ptr = <void*>body
1004 prd.read_callback = data_source_read
1009 # TODO: Enable priorities
1012 stream_id = cnghttp2.nghttp2_submit_request\
1013 (self.session, pri_ptr,
1014 nva, nvlen, prd_ptr,
1019 raise Exception('nghttp2_submit_request failed: {}'.format\
1020 (_strerror(stream_id)))
1022 logging.debug('request, stream_id:%s', stream_id)
1024 self._add_handler(handler, stream_id)
1025 cnghttp2.nghttp2_session_set_stream_user_data(self.session, stream_id,
1030 def push(self, push_promise, handler):
1032 # push_promise accepted, fill in the handler with the stored
1033 # headers from the push_promise
1034 handler.status = push_promise.status
1035 handler.scheme = push_promise.scheme
1036 handler.method = push_promise.method
1037 handler.host = push_promise.host
1038 handler.path = push_promise.path
1039 handler.cookies = push_promise.cookies
1040 handler.stream_id = push_promise.stream_id
1041 handler.http2 = self
1042 handler.pushed = True
1044 self._add_handler(handler, handler.stream_id)
1046 cnghttp2.nghttp2_session_set_stream_user_data(self.session, handler.stream_id,
1049 # push_promise rejected, reset the stream
1050 self._rst_stream(push_promise.stream_id,
1051 error_code=cnghttp2.NGHTTP2_NO_ERROR)
1055 class BaseRequestHandler:
1057 """HTTP/2 request (stream) handler base class.
1059 The class is used to handle the HTTP/2 stream. By default, it does
1060 not nothing. It must be subclassed to handle each event callback
1063 The first callback method invoked is on_headers(). It is called
1064 when HEADERS frame, which includes request header fields, is
1067 If request has request body, on_data(data) is invoked for each
1068 chunk of received data.
1070 When whole request is received, on_request_done() is invoked.
1072 When stream is closed, on_close(error_code) is called.
1074 The application can send response using send_response() method. It
1075 can be used in on_headers(), on_data() or on_request_done().
1077 The application can push resource using push() method. It must be
1078 used before send_response() call.
1080 The following instance variables are available:
1083 Contains a tuple of the form (host, port) referring to the client's
1087 May contain the client certifcate in its non-binary form
1090 Stream ID of this stream
1093 Scheme of the request URI. This is a value of :scheme header field.
1096 Method of this stream. This is a value of :method header field.
1099 This is a value of :authority or host header field.
1102 This is a value of :path header field.
1105 Request header fields
1109 def __init__(self, http2, stream_id):
1112 # Stream ID. For promised stream, it is initially -1.
1113 self.stream_id = stream_id
1115 # address of the client
1116 self.remote_address = self.http2._get_remote_address()
1117 # certificate of the client
1118 self._client_certificate = self.http2._get_client_certificate()
1119 # :scheme header field in request
1121 # :method header field in request
1123 # :authority or host header field in request
1125 # :path header field in request
1129 # True if this is a handler for pushed resource
1133 def client_address(self):
1134 return self.remote_address
1137 def client_certificate(self):
1138 return self._client_certificate
1140 def on_headers(self):
1142 '''Called when request HEADERS is arrived.
1147 def on_data(self, data):
1149 '''Called when a chunk of request body is arrived. This method
1150 will be called multiple times until all data are received.
1155 def on_request_done(self):
1157 '''Called when whole request was received
1162 def on_close(self, error_code):
1164 '''Called when stream is about to close.
1169 def send_response(self, status=200, headers=None, body=None):
1171 '''Send response. The status is HTTP status code. The headers is
1172 additional response headers. The :status header field is
1173 appended by the library. The body is the response body. It
1174 could be None if response body is empty. Or it must be
1175 instance of either str, bytes, io.IOBase or callable,
1176 called body generator, which takes one parameter,
1177 size. The body generator generates response body. It can
1178 pause generation of response so that it can wait for slow
1179 backend data generation. When invoked, it should return
1180 tuple, byte string and flag. The flag is either DATA_OK,
1181 DATA_EOF and DATA_DEFERRED. For non-empty byte string and
1182 it is not the last chunk of response, DATA_OK is returned
1183 as flag. If this is the last chunk of the response (byte
1184 string is possibly None), DATA_EOF must be returned as
1185 flag. If there is no data available right now, but
1186 additional data are anticipated, return tuple (None,
1187 DATA_DEFERRD). When data arrived, call resume() and
1188 restart response body transmission.
1190 Only the body generator can pause response body
1191 generation; instance of io.IOBase must not block.
1193 If instance of str is specified as body, it is encoded
1196 The headers is a list of tuple of the form (name,
1197 value). The name and value can be either unicode string or
1200 On error, exception will be thrown.
1203 if self.status is not None:
1204 raise Exception('response has already been sent')
1207 raise Exception('status must not be empty')
1209 body = wrap_body(body)
1211 self._set_response_prop(status, headers, body)
1212 self.http2.send_response(self)
1214 def push(self, path, method='GET', request_headers=None,
1215 status=200, headers=None, body=None):
1217 '''Push a resource. The path is a path portion of request URI
1219 resource. The method is a method to access this
1220 resource. The request_headers is additional request
1221 headers to access this resource. The :scheme, :method,
1222 :authority and :path are appended by the library. The
1223 :scheme and :authority are inherited from the request (not
1224 request_headers parameter).
1226 The status is HTTP status code. The headers is additional
1227 response headers. The :status header field is appended by
1228 the library. The body is the response body. It has the
1229 same semantics of body parameter of send_response().
1231 The headers and request_headers are a list of tuple of the
1232 form (name, value). The name and value can be either
1233 unicode string or byte string.
1235 On error, exception will be thrown.
1239 raise Exception('status must not be empty')
1242 raise Exception('method must not be empty')
1245 raise Exception('path must not be empty')
1247 body = wrap_body(body)
1249 promised_handler = self.http2._make_handler(-1)
1250 promised_handler.pushed = True
1251 promised_handler.scheme = self.scheme
1252 promised_handler.method = method.encode('utf-8')
1253 promised_handler.host = self.host
1254 promised_handler.path = path.encode('utf-8')
1255 promised_handler._set_response_prop(status, headers, body)
1258 (b':method', promised_handler.method),
1259 (b':scheme', promised_handler.scheme),
1260 (b':authority', promised_handler.host),
1261 (b':path', promised_handler.path)
1263 headers.extend(_encode_headers(request_headers))
1265 promised_handler.headers = headers
1267 return self.http2.push(self, promised_handler)
1269 def _set_response_prop(self, status, headers, body):
1270 self.status = status
1275 self.response_headers = [(b':status', str(status).encode('utf-8'))]
1276 self.response_headers.extend(_encode_headers(headers))
1278 self.response_body = body
1281 self.http2.resume(self.stream_id)
1283 def _encode_headers(headers):
1286 return [(k if isinstance(k, bytes) else k.encode('utf-8'),
1287 v if isinstance(v, bytes) else v.encode('utf-8')) \
1288 for k, v in headers]
1290 class _HTTP2Session(asyncio.Protocol):
1292 def __init__(self, RequestHandlerClass):
1293 asyncio.Protocol.__init__(self)
1294 self.RequestHandlerClass = RequestHandlerClass
1297 def connection_made(self, transport):
1298 address = transport.get_extra_info('peername')
1299 logging.info('connection_made, address:%s, port:%s', address[0], address[1])
1301 self.transport = transport
1302 sock = self.transport.get_extra_info('socket')
1304 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
1305 except OSError as e:
1306 logging.info('failed to set tcp-nodelay: %s', str(e))
1307 ssl_ctx = self.transport.get_extra_info('sslcontext')
1309 ssl_obj = self.transport.get_extra_info('ssl_object')
1310 protocol = negotiated_protocol(ssl_obj)
1311 if protocol is None or protocol.encode('utf-8') != \
1312 cnghttp2.NGHTTP2_PROTO_VERSION_ID:
1313 self.transport.abort()
1316 self.http2 = _HTTP2SessionCore\
1318 self.RequestHandlerClass)
1319 except Exception as err:
1320 sys.stderr.write(traceback.format_exc())
1321 self.transport.abort()
1325 def connection_lost(self, exc):
1326 logging.info('connection_lost')
1328 self.http2.connection_lost()
1331 def data_received(self, data):
1333 self.http2.data_received(data)
1334 except Exception as err:
1335 sys.stderr.write(traceback.format_exc())
1336 self.transport.close()
1339 def resume_writing(self):
1341 self.http2.send_data()
1342 except Exception as err:
1343 sys.stderr.write(traceback.format_exc())
1344 self.transport.close()
1351 This class builds on top of the asyncio event loop. On
1352 construction, RequestHandlerClass must be given, which must be a
1353 subclass of BaseRequestHandler class.
1356 def __init__(self, address, RequestHandlerClass, ssl=None):
1358 '''address is a tuple of the listening address and port (e.g.,
1359 ('127.0.0.1', 8080)). RequestHandlerClass must be a subclass
1360 of BaseRequestHandler class to handle a HTTP/2 stream. The
1361 ssl can be ssl.SSLContext instance. If it is not None, the
1362 resulting server is SSL/TLS capable.
1365 def session_factory():
1366 return _HTTP2Session(RequestHandlerClass)
1368 self.loop = asyncio.get_event_loop()
1371 set_application_protocol(ssl)
1373 coro = self.loop.create_server(session_factory,
1374 host=address[0], port=address[1],
1376 self.server = self.loop.run_until_complete(coro)
1377 logging.info('listen, address:%s, port:%s', address[0], address[1])
1379 def serve_forever(self):
1381 self.loop.run_forever()
1388 class BaseResponseHandler:
1390 """HTTP/2 response (stream) handler base class.
1392 The class is used to handle the HTTP/2 stream. By default, it does
1393 not nothing. It must be subclassed to handle each event callback
1396 The first callback method invoked is on_headers(). It is called
1397 when HEADERS frame, which includes response header fields, is
1400 If response has a body, on_data(data) is invoked for each
1401 chunk of received data.
1403 When whole response is received, on_response_done() is invoked.
1405 When stream is closed or underlying connection is lost,
1406 on_close(error_code) is called.
1408 The application can send follow up requests using HTTP2Client.send_request() method.
1410 The application can handle push resource using on_push_promise() method.
1412 The following instance variables are available:
1415 Contains a tuple of the form (host, port) referring to the server's
1419 Stream ID of this stream
1422 Scheme of the request URI. This is a value of :scheme header field.
1425 Method of this stream. This is a value of :method header field.
1428 This is a value of :authority or host header field.
1431 This is a value of :path header field.
1434 Response header fields. There is a special exception. If this
1435 object is passed to push_promise(), this instance variable contains
1436 pushed request header fields.
1440 def __init__(self, http2=None, stream_id=-1):
1443 # Stream ID. For promised stream, it is initially -1.
1444 self.stream_id = stream_id
1446 # address of the server
1447 self.remote_address = None
1448 # :scheme header field in request
1450 # :method header field in request
1452 # :authority or host header field in request
1454 # :path header field in request
1458 # True if this is a handler for pushed resource
1462 def server_address(self):
1463 return self.remote_address
1465 def on_headers(self):
1467 '''Called when response HEADERS is arrived.
1472 def on_data(self, data):
1474 '''Called when a chunk of response body is arrived. This method
1475 will be called multiple times until all data are received.
1480 def on_response_done(self):
1482 '''Called when whole response was received
1487 def on_close(self, error_code):
1489 '''Called when stream is about to close.
1494 def on_push_promise(self, push_promise):
1496 '''Called when a push is promised. Default behavior is to
1497 cancel the push. If application overrides this method,
1498 it should call either accept_push or reject_push.
1501 self.reject_push(push_promise)
1503 def reject_push(self, push_promise):
1505 '''Convenience method equivalent to calling accept_push
1509 self.http2.push(push_promise, None)
1511 def accept_push(self, push_promise, handler=None):
1513 '''Accept a push_promise and provider a handler for the
1514 new stream. If a falsy value is supplied for the handler,
1515 the push is rejected.
1518 self.http2.push(push_promise, handler)
1521 self.http2.resume(self.stream_id)
1523 class _HTTP2ClientSession(asyncio.Protocol):
1525 def __init__(self, client):
1526 asyncio.Protocol.__init__(self)
1529 self.client = client
1531 def connection_made(self, transport):
1532 address = transport.get_extra_info('peername')
1533 logging.info('connection_made, address:%s, port:%s', address[0], address[1])
1535 self.transport = transport
1536 sock = self.transport.get_extra_info('socket')
1537 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
1538 ssl_ctx = self.transport.get_extra_info('sslcontext')
1540 ssl_obj = self.transport.get_extra_info('ssl_object')
1541 protocol = negotiated_protocol(ssl_obj)
1542 if protocol is None or protocol.encode('utf-8') != \
1543 cnghttp2.NGHTTP2_PROTO_VERSION_ID:
1544 self.transport.abort()
1546 self.http2 = _HTTP2ClientSessionCore(self.transport)
1548 # Clear pending requests
1549 send_pending = self.pending
1551 for method,scheme,host,path,headers,body,handler in send_pending:
1552 self.send_request(method=method, scheme=scheme, host=host, path=path,\
1553 headers=headers, body=body, handler=handler)
1554 self.http2.send_data()
1556 def connection_lost(self, exc):
1557 logging.info('connection_lost')
1562 def data_received(self, data):
1564 self.http2.data_received(data)
1565 except Exception as err:
1566 sys.stderr.write(traceback.format_exc())
1567 self.transport.close()
1570 def resume_writing(self):
1572 self.http2.send_data()
1573 except Exception as err:
1574 sys.stderr.write(traceback.format_exc())
1575 self.transport.close()
1578 def send_request(self, method, scheme, host, path, headers, body, handler):
1580 # Waiting until connection established
1582 self.pending.append([method, scheme, host, path, headers, body, handler])
1585 self.http2.send_request(method=method, scheme=scheme, host=host, path=path,\
1586 headers=headers, body=body, handler=handler)
1587 self.http2.send_data()
1588 except Exception as err:
1589 sys.stderr.write(traceback.format_exc())
1590 self.transport.close()
1602 This class builds on top of the asyncio event loop.
1605 def __init__(self, address, loop=None, ssl=None):
1607 '''address is a tuple of the connect address and port (e.g.,
1608 ('127.0.0.1', 8080)). The ssl can be ssl.SSLContext instance.
1609 If it is not None, the resulting client is SSL/TLS capable.
1612 self.address = address
1613 self.session = _HTTP2ClientSession(self)
1614 def session_factory():
1618 set_application_protocol(ssl)
1622 self.loop = asyncio.get_event_loop()
1624 coro = self.loop.create_connection(session_factory,
1625 host=address[0], port=address[1],
1629 self.scheme = 'https'
1631 self.scheme = 'http'
1633 self.transport,_ = self.loop.run_until_complete(coro)
1634 logging.info('connect, address:%s, port:%s', self.address[0], self.address[1])
1641 self.session.close()
1643 def send_request(self, method='GET', url='/', headers=None, body=None, handler=None):
1645 scheme = url.scheme if url.scheme else self.scheme
1646 host = url.netloc if url.netloc else self.address[0]+':'+str(self.address[1])
1649 path += ';'+url.params
1651 path += '?'+url.query
1653 path += '#'+url.fragment
1655 self.session.send_request(method=method, scheme=scheme, host=host, path=path,\
1656 headers=headers, body=body, handler=handler)