%PDF- %PDF-
Direktori : /backups/router/usr/local/lib/python3.11/site-packages/pylsqpack/ |
Current File : //backups/router/usr/local/lib/python3.11/site-packages/pylsqpack/binding.c |
#define PY_SSIZE_T_CLEAN #include <Python.h> #include "lsqpack.h" #define MODULE_NAME "pylsqpack._binding" #define DEC_BUF_SZ 4096 #define ENC_BUF_SZ 4096 #define HDR_BUF_SZ 4096 #define PREFIX_MAX_SIZE 16 static PyObject *DecompressionFailed; static PyObject *DecoderStreamError; static PyObject *DecoderType; static PyObject *EncoderStreamError; static PyObject *EncoderType; static PyObject *StreamBlocked; struct header_block { STAILQ_ENTRY(header_block) entries; int blocked:1; unsigned char *data; size_t data_len; const unsigned char *data_ptr; struct lsqpack_header_list *hlist; uint64_t stream_id; }; static struct header_block *header_block_new(size_t stream_id, const unsigned char *data, size_t data_len) { struct header_block *hblock = malloc(sizeof(struct header_block)); memset(hblock, 0, sizeof(*hblock)); hblock->data = malloc(data_len); hblock->data_len = data_len; hblock->data_ptr = hblock->data; memcpy(hblock->data, data, data_len); hblock->stream_id = stream_id; return hblock; } static void header_block_free(struct header_block *hblock) { free(hblock->data); hblock->data = 0; hblock->data_ptr = 0; if (hblock->hlist) { lsqpack_dec_destroy_header_list(hblock->hlist); hblock->hlist = 0; } free(hblock); } static PyObject *hlist_to_headers(struct lsqpack_header_list *hlist) { PyObject *list, *tuple, *name, *value; struct lsqpack_header *header; list = PyList_New(hlist->qhl_count); for (size_t i = 0; i < hlist->qhl_count; ++i) { header = hlist->qhl_headers[i]; name = PyBytes_FromStringAndSize(header->qh_name, header->qh_name_len); value = PyBytes_FromStringAndSize(header->qh_value, header->qh_value_len); tuple = PyTuple_Pack(2, name, value); Py_DECREF(name); Py_DECREF(value); PyList_SetItem(list, i, tuple); } return list; } static void header_unblocked(void *opaque) { struct header_block *hblock = opaque; hblock->blocked = 0; } // DECODER typedef struct { PyObject_HEAD struct lsqpack_dec dec; unsigned char dec_buf[DEC_BUF_SZ]; STAILQ_HEAD(, header_block) pending_blocks; } DecoderObject; static int Decoder_init(DecoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"max_table_capacity", "blocked_streams", NULL}; unsigned max_table_capacity, blocked_streams; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "II", kwlist, &max_table_capacity, &blocked_streams)) return -1; lsqpack_dec_init(&self->dec, NULL, max_table_capacity, blocked_streams, header_unblocked); STAILQ_INIT(&self->pending_blocks); return 0; } static void Decoder_dealloc(DecoderObject *self) { struct header_block *hblock; lsqpack_dec_cleanup(&self->dec); while (!STAILQ_EMPTY(&self->pending_blocks)) { hblock = STAILQ_FIRST(&self->pending_blocks); STAILQ_REMOVE_HEAD(&self->pending_blocks, entries); header_block_free(hblock); } PyTypeObject *tp = Py_TYPE(self); freefunc free = PyType_GetSlot(tp, Py_tp_free); free(self); Py_DECREF(tp); } static PyObject* Decoder_feed_encoder(DecoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"data", NULL}; const unsigned char *data; Py_ssize_t data_len; PyObject *list, *value; struct header_block *hblock; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y#", kwlist, &data, &data_len)) return NULL; if (lsqpack_dec_enc_in(&self->dec, data, data_len) < 0) { PyErr_SetString(EncoderStreamError, "lsqpack_dec_enc_in failed"); return NULL; } list = PyList_New(0); STAILQ_FOREACH(hblock, &self->pending_blocks, entries) { if (!hblock->blocked) { value = PyLong_FromUnsignedLongLong(hblock->stream_id); PyList_Append(list, value); Py_DECREF(value); } } return list; } PyDoc_STRVAR(Decoder_feed_encoder__doc__, "feed_encoder(data: bytes) -> List[int]\n\n" "Feed data from the encoder stream.\n\n" "If processing the data unblocked any streams, their IDs are returned, " "and :meth:`resume_header()` must be called for each stream ID.\n\n" "If the data cannot be processed, :class:`EncoderStreamError` is raised.\n\n" ":param data: the encoder stream data\n"); static PyObject* Decoder_feed_header(DecoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"stream_id", "data", NULL}; uint64_t stream_id; const unsigned char *data; Py_ssize_t data_len; PyObject *control, *headers, *tuple; size_t dec_len = DEC_BUF_SZ; enum lsqpack_read_header_status status; struct header_block *hblock; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ky#", kwlist, &stream_id, &data, &data_len)) return NULL; // check there is no header block for the stream STAILQ_FOREACH(hblock, &self->pending_blocks, entries) { if (hblock->stream_id == stream_id) { PyErr_Format(PyExc_ValueError, "a header block for stream %d already exists", stream_id); return NULL; } } hblock = header_block_new(stream_id, data, data_len); status = lsqpack_dec_header_in( &self->dec, hblock, stream_id, hblock->data_len, &hblock->data_ptr, hblock->data_len, &hblock->hlist, self->dec_buf, &dec_len ); if (status == LQRHS_BLOCKED || status == LQRHS_NEED) { hblock->blocked = 1; STAILQ_INSERT_TAIL(&self->pending_blocks, hblock, entries); PyErr_Format(StreamBlocked, "stream %d is blocked", stream_id); return NULL; } else if (status != LQRHS_DONE) { PyErr_Format(DecompressionFailed, "lsqpack_dec_header_in for stream %d failed", stream_id); header_block_free(hblock); return NULL; } control = PyBytes_FromStringAndSize((const char*)self->dec_buf, dec_len); headers = hlist_to_headers(hblock->hlist); header_block_free(hblock); tuple = PyTuple_Pack(2, control, headers); Py_DECREF(control); Py_DECREF(headers); return tuple; } PyDoc_STRVAR(Decoder_feed_header__doc__, "feed_header(stream_id: int, data: bytes) -> Tuple[bytes, List[Tuple[bytes, bytes]]]\n\n" "Decode a header block and return control data and headers.\n\n" "If the stream is blocked, :class:`StreamBlocked` is raised.\n\n" "If the data cannot be processed, :class:`DecompressionFailed` is raised.\n\n" ":param stream_id: the ID of the stream\n" ":param data: the header block data\n"); static PyObject* Decoder_resume_header(DecoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"stream_id", NULL}; uint64_t stream_id; PyObject *control, *headers, *tuple; size_t dec_len = DEC_BUF_SZ; enum lsqpack_read_header_status status; struct header_block *hblock; int found = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "K", kwlist, &stream_id)) return NULL; // find the header block for the stream STAILQ_FOREACH(hblock, &self->pending_blocks, entries) { if (hblock->stream_id == stream_id) { found = 1; break; } } if (!found) { PyErr_Format(PyExc_ValueError, "no pending header block for stream %d", stream_id); return NULL; } if (hblock->blocked) { status = LQRHS_BLOCKED; } else { status = lsqpack_dec_header_read( &self->dec, hblock, &hblock->data_ptr, hblock->data_len - (hblock->data_ptr - hblock->data), &hblock->hlist, self->dec_buf, &dec_len ); } if (status == LQRHS_BLOCKED || status == LQRHS_NEED) { hblock->blocked = 1; PyErr_Format(StreamBlocked, "stream %d is blocked", stream_id); return NULL; } else if (status != LQRHS_DONE) { PyErr_Format(DecompressionFailed, "lsqpack_dec_header_read for stream %d failed (%d)", stream_id, status); STAILQ_REMOVE(&self->pending_blocks, hblock, header_block, entries); header_block_free(hblock); return NULL; } control = PyBytes_FromStringAndSize((const char*)self->dec_buf, dec_len); headers = hlist_to_headers(hblock->hlist); STAILQ_REMOVE(&self->pending_blocks, hblock, header_block, entries); header_block_free(hblock); tuple = PyTuple_Pack(2, control, headers); Py_DECREF(control); Py_DECREF(headers); return tuple; } PyDoc_STRVAR(Decoder_resume_header__doc__, "resume_header(stream_id: int) -> Tuple[bytes, List[Tuple[bytes, bytes]]]\n\n" "Continue decoding a header block and return control data and headers.\n\n" "This method should be called only when :meth:`feed_encoder` indicates " "that a stream has become unblocked\n\n" ":param stream_id: the ID of the stream\n"); static PyMethodDef Decoder_methods[] = { {"feed_encoder", (PyCFunction)Decoder_feed_encoder, METH_VARARGS | METH_KEYWORDS, Decoder_feed_encoder__doc__}, {"feed_header", (PyCFunction)Decoder_feed_header, METH_VARARGS | METH_KEYWORDS, Decoder_feed_header__doc__}, {"resume_header", (PyCFunction)Decoder_resume_header, METH_VARARGS | METH_KEYWORDS, Decoder_resume_header__doc__}, {NULL} }; PyDoc_STRVAR(Decoder__doc__, "Decoder(max_table_capacity: int, blocked_streams: int)\n\n" "QPACK decoder.\n\n" ":param max_table_capacity: the maximum size in bytes of the dynamic table\n" ":param blocked_streams: the maximum number of streams that could be blocked\n"); static PyType_Slot DecoderType_slots[] = { {Py_tp_dealloc, Decoder_dealloc}, {Py_tp_methods, Decoder_methods}, {Py_tp_doc, Decoder__doc__}, {Py_tp_init, Decoder_init}, {0, 0}, }; static PyType_Spec DecoderType_spec = { MODULE_NAME ".Decoder", sizeof(DecoderObject), 0, Py_TPFLAGS_DEFAULT, DecoderType_slots }; // ENCODER typedef struct { PyObject_HEAD struct lsqpack_enc enc; unsigned char hdr_buf[HDR_BUF_SZ]; unsigned char enc_buf[ENC_BUF_SZ]; unsigned char pfx_buf[PREFIX_MAX_SIZE]; } EncoderObject; static int Encoder_init(EncoderObject *self, PyObject *args, PyObject *kwargs) { lsqpack_enc_preinit(&self->enc, NULL); return 0; } static void Encoder_dealloc(EncoderObject *self) { lsqpack_enc_cleanup(&self->enc); PyTypeObject *tp = Py_TYPE(self); freefunc free = PyType_GetSlot(tp, Py_tp_free); free(self); Py_DECREF(tp); } static PyObject* Encoder_apply_settings(EncoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"max_table_capacity", "blocked_streams", NULL}; unsigned max_table_capacity, blocked_streams; unsigned char tsu_buf[LSQPACK_LONGEST_SDTC]; size_t tsu_len = sizeof(tsu_buf); if (!PyArg_ParseTupleAndKeywords(args, kwargs, "II", kwlist, &max_table_capacity, &blocked_streams)) return NULL; if (lsqpack_enc_init(&self->enc, NULL, max_table_capacity, max_table_capacity, blocked_streams, LSQPACK_ENC_OPT_STAGE_2, tsu_buf, &tsu_len) != 0) { PyErr_SetString(PyExc_RuntimeError, "lsqpack_enc_init failed"); return NULL; } return PyBytes_FromStringAndSize((const char*)tsu_buf, tsu_len); } PyDoc_STRVAR(Encoder_apply_settings__doc__, "apply_settings(max_table_capacity: int, blocked_streams: int) -> bytes\n\n" "Apply the settings received from the encoder.\n\n" ":param max_table_capacity: the maximum size in bytes of the dynamic table\n" ":param blocked_streams: the maximum number of streams that could be blocked\n"); static PyObject* Encoder_encode(EncoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"stream_id", "headers", NULL}; uint64_t stream_id; unsigned seqno = 0; PyObject *list, *tuple, *name, *value; size_t enc_len, hdr_len, pfx_len; size_t enc_off = 0, hdr_off = PREFIX_MAX_SIZE, pfx_off = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "KO", kwlist, &stream_id, &list)) return NULL; if (!PyList_Check(list)) { PyErr_SetString(PyExc_ValueError, "headers must be a list"); return NULL; } if (lsqpack_enc_start_header(&self->enc, stream_id, seqno) != 0) { PyErr_SetString(PyExc_RuntimeError, "lsqpack_enc_start_header failed"); return NULL; } for (Py_ssize_t i = 0; i < PyList_Size(list); ++i) { tuple = PyList_GetItem(list, i); if (!PyTuple_Check(tuple) || PyTuple_Size(tuple) != 2) { PyErr_SetString(PyExc_ValueError, "the header must be a two-tuple"); return NULL; } name = PyTuple_GetItem(tuple, 0); value = PyTuple_GetItem(tuple, 1); if (!PyBytes_Check(name) || !PyBytes_Check(value)) { PyErr_SetString(PyExc_ValueError, "the header's name and value must be bytes"); return NULL; } enc_len = ENC_BUF_SZ - enc_off; hdr_len = HDR_BUF_SZ - hdr_off; if (lsqpack_enc_encode(&self->enc, self->enc_buf + enc_off, &enc_len, self->hdr_buf + hdr_off, &hdr_len, PyBytes_AsString(name), PyBytes_Size(name), PyBytes_AsString(value), PyBytes_Size(value), 0) != LQES_OK) { PyErr_SetString(PyExc_RuntimeError, "lsqpack_enc_encode failed"); return NULL; } enc_off += enc_len; hdr_off += hdr_len; } pfx_len = lsqpack_enc_end_header(&self->enc, self->pfx_buf, PREFIX_MAX_SIZE, NULL); if (pfx_len <= 0) { PyErr_SetString(PyExc_RuntimeError, "lsqpack_enc_start_header failed"); return NULL; } pfx_off = PREFIX_MAX_SIZE - pfx_len; memcpy(self->hdr_buf + pfx_off, self->pfx_buf, pfx_len); name = PyBytes_FromStringAndSize((const char*)self->enc_buf, enc_off); value = PyBytes_FromStringAndSize((const char*)self->hdr_buf + pfx_off, hdr_off - pfx_off); tuple = PyTuple_Pack(2, name, value); Py_DECREF(name); Py_DECREF(value); return tuple; } PyDoc_STRVAR(Encoder_encode__doc__, "encode(stream_id: int, headers: List[Tuple[bytes, bytes]]) -> Tuple[bytes, bytes]\n\n" "Encode a list of headers.\n\n" "A tuple is returned containing two bytestrings: the encoder stream data " " and the encoded header block.\n\n" ":param stream_id: the stream ID\n" ":param headers: a list of header tuples\n"); static PyObject* Encoder_feed_decoder(EncoderObject *self, PyObject *args, PyObject *kwargs) { char *kwlist[] = {"data", NULL}; const unsigned char *data; Py_ssize_t data_len; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y#", kwlist, &data, &data_len)) return NULL; if (lsqpack_enc_decoder_in(&self->enc, data, data_len) < 0) { PyErr_SetString(DecoderStreamError, "lsqpack_enc_decoder_in failed"); return NULL; } Py_RETURN_NONE; } PyDoc_STRVAR(Encoder_feed_decoder__doc__, "feed_decoder(data: bytes) -> None\n\n" "Feed data from the decoder stream.\n\n" "If the data cannot be processed, :class:`DecoderStreamError` is raised.\n\n" ":param data: the decoder stream data\n"); static PyMethodDef Encoder_methods[] = { {"apply_settings", (PyCFunction)Encoder_apply_settings, METH_VARARGS | METH_KEYWORDS, Encoder_apply_settings__doc__}, {"encode", (PyCFunction)Encoder_encode, METH_VARARGS | METH_KEYWORDS, Encoder_encode__doc__}, {"feed_decoder", (PyCFunction)Encoder_feed_decoder, METH_VARARGS | METH_KEYWORDS, Encoder_feed_decoder__doc__}, {NULL} }; PyDoc_STRVAR(Encoder__doc__, "Encoder()\n\n" "QPACK encoder.\n"); static PyType_Slot EncoderType_slots[] = { {Py_tp_dealloc, Encoder_dealloc}, {Py_tp_methods, Encoder_methods}, {Py_tp_doc, Encoder__doc__}, {Py_tp_init, Encoder_init}, {0, 0}, }; static PyType_Spec EncoderType_spec = { MODULE_NAME ".Encoder", sizeof(EncoderObject), 0, Py_TPFLAGS_DEFAULT, EncoderType_slots }; // MODULE static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, MODULE_NAME, /* m_name */ "Bindings for ls-qpack.", /* m_doc */ -1, /* m_size */ NULL, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; PyMODINIT_FUNC PyInit__binding(void) { PyObject* m; m = PyModule_Create(&moduledef); if (m == NULL) return NULL; DecompressionFailed = PyErr_NewException(MODULE_NAME ".DecompressionFailed", PyExc_ValueError, NULL); Py_INCREF(DecompressionFailed); PyModule_AddObject(m, "DecompressionFailed", DecompressionFailed); DecoderStreamError = PyErr_NewException(MODULE_NAME ".DecoderStreamError", PyExc_ValueError, NULL); Py_INCREF(DecoderStreamError); PyModule_AddObject(m, "DecoderStreamError", DecoderStreamError); EncoderStreamError = PyErr_NewException(MODULE_NAME ".EncoderStreamError", PyExc_ValueError, NULL); Py_INCREF(EncoderStreamError); PyModule_AddObject(m, "EncoderStreamError", EncoderStreamError); StreamBlocked = PyErr_NewException(MODULE_NAME ".StreamBlocked", PyExc_ValueError, NULL); Py_INCREF(StreamBlocked); PyModule_AddObject(m, "StreamBlocked", StreamBlocked); DecoderType = PyType_FromSpec(&DecoderType_spec); if (DecoderType == NULL) return NULL; PyModule_AddObject(m, "Decoder", DecoderType); EncoderType = PyType_FromSpec(&EncoderType_spec); if (EncoderType == NULL) return NULL; PyModule_AddObject(m, "Encoder", EncoderType); return m; }