mirror of
https://gitee.com/fantix/kloop.git
synced 2024-09-26 14:10:01 +00:00
Compare commits
32 commits
Author | SHA1 | Date | |
---|---|---|---|
|
f055323af3 | ||
|
bfc3fb451a | ||
|
479d8be584 | ||
|
d659f7d533 | ||
|
652efd90a4 | ||
|
3c2d02a3f9 | ||
|
761f741d5d | ||
|
74f0062154 | ||
|
a1094281ec | ||
|
081a2877b8 | ||
|
665647013b | ||
|
47636fbf04 | ||
|
db84313627 | ||
|
08d3d8ff9a | ||
|
a8f639fdce | ||
|
19803cd298 | ||
|
9bdf0d1834 | ||
|
a935db4858 | ||
|
eca322da58 | ||
|
6ca4161a46 | ||
|
85799a1624 | ||
|
9cf03082c8 | ||
|
91691e5996 | ||
|
f3c43bf145 | ||
|
dd4a38a610 | ||
|
3b4b6c5c67 | ||
|
64b105657a | ||
|
bb7c378c95 | ||
|
67c2c9aa2a | ||
|
9fc44a51c8 | ||
|
97f02d40bc | ||
|
04b59cc52e |
53 changed files with 5016 additions and 34 deletions
32
.github/workflows/build.yml
vendored
Normal file
32
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: 构建
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: 获取源代码
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: 安装 Python
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
- name: 安装依赖关系
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install build
|
||||||
|
- name: 构建软件包
|
||||||
|
run: python -m build
|
||||||
|
- name: 上传源码包
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: kLoop 源码包
|
||||||
|
path: dist/*.tar.gz
|
||||||
|
- name: 上传 wheel 安装包
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: kLoop wheel 安装包
|
||||||
|
path: dist/*.whl
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -6,4 +6,5 @@
|
||||||
/tests/**/__pycache__/
|
/tests/**/__pycache__/
|
||||||
/src/**/*.c
|
/src/**/*.c
|
||||||
/src/**/*.so
|
/src/**/*.so
|
||||||
/src/kloop.egg-info/
|
/src/kLoop.egg-info/
|
||||||
|
/resolver/target/
|
||||||
|
|
4
LICENSE
4
LICENSE
|
@ -1,5 +1,5 @@
|
||||||
版权所有 (c) 2022 王川 http://fantix.pro
|
版权所有 (c) 2022 王川 https://fantix.pro
|
||||||
Copyright (c) 2022 Fantix King http://fantix.pro
|
Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
|
||||||
|
|
||||||
木兰宽松许可证
|
木兰宽松许可证
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
recursive-include src *.pyx *.pxd
|
include README*.md
|
||||||
|
include Makefile
|
||||||
|
recursive-include docs *.png *.md
|
||||||
|
recursive-include src *.pyx *.pxd *.h
|
||||||
|
include resolver/Cargo.toml
|
||||||
|
include resolver/Cargo.lock
|
||||||
|
recursive-include resolver *.rs
|
||||||
graft tests
|
graft tests
|
||||||
global-exclude *.py[cod] *.c
|
global-exclude *.py[cod] *.c
|
||||||
|
|
||||||
|
|
23
Makefile
Normal file
23
Makefile
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.PHONY: dist dev build clean
|
||||||
|
.DEFAULT_GOAL := dev
|
||||||
|
|
||||||
|
|
||||||
|
# Make distribution tarballs
|
||||||
|
dist:
|
||||||
|
pip install -U build
|
||||||
|
python -m build
|
||||||
|
|
||||||
|
|
||||||
|
# Incrementally build for development
|
||||||
|
dev:
|
||||||
|
KLOOP_DEBUG=1 python setup.py develop
|
||||||
|
|
||||||
|
|
||||||
|
# Always build for development
|
||||||
|
build:
|
||||||
|
KLOOP_FORCE=1 KLOOP_DEBUG=1 python setup.py develop
|
||||||
|
|
||||||
|
|
||||||
|
# Clean up everything including the Rust build cache
|
||||||
|
clean:
|
||||||
|
python setup.py clean --all
|
76
README.md
76
README.md
|
@ -1,5 +1,73 @@
|
||||||
# kLoop
|
# <img src="docs/kloop@2x.png" height="96px" align="left"> kLoop - *asyncio on Linux kernel*
|
||||||
|
|
||||||
kLoop is an implementation of the Python asyncio event loop written in Cython,
|
[![中文](https://img.shields.io/badge/Zh-中文-informational?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAFKADAAQAAAABAAAAEAAAAABHXVY9AAABc0lEQVQ4EaWSOy8EURTHd+wDEY94JVtgg9BI1B6dQqHiE1CrRasT30DpC2hVQimiFkJWVsSj2U1EsmQH4/ff3CO3WDuDk/zmf8/jnntm7qRSMRZFUQ4WYSimNFmaRlsgq8F83K6WuALyva4mixbc+kfJcGqa7CqU4AjaocNpG5oHsx7qB3EqQRC8K4g/gazAMbFTBdbgL1Zh0w2EbnMVHdMrd4LZNotZmIZJKMAemC2z0MS6oDlYhzOQ6c3yGR5Fec4OGPvEHCmn3np+kfyT51+QH8afcbFLTfjgFVS9tZrpwC4v1k9M39w3NTQrBxSM4127SAmNoBt0Ma3QyHRwGUIYdQUh0+c0wZsLPKKH8AwvoHgNlmABZLtwBdqnP0DD9IEG2If6N0oz5SbYSfW4PYhvgNmUxU1JZGEEAsUyjPmB7lhBA1Xe7NMWpuzXa39fnC7lN1b/mZttSNLQv9XXZs2US9LwzjU5R+/d+n/CBx9I2uELeXrRajeDqHwAAAAASUVORK5CYII=)](README.zh.md)
|
||||||
using io_uring and kTLS features of the Linux kernel, open-sourced and released
|
[![build](https://img.shields.io/github/workflow/status/fantix/kloop/构建?label=build&logo=github)](https://github.com/fantix/kloop/actions/workflows/build.yml)
|
||||||
under the MulanPSL - 2.0 license.
|
[![downloads](https://img.shields.io/pypi/dm/kloop?logo=pypi&logoColor=white)](https://pypi.python.org/pypi/gino)
|
||||||
|
[![code quality](https://img.shields.io/codacy/grade/f2e97d6eb2554e87b3cd15aae8f6b1e0?logo=codacy)](https://app.codacy.com/gh/fantix/kloop/dashboard)
|
||||||
|
[![license](https://img.shields.io/badge/license-MulanPSL--2.0-success?logo=opensourceinitiative&logoColor=white)](http://license.coscl.org.cn/MulanPSL2/)
|
||||||
|
|
||||||
|
kLoop is an implementation of the Python
|
||||||
|
[asyncio](https://docs.python.org/3/library/asyncio.html) event loop written
|
||||||
|
in [Cython](https://cython.org/), using
|
||||||
|
[io_uring](https://unixism.net/loti/what_is_io_uring.html) and
|
||||||
|
[kTLS](https://www.kernel.org/doc/html/latest/networking/tls-offload.html)
|
||||||
|
features of the Linux kernel, therefore called k(ernel)Loop.
|
||||||
|
|
||||||
|
kLoop is open-sourced and released under the
|
||||||
|
[MulanPSL - 2.0 license](http://license.coscl.org.cn/MulanPSL2).
|
||||||
|
|
||||||
|
**⚠️WARNING: THIS PROJECT IS IN PROOF-OF-CONCEPT STAGE!⚠️**
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* **Minimum syscalls** - all I/O calls are done in the kernel thanks to
|
||||||
|
io_uring, and the only remaining syscall to `io_uring_enter()` is also
|
||||||
|
optimized to be only called when necessary using the `SQPOLL` feature. That
|
||||||
|
means most of the overhead of syscalls is gone;
|
||||||
|
* **No GIL in the main-loop** - the hot-path is written in Cython without GIL,
|
||||||
|
that means it's compiled into pure C code without Python structures, saving
|
||||||
|
some memory and execution time. On the other hand, GIL is only taken before
|
||||||
|
Python callbacks, so it's slightly more friendly to multithreading, which is
|
||||||
|
still not recommended though.
|
||||||
|
* **TLS offloading** - all symmetric-key encryption and decryption work is
|
||||||
|
offloaded to the NIC if supported, or to the thread pool of io_uring
|
||||||
|
otherwise. This allows us to free up CPU for more I/O, or leverage all the
|
||||||
|
CPU cores even if the application is running single-threaded.
|
||||||
|
* **Asynchronous DNS resolving** - we blended in the Rust
|
||||||
|
[trust-dns](https://github.com/bluejekyll/trust-dns/) library with a custom
|
||||||
|
I/O runtime bridging to io_uring in C (including reading the
|
||||||
|
`/etc/resolv.conf` and `/etc/hosts` files), providing flexible APIs to manage
|
||||||
|
the concurrency, cache and configuration in Python.
|
||||||
|
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Python >= 3.10
|
||||||
|
* Linux >= 5.11 (enable kTLS with `modprobe tls`)
|
||||||
|
* OpenSSL >= 3.0 (kTLS receive offloading on TLS 1.3 requires the latest
|
||||||
|
development version)
|
||||||
|
|
||||||
|
Development and testing is done on Ubuntu 22.04.
|
||||||
|
|
||||||
|
|
||||||
|
## Architecture Diagram
|
||||||
|
|
||||||
|
<img src="docs/architecture.en.png" width="620px" alt="architecture.png">
|
||||||
|
|
||||||
|
Looks like the Lucky Charms factory, says @aaronbrighton ...
|
||||||
|
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Ubuntu 22.04
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install gcc libssl-dev python3-dev python3.10-venv
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
python3 -m venv path/to/env
|
||||||
|
source path/to/env/bin/activate
|
||||||
|
pip install cython
|
||||||
|
KLOOP_DEBUG=1 python setup.py develop # or just run `make`
|
||||||
|
```
|
||||||
|
|
67
README.zh.md
Normal file
67
README.zh.md
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# <img src="docs/kloop@2x.png" height="80px" align="left"> kLoop:*Linux 内核上的 asyncio*
|
||||||
|
|
||||||
|
[![English](https://img.shields.io/badge/英文-English-informational?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAQCAYAAADnEwSWAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAG6ADAAQAAAABAAAAEAAAAACiF0fSAAABJUlEQVQ4EWP8//8/MwMDAysQEw0YGRl/EK0YWSHQsgogJgX8RNZPCpuJFMWUqqWrZSw4XBsCFL+FQ+4/DnEUYWC8gNKCEDB+X8MlgIKVWCJMD64ADwOorwmIHyNhdyDbFYgPAfFXIAaBV0CcCTIG5DNsLo0GKnDEYc8+oGsvQ+UEgLQMkrpYIDsSiJGjRxTInwY07wmuYCxDMgCdmQUUgFmGLheNLoDEzwG5gBFJgFQmNr3ZQEPsgfgImmEquHy2BajwHZpiGPcmjAGk0aPgCDCIp4HkgcHWA6RsQGwoEMEVZ9VATZdgqkig7yKp/YjEBjORIxJdjhw+3tIFVzCGAoPBEo9ta4E+f4NHHqsULstqsKpGCJ4BMkm2jNrBiHAOFhZdLQMA8pKhkQYZiokAAAAASUVORK5CYII=)](README.md)
|
||||||
|
[![构建](https://img.shields.io/github/workflow/status/fantix/kloop/构建?label=构建&logo=github)](https://github.com/fantix/kloop/actions/workflows/build.yml)
|
||||||
|
[![下载](https://img.shields.io/pypi/dm/kloop?logo=pypi&logoColor=white&label=下载)](https://pypi.python.org/pypi/gino)
|
||||||
|
[![质量](https://img.shields.io/codacy/grade/f2e97d6eb2554e87b3cd15aae8f6b1e0?logo=codacy&label=质量)](https://app.codacy.com/gh/fantix/kloop/dashboard)
|
||||||
|
[![许可](https://img.shields.io/badge/许可-木兰PSLv2-success?logo=opensourceinitiative&logoColor=white)](http://license.coscl.org.cn/MulanPSL2/)
|
||||||
|
|
||||||
|
kLoop 是一个 Python
|
||||||
|
[asyncio](https://docs.python.org/3/library/asyncio.html)
|
||||||
|
事件循环的实现,主要用 [Cython](https://cython.org/) 编写,重点使用了 Linux 内核的
|
||||||
|
[io_uring](https://unixism.net/loti/what_is_io_uring.html) 和
|
||||||
|
[kTLS](https://www.kernel.org/doc/html/latest/networking/tls-offload.html)
|
||||||
|
功能,故称作 k(ernel)Loop。
|
||||||
|
|
||||||
|
您可在[木兰宽松许可证, 第2版](http://license.coscl.org.cn/MulanPSL2)允许的范围内使用
|
||||||
|
kLoop 的源代码或发行版。
|
||||||
|
|
||||||
|
**⚠️警告:项目仍在概念验证当中,满地都是坑!⚠️**
|
||||||
|
|
||||||
|
|
||||||
|
## 主要特性
|
||||||
|
|
||||||
|
* **系统调用次数非常少**:得益于 io_uring,所有 I/O 调用都由内核完成;因为启用了
|
||||||
|
`SQPOLL`,唯一的系统调用 `io_uring_enter()`
|
||||||
|
也仅在必要时才会用到,因此节省下了几乎所有系统调用的额外开销;
|
||||||
|
* **主循环主体不占有 GIL**:虽然使用了 Cython 编写,但大部分最核心的代码都被编译为不需要
|
||||||
|
Python 结构的普通 C 代码,一方面节省内存开销和处理时间,另一方面不持有 GIL,仅在有
|
||||||
|
Python 回调时才上 GIL 锁,对多线程稍微友好一点,但还是推荐单线程跑;
|
||||||
|
* **内核网卡代工 TLS**:如果硬件支持,TLS 通讯的全部对称加密解密皆由网卡完成,空出 CPU
|
||||||
|
来做更多的 I/O;即使网卡不支持,io_uring
|
||||||
|
也可以在内核中开多线程来执行对称加解密,不受应用端单线程的限制,充分利用多核 CPU;
|
||||||
|
* **纯异步 DNS 解析**:混编进了 Rust 写的
|
||||||
|
[trust-dns](https://github.com/bluejekyll/trust-dns/),I/O 层直接走
|
||||||
|
io_uring(包括加载 `/etc/resolv.conf` 和 `/etc/hosts` 文件),在 C 和 Rust
|
||||||
|
之间互相调用完成 DNS 解析,并且提供了更加灵活的 Python 接口来控制并发、缓存和配置文件。
|
||||||
|
|
||||||
|
|
||||||
|
## 环境需求
|
||||||
|
|
||||||
|
* Python >= 3.10
|
||||||
|
* Linux >= 5.11 (用 `modprobe tls` 命令来启用 kTLS 模块)
|
||||||
|
* OpenSSL >= 3.0(TLS 1.3 支持 kTLS 收包代工需要最新的开发版本)
|
||||||
|
|
||||||
|
目前主要是在 Ubuntu 22.04 上开发测试的。
|
||||||
|
|
||||||
|
|
||||||
|
## 架构图
|
||||||
|
|
||||||
|
<img src="docs/architecture.zh.png" width="620px" alt="架构图.png">
|
||||||
|
|
||||||
|
@aaronbrighton 说像 Lucky Charms 卡通麦片工厂……
|
||||||
|
|
||||||
|
|
||||||
|
## 开发
|
||||||
|
|
||||||
|
### Ubuntu 22.04
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install gcc libssl-dev python3-dev python3.10-venv
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
python3 -m venv path/to/env
|
||||||
|
source path/to/env/bin/activate
|
||||||
|
pip install cython
|
||||||
|
KLOOP_DEBUG=1 python setup.py develop # 或者直接执行 `make`
|
||||||
|
```
|
BIN
docs/architecture.en.png
Normal file
BIN
docs/architecture.en.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 294 KiB |
BIN
docs/architecture.zh.png
Normal file
BIN
docs/architecture.zh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 293 KiB |
BIN
docs/kloop@2x.png
Normal file
BIN
docs/kloop@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -1,3 +1,7 @@
|
||||||
|
[tool.black]
|
||||||
|
line-length = 79
|
||||||
|
target-version = ["py310"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=42", "Cython>=0.29"]
|
requires = ["setuptools>=42", "Cython>=0.29"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
645
resolver/Cargo.lock
generated
Normal file
645
resolver/Cargo.lock
generated
Normal file
|
@ -0,0 +1,645 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum-as-inner"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "432f044e9077ad6666f3446df0489a71ac3ed0336ea54edf6b85e00cd7562283"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "form_urlencoded"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hostname"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"match_cfg",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||||
|
dependencies = [
|
||||||
|
"matches",
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-normalization",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipconfig"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723519edce41262b05d4143ceb95050e4c614f483e78e9fd9e39a8275a84ad98"
|
||||||
|
dependencies = [
|
||||||
|
"socket2",
|
||||||
|
"widestring",
|
||||||
|
"winapi",
|
||||||
|
"winreg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.124"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru-cache"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "match_cfg"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matches"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resolv-conf"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
|
||||||
|
dependencies = [
|
||||||
|
"hostname",
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resolver"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"resolv-conf",
|
||||||
|
"trust-dns-proto",
|
||||||
|
"trust-dns-resolver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.91"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trust-dns-proto"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "git+https://github.com/bluejekyll/trust-dns#170e3f7d7dfbc2b6c48318cb5c996a8cc6db77a1"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"cfg-if",
|
||||||
|
"data-encoding",
|
||||||
|
"enum-as-inner",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"idna",
|
||||||
|
"ipnet",
|
||||||
|
"lazy_static",
|
||||||
|
"rand",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"tinyvec",
|
||||||
|
"tracing",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trust-dns-resolver"
|
||||||
|
version = "0.21.2"
|
||||||
|
source = "git+https://github.com/bluejekyll/trust-dns#170e3f7d7dfbc2b6c48318cb5c996a8cc6db77a1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
|
"ipconfig",
|
||||||
|
"lazy_static",
|
||||||
|
"lru-cache",
|
||||||
|
"parking_lot",
|
||||||
|
"resolv-conf",
|
||||||
|
"smallvec",
|
||||||
|
"thiserror",
|
||||||
|
"tracing",
|
||||||
|
"trust-dns-proto",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-normalization"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "url"
|
||||||
|
version = "2.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"idna",
|
||||||
|
"matches",
|
||||||
|
"percent-encoding",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winreg"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
19
resolver/Cargo.toml
Normal file
19
resolver/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "resolver"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "kloop_resolver"
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2.124"
|
||||||
|
log = "0.4.16"
|
||||||
|
resolv-conf = { version = "0.7.0", features = ["system"] }
|
||||||
|
trust-dns-proto = { git = "https://github.com/bluejekyll/trust-dns", default-features = false }
|
||||||
|
trust-dns-resolver = { git = "https://github.com/bluejekyll/trust-dns", default-features = false, features = ["system-config"]}
|
||||||
|
futures-util = "0.3.21"
|
||||||
|
futures-io = "0.3.5"
|
||||||
|
futures-executor = "0.3.5"
|
||||||
|
async-trait = "0.1.43"
|
61
resolver/src/kloop.rs
Normal file
61
resolver/src/kloop.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
kLoop is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
use core::marker;
|
||||||
|
use std::task::Waker;
|
||||||
|
use libc;
|
||||||
|
|
||||||
|
use crate::resolve::KLoopResolver;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CResolver {
|
||||||
|
_data: [u8; 0],
|
||||||
|
_marker: marker::PhantomData<(*mut u8, marker::PhantomPinned)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CResolve {
|
||||||
|
_data: [u8; 0],
|
||||||
|
_marker: marker::PhantomData<(*mut u8, marker::PhantomPinned)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct UDPAction {
|
||||||
|
_data: [u8; 0],
|
||||||
|
_marker: marker::PhantomData<(*mut u8, marker::PhantomPinned)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn resolver_set(c_resolver: *const CResolver, resolver: *mut KLoopResolver);
|
||||||
|
// pub fn resolve_set_poller(resolve: *const CResolve, poller: *mut Poller);
|
||||||
|
pub fn resolve_prep_addr(resolve: *const CResolve) -> *mut libc::sockaddr;
|
||||||
|
pub fn resolve_done_cb(resolve: *const CResolve) -> libc::c_int;
|
||||||
|
pub fn udp_bind(addr: *const libc::sockaddr, addrlen: libc::socklen_t) -> libc::c_int;
|
||||||
|
pub fn udp_action_init(fd: libc::c_int, resolver: *const CResolver) -> libc::c_ulong;
|
||||||
|
pub fn udp_send_poll(
|
||||||
|
send: libc::c_ulong,
|
||||||
|
data: *const u8,
|
||||||
|
datalen: libc::size_t,
|
||||||
|
addr: *const libc::sockaddr,
|
||||||
|
addrlen: libc::socklen_t,
|
||||||
|
waker: *mut Waker,
|
||||||
|
) -> libc::c_int;
|
||||||
|
pub fn udp_recv_poll(
|
||||||
|
recv: libc::c_ulong,
|
||||||
|
data: *mut u8,
|
||||||
|
datalen: libc::size_t,
|
||||||
|
waker: *mut Waker,
|
||||||
|
) -> libc::c_int;
|
||||||
|
pub fn udp_get_addr(action: libc::c_ulong) -> *const libc::sockaddr;
|
||||||
|
pub fn udp_action_free(action: libc::c_ulong);
|
||||||
|
}
|
18
resolver/src/lib.rs
Normal file
18
resolver/src/lib.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
kLoop is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
mod kloop;
|
||||||
|
mod resolve;
|
||||||
|
mod runtime;
|
||||||
|
|
||||||
|
pub use resolve::{resolver_init, resolver_lookup_ip};
|
183
resolver/src/resolve.rs
Normal file
183
resolver/src/resolve.rs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
kLoop is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
use futures_executor::{LocalPool, LocalSpawner};
|
||||||
|
use futures_util::task::{LocalSpawnExt, SpawnError};
|
||||||
|
use std;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::io;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use libc;
|
||||||
|
use trust_dns_resolver::name_server::{GenericConnection, GenericConnectionProvider};
|
||||||
|
use trust_dns_resolver::system_conf::parse_resolv_conf;
|
||||||
|
use trust_dns_resolver::{AsyncResolver, Hosts};
|
||||||
|
|
||||||
|
use crate::kloop::{resolve_done_cb, resolve_prep_addr, resolver_set, CResolve, CResolver};
|
||||||
|
use crate::runtime::{KLoopHandle, KLoopRuntime};
|
||||||
|
|
||||||
|
type KLoopConnection = GenericConnection;
|
||||||
|
|
||||||
|
type KLoopConnectionProvider = GenericConnectionProvider<KLoopRuntime>;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
pub static CURRENT_RESOLVER: Rc<RefCell<Option<*mut KLoopResolver>>> = Rc::new(RefCell::new(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct KLoopResolver {
|
||||||
|
resolver: AsyncResolver<KLoopConnection, KLoopConnectionProvider>,
|
||||||
|
pub c_resolver: *const CResolver,
|
||||||
|
pool: LocalPool,
|
||||||
|
spawner: LocalSpawner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KLoopResolver {
|
||||||
|
fn new(
|
||||||
|
resolv_conf: &[u8],
|
||||||
|
hosts_conf: &[u8],
|
||||||
|
c_resolver: *const CResolver,
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
let (config, mut options) = parse_resolv_conf(resolv_conf)?;
|
||||||
|
options.use_hosts_file = false;
|
||||||
|
let conn_provider = GenericConnectionProvider::new(KLoopHandle);
|
||||||
|
let mut resolver = AsyncResolver::new_with_conn(config, options, conn_provider).unwrap();
|
||||||
|
resolver.set_hosts(Some(Hosts::default().read_hosts_conf(hosts_conf)?));
|
||||||
|
let pool = LocalPool::new();
|
||||||
|
let spawner = pool.spawner();
|
||||||
|
Ok(Self {
|
||||||
|
resolver,
|
||||||
|
c_resolver,
|
||||||
|
pool,
|
||||||
|
spawner,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn<Fut>(&self, future: Fut) -> Result<(), SpawnError>
|
||||||
|
where
|
||||||
|
Fut: Future<Output = ()> + 'static,
|
||||||
|
{
|
||||||
|
self.spawner.spawn_local(future)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_until_stalled(&mut self) {
|
||||||
|
loop {
|
||||||
|
self.pool.run_until_stalled();
|
||||||
|
if !self.pool.try_run_one() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn lookup_ip(&self, resolve: *mut CResolve, host: &str, port: libc::in_port_t) -> () {
|
||||||
|
match self.resolver.lookup_ip(host).await {
|
||||||
|
Ok(result) => {
|
||||||
|
for ip in result.into_iter() {
|
||||||
|
match ip {
|
||||||
|
IpAddr::V4(ip) => unsafe {
|
||||||
|
let out = resolve_prep_addr(resolve) as *mut libc::sockaddr_in;
|
||||||
|
if out.is_null() {
|
||||||
|
println!("resolve_prep_addr returned NULL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*out).sin_family = libc::AF_INET as libc::sa_family_t;
|
||||||
|
(*out).sin_addr = libc::in_addr {
|
||||||
|
s_addr: u32::from_ne_bytes(ip.octets()),
|
||||||
|
};
|
||||||
|
(*out).sin_port = port.to_be();
|
||||||
|
(*out).sin_zero = [0; 8];
|
||||||
|
},
|
||||||
|
IpAddr::V6(ip) => unsafe {
|
||||||
|
let out = resolve_prep_addr(resolve) as *mut libc::sockaddr_in6;
|
||||||
|
if out.is_null() {
|
||||||
|
println!("resolve_prep_addr returned NULL");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*out).sin6_family = libc::AF_INET6 as libc::sa_family_t;
|
||||||
|
(*out).sin6_addr = libc::in6_addr {
|
||||||
|
s6_addr: ip.octets(),
|
||||||
|
};
|
||||||
|
(*out).sin6_port = port.to_be();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("lookup_ip error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
resolve_done_cb(resolve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn resolver_init(
|
||||||
|
c_resolver: *const CResolver,
|
||||||
|
resolv_conf_data: *const u8,
|
||||||
|
resolv_conf_data_size: libc::size_t,
|
||||||
|
hosts_conf_data: *const u8,
|
||||||
|
hosts_conf_data_size: libc::size_t,
|
||||||
|
) -> libc::c_int {
|
||||||
|
let resolv_conf =
|
||||||
|
unsafe { std::slice::from_raw_parts(resolv_conf_data, resolv_conf_data_size) };
|
||||||
|
let hosts_conf = unsafe { std::slice::from_raw_parts(hosts_conf_data, hosts_conf_data_size) };
|
||||||
|
let resolver = match KLoopResolver::new(resolv_conf, hosts_conf, c_resolver) {
|
||||||
|
Ok(resolver) => resolver,
|
||||||
|
Err(e) => return 0,
|
||||||
|
};
|
||||||
|
let rv = Box::into_raw(Box::new(resolver));
|
||||||
|
unsafe {
|
||||||
|
resolver_set(c_resolver, rv);
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn resolver_lookup_ip(
|
||||||
|
resolver: *mut KLoopResolver,
|
||||||
|
resolve: *mut CResolve,
|
||||||
|
host_raw: *const u8,
|
||||||
|
length: libc::size_t,
|
||||||
|
port: libc::in_port_t,
|
||||||
|
) -> libc::c_int {
|
||||||
|
let host = match std::str::from_utf8(unsafe { std::slice::from_raw_parts(host_raw, length) }) {
|
||||||
|
Ok(host) => host,
|
||||||
|
_ => return 0,
|
||||||
|
};
|
||||||
|
let r = || unsafe { resolver.as_mut() }.unwrap();
|
||||||
|
|
||||||
|
let fut = r().lookup_ip(resolve, host, port);
|
||||||
|
if let Err(e) = r().spawn(fut) {
|
||||||
|
println!("spawn error: {:?}", e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
CURRENT_RESOLVER.with(|current| {
|
||||||
|
*current.borrow_mut() = Some(resolver);
|
||||||
|
r().run_until_stalled();
|
||||||
|
*current.borrow_mut() = None;
|
||||||
|
});
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn resolver_run_until_stalled(resolver: *mut KLoopResolver) {
|
||||||
|
CURRENT_RESOLVER.with(|current| {
|
||||||
|
*current.borrow_mut() = Some(resolver);
|
||||||
|
unsafe { resolver.as_mut() }.unwrap().run_until_stalled();
|
||||||
|
*current.borrow_mut() = None;
|
||||||
|
});
|
||||||
|
}
|
270
resolver/src/runtime.rs
Normal file
270
resolver/src/runtime.rs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
kLoop is licensed under Mulan PSL v2.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
http://license.coscl.org.cn/MulanPSL2
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
use crate::kloop;
|
||||||
|
use crate::resolve::CURRENT_RESOLVER;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use futures_io::{AsyncRead, AsyncWrite};
|
||||||
|
use libc::{sockaddr, socklen_t};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll, Waker};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{io, mem};
|
||||||
|
use trust_dns_proto::error::ProtoError;
|
||||||
|
use trust_dns_proto::tcp::{Connect, DnsTcpStream};
|
||||||
|
use trust_dns_proto::udp::UdpSocket;
|
||||||
|
use trust_dns_proto::Time;
|
||||||
|
use trust_dns_resolver::name_server::{RuntimeProvider, Spawn};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct KLoopTimer {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Time for KLoopTimer {
|
||||||
|
async fn delay_for(duration: Duration) {
|
||||||
|
println!("TODO: delay_for: {:?}", duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn timeout<F: 'static + Future + Send>(
|
||||||
|
duration: Duration,
|
||||||
|
future: F,
|
||||||
|
) -> io::Result<F::Output> {
|
||||||
|
println!("TODO: timeout: {:?}", duration);
|
||||||
|
Ok(future.await)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KLoopTcp {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Connect for KLoopTcp {
|
||||||
|
async fn connect_with_bind(
|
||||||
|
addr: SocketAddr,
|
||||||
|
bind_addr: Option<SocketAddr>,
|
||||||
|
) -> io::Result<Self> {
|
||||||
|
println!("TODO: connect_with_bind: {:?} {:?}", addr, bind_addr);
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for KLoopTcp {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
println!("TODO: poll_read");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for KLoopTcp {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
println!("TODO: poll_write");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
println!("TODO: poll_flush");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
println!("TODO: poll_close");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsTcpStream for KLoopTcp {
|
||||||
|
type Time = KLoopTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KLoopUdp {
|
||||||
|
fd: libc::c_int,
|
||||||
|
send: libc::c_ulong,
|
||||||
|
recv: libc::c_ulong,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl UdpSocket for KLoopUdp {
|
||||||
|
type Time = KLoopTimer;
|
||||||
|
|
||||||
|
async fn connect(addr: SocketAddr) -> io::Result<Self> {
|
||||||
|
println!("TODO: KLoopUdp: connect({})", addr);
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect_with_bind(addr: SocketAddr, bind_addr: SocketAddr) -> io::Result<Self> {
|
||||||
|
println!("TODO: KLoopUdp: connect_with_bind({}, {})", addr, bind_addr);
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn bind(addr: SocketAddr) -> io::Result<Self> {
|
||||||
|
let (addr_ptr, addr_len) = socket_addr_as_ptr(addr);
|
||||||
|
let fd = unsafe { kloop::udp_bind(addr_ptr, addr_len) };
|
||||||
|
CURRENT_RESOLVER.with(|resolver| {
|
||||||
|
let resolver = resolver.borrow().unwrap();
|
||||||
|
let resolver = unsafe { resolver.as_ref() }.unwrap();
|
||||||
|
let resolver = resolver.c_resolver;
|
||||||
|
let send = unsafe { kloop::udp_action_init(fd, resolver) };
|
||||||
|
let recv = unsafe { kloop::udp_action_init(fd, resolver) };
|
||||||
|
Ok(KLoopUdp { fd, send, recv })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_recv_from(
|
||||||
|
&self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Poll<io::Result<(usize, SocketAddr)>> {
|
||||||
|
let waker = Box::new(cx.waker().clone());
|
||||||
|
match unsafe {
|
||||||
|
kloop::udp_recv_poll(
|
||||||
|
self.recv,
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
buf.len(),
|
||||||
|
Box::into_raw(waker),
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
res if res > 0 => {
|
||||||
|
Poll::Ready(unsafe {
|
||||||
|
ptr_to_socket_addr(kloop::udp_get_addr(self.recv))
|
||||||
|
}.map(|addr| (res as usize, addr)))
|
||||||
|
}
|
||||||
|
_ => Poll::Pending,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_send_to(
|
||||||
|
&self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
target: SocketAddr,
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
let waker = Box::new(cx.waker().clone());
|
||||||
|
let (addr, addrlen) = socket_addr_as_ptr(target);
|
||||||
|
match unsafe {
|
||||||
|
kloop::udp_send_poll(
|
||||||
|
self.send,
|
||||||
|
buf.as_ptr(),
|
||||||
|
buf.len(),
|
||||||
|
addr,
|
||||||
|
addrlen,
|
||||||
|
Box::into_raw(waker),
|
||||||
|
)
|
||||||
|
} {
|
||||||
|
res if res > 0 => {
|
||||||
|
Poll::Ready(Ok(res as usize))
|
||||||
|
}
|
||||||
|
res => {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for KLoopUdp {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
kloop::udp_action_free(self.send);
|
||||||
|
kloop::udp_action_free(self.recv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn socket_addr_as_ptr(addr: SocketAddr) -> (*const sockaddr, socklen_t) {
|
||||||
|
match addr {
|
||||||
|
SocketAddr::V4(ref a) => (
|
||||||
|
a as *const _ as *const _,
|
||||||
|
mem::size_of_val(a) as libc::socklen_t,
|
||||||
|
),
|
||||||
|
SocketAddr::V6(ref a) => (
|
||||||
|
a as *const _ as *const _,
|
||||||
|
mem::size_of_val(a) as libc::socklen_t,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsafe fn ptr_to_socket_addr(
|
||||||
|
addr: *const libc::sockaddr,
|
||||||
|
) -> io::Result<SocketAddr> {
|
||||||
|
match (*addr).sa_family as libc::c_int {
|
||||||
|
libc::AF_INET => {
|
||||||
|
let addr: &libc::sockaddr_in = &*(addr as *const libc::sockaddr_in);
|
||||||
|
let ip = Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes());
|
||||||
|
let port = u16::from_be(addr.sin_port);
|
||||||
|
Ok(SocketAddr::V4(SocketAddrV4::new(ip, port)))
|
||||||
|
}
|
||||||
|
libc::AF_INET6 => {
|
||||||
|
let addr: &libc::sockaddr_in6 = &*(addr as *const libc::sockaddr_in6);
|
||||||
|
let ip = Ipv6Addr::from(addr.sin6_addr.s6_addr);
|
||||||
|
let port = u16::from_be(addr.sin6_port);
|
||||||
|
Ok(SocketAddr::V6(SocketAddrV6::new(
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
addr.sin6_flowinfo,
|
||||||
|
addr.sin6_scope_id,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
_ => Err(io::ErrorKind::InvalidInput.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct KLoopHandle;
|
||||||
|
|
||||||
|
impl Spawn for KLoopHandle {
|
||||||
|
fn spawn_bg<F>(&mut self, future: F)
|
||||||
|
where
|
||||||
|
F: Future<Output = Result<(), ProtoError>> + Send + 'static,
|
||||||
|
{
|
||||||
|
CURRENT_RESOLVER.with(|resolver| {
|
||||||
|
let r = resolver.borrow().unwrap();
|
||||||
|
let r = unsafe { r.as_mut() }.unwrap();
|
||||||
|
r.spawn(async {
|
||||||
|
future.await.unwrap_or_else(|e| {
|
||||||
|
println!("spawn_bg error: {:?}", e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct KLoopRuntime;
|
||||||
|
|
||||||
|
impl RuntimeProvider for KLoopRuntime {
|
||||||
|
type Handle = KLoopHandle;
|
||||||
|
type Timer = KLoopTimer;
|
||||||
|
type Udp = KLoopUdp;
|
||||||
|
type Tcp = KLoopTcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn waker_wake(waker: *mut Waker) {
|
||||||
|
let waker = unsafe { Box::from_raw(waker) };
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn waker_forget(waker: *mut Waker) {
|
||||||
|
unsafe { Box::from_raw(waker) };
|
||||||
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = kLoop
|
name = kLoop
|
||||||
version = 0.0.1
|
version = 0.1.0
|
||||||
author = Fantix King
|
author = Fantix King
|
||||||
author_email = fantix.king@gmail.com
|
author_email = fantix.king@gmail.com
|
||||||
description = An asyncio event loop using Linux io_uring and kTLS.
|
description = An asyncio event loop using Linux io_uring and kTLS.
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
url = https://github.com/fantix/kloop
|
url = https://gitee.com/fantix/kloop
|
||||||
project_urls =
|
project_urls =
|
||||||
Bug Tracker = https://github.com/fantix/kloop/issues
|
Bug Tracker = https://gitee.com/fantix/kloop/issues
|
||||||
classifiers =
|
classifiers =
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
License :: OSI Approved
|
License :: OSI Approved
|
||||||
|
@ -18,7 +18,7 @@ classifiers =
|
||||||
package_dir =
|
package_dir =
|
||||||
= src
|
= src
|
||||||
packages = find:
|
packages = find:
|
||||||
python_requires = >=3.8
|
python_requires = >=3.10
|
||||||
|
|
||||||
[options.packages.find]
|
[options.packages.find]
|
||||||
where = src
|
where = src
|
||||||
|
|
138
setup.py
138
setup.py
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -9,16 +9,134 @@
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
from Cython.Build import cythonize
|
import os
|
||||||
from Cython.Distutils import Extension
|
import subprocess
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
setup(
|
from Cython.Build import cythonize
|
||||||
ext_modules=cythonize(
|
from Cython.Distutils import build_ext
|
||||||
[
|
from Cython.Distutils import Extension
|
||||||
Extension("kloop.uring", ["src/kloop/uring.pyx"]),
|
from distutils import dir_util
|
||||||
Extension("kloop.ktls", ["src/kloop/ktls.pyx"]),
|
from distutils import log
|
||||||
],
|
from distutils.command.clean import clean
|
||||||
language_level="3",
|
|
||||||
|
|
||||||
|
class build_ext_with_resolver(build_ext):
|
||||||
|
def finalize_options(self):
|
||||||
|
self.set_undefined_options(
|
||||||
|
"build",
|
||||||
|
("debug", "debug"),
|
||||||
|
("force", "force"),
|
||||||
)
|
)
|
||||||
|
if self.debug is None:
|
||||||
|
self.debug = os.getenv("KLOOP_DEBUG", "0") == "1"
|
||||||
|
if self.force is None:
|
||||||
|
self.force = os.getenv("KLOOP_FORCE", "0") == "1"
|
||||||
|
|
||||||
|
for ext in self.distribution.ext_modules:
|
||||||
|
if ext.cython_directives:
|
||||||
|
ext.cython_directives["language_level"] = "3"
|
||||||
|
else:
|
||||||
|
ext.cython_directives = {"language_level": "3"}
|
||||||
|
if ext.cython_compile_time_env:
|
||||||
|
ext.cython_compile_time_env["DEBUG"] = self.debug
|
||||||
|
else:
|
||||||
|
ext.cython_compile_time_env = {"DEBUG": self.debug}
|
||||||
|
if self.debug:
|
||||||
|
if "-O0" not in ext.extra_compile_args:
|
||||||
|
ext.extra_compile_args.append("-O0")
|
||||||
|
if ext.name == "kloop.loop":
|
||||||
|
resolver = (
|
||||||
|
f"resolver/target/{'debug' if self.debug else 'release'}"
|
||||||
|
f"/libkloop_resolver.a"
|
||||||
|
)
|
||||||
|
if resolver not in ext.extra_link_args:
|
||||||
|
ext.extra_link_args.append(resolver)
|
||||||
|
if resolver not in ext.depends:
|
||||||
|
ext.depends.append(resolver)
|
||||||
|
|
||||||
|
super().finalize_options()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.force:
|
||||||
|
cmd = ["cargo", "clean", "-p", "resolver"]
|
||||||
|
if not self.debug:
|
||||||
|
cmd.append("-r")
|
||||||
|
self.announce(f"Running: {cmd}", log.INFO)
|
||||||
|
subprocess.check_call(cmd, cwd="resolver")
|
||||||
|
|
||||||
|
cmd = ["cargo", "build"]
|
||||||
|
if not self.debug:
|
||||||
|
cmd.append("-r")
|
||||||
|
self.announce(f"Running: {cmd}", log.INFO)
|
||||||
|
subprocess.check_call(cmd, cwd="resolver")
|
||||||
|
super().run()
|
||||||
|
|
||||||
|
|
||||||
|
class clean_with_resolver(clean):
|
||||||
|
def run(self):
|
||||||
|
super().run()
|
||||||
|
for d in self.distribution.package_dir.values():
|
||||||
|
self._clean_dir(d)
|
||||||
|
cmd = ["cargo", "clean"]
|
||||||
|
if not self.all:
|
||||||
|
cmd.extend(["-p", "resolver"])
|
||||||
|
self.announce(f"Running: {cmd}", log.INFO)
|
||||||
|
if not self.dry_run:
|
||||||
|
subprocess.check_call(cmd, cwd="resolver")
|
||||||
|
|
||||||
|
def _clean_dir(self, path):
|
||||||
|
for f in os.listdir(path):
|
||||||
|
name, ext = os.path.splitext(f)
|
||||||
|
real_f = os.path.join(path, f)
|
||||||
|
is_dir = os.path.isdir(real_f) and not os.path.islink(real_f)
|
||||||
|
if name == "__pycache__" or ext in {".egg-info", ".so", ".c"}:
|
||||||
|
if is_dir:
|
||||||
|
dir_util.remove_tree(real_f, dry_run=self.dry_run)
|
||||||
|
else:
|
||||||
|
self.announce(f"removing {real_f!r}", log.INFO)
|
||||||
|
if not self.dry_run:
|
||||||
|
os.remove(real_f)
|
||||||
|
elif is_dir:
|
||||||
|
self._clean_dir(real_f)
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
cmdclass={
|
||||||
|
"build_ext": build_ext_with_resolver,
|
||||||
|
"clean": clean_with_resolver,
|
||||||
|
},
|
||||||
|
ext_modules=[
|
||||||
|
Extension(
|
||||||
|
"kloop.loop",
|
||||||
|
["src/kloop/loop.pyx"],
|
||||||
|
),
|
||||||
|
Extension(
|
||||||
|
"kloop.tls",
|
||||||
|
["src/kloop/tls.pyx"],
|
||||||
|
libraries=[
|
||||||
|
lib.strip().removeprefix("-l")
|
||||||
|
for lib in sysconfig.get_config_var("OPENSSL_LIBS").split()
|
||||||
|
],
|
||||||
|
include_dirs=[
|
||||||
|
d.strip().removeprefix("-I")
|
||||||
|
for d in sysconfig.get_config_var("OPENSSL_INCLUDES").split()
|
||||||
|
],
|
||||||
|
library_dirs=[
|
||||||
|
d.strip().removeprefix("-L")
|
||||||
|
for d in sysconfig.get_config_var("OPENSSL_LDFLAGS").split()
|
||||||
|
if d.strip().startswith("-L")
|
||||||
|
],
|
||||||
|
extra_link_args=[
|
||||||
|
d.strip()
|
||||||
|
for d in sysconfig.get_config_var("OPENSSL_LDFLAGS").split()
|
||||||
|
if not d.strip().startswith("-L")
|
||||||
|
],
|
||||||
|
runtime_library_dirs=(lambda x: [x] if x else [])(
|
||||||
|
sysconfig.get_config_var("OPENSSL_RPATH")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -8,3 +8,5 @@
|
||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
from .loop import KLoop, KLoopPolicy
|
||||||
|
|
19
src/kloop/fileio.pxd
Normal file
19
src/kloop/fileio.pxd
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct FileReader:
|
||||||
|
Loop* loop
|
||||||
|
const char* path
|
||||||
|
int fd, cancelled
|
||||||
|
char* data
|
||||||
|
size_t size, offset
|
||||||
|
RingCallback ring_cb
|
||||||
|
RingCallback done_cb
|
116
src/kloop/fileio.pyx
Normal file
116
src/kloop/fileio.pyx
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef size_t FILE_READ_BUF_SIZE = 4096
|
||||||
|
|
||||||
|
|
||||||
|
cdef int file_reader_openat_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
int fd = cb.res
|
||||||
|
FileReader* fr = <FileReader*>cb.data
|
||||||
|
|
||||||
|
if fd < 0:
|
||||||
|
fr.done_cb.res = fd
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
if fr.cancelled:
|
||||||
|
fr.done_cb.res = -libc.ECANCELED
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
fr.fd = fd
|
||||||
|
fr.data = <char*>PyMem_RawMalloc(FILE_READ_BUF_SIZE)
|
||||||
|
if fr.data == NULL:
|
||||||
|
fr.done_cb.res = -errno.ENOMEM
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
fr.size = FILE_READ_BUF_SIZE
|
||||||
|
fr.offset = 0
|
||||||
|
fr.ring_cb.callback = file_reader_read_cb
|
||||||
|
if ring_sq_submit_read(
|
||||||
|
&fr.loop.ring.sq,
|
||||||
|
fd,
|
||||||
|
fr.data,
|
||||||
|
FILE_READ_BUF_SIZE,
|
||||||
|
0,
|
||||||
|
&fr.ring_cb,
|
||||||
|
):
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
fr.done_cb.res = -errno.EAGAIN
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int file_reader_read_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
int read = cb.res
|
||||||
|
FileReader* fr = <FileReader*>cb.data
|
||||||
|
size_t size = fr.size
|
||||||
|
size_t offset = fr.offset
|
||||||
|
|
||||||
|
if read < 0:
|
||||||
|
fr.done_cb.res = read
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
if fr.cancelled:
|
||||||
|
fr.done_cb.res = -libc.ECANCELED
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
offset += read
|
||||||
|
if read > 0 and offset == size:
|
||||||
|
size += FILE_READ_BUF_SIZE
|
||||||
|
if PyMem_RawRealloc(fr.data, size) == NULL:
|
||||||
|
fr.done_cb.res = -errno.ENOMEM
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
fr.size = size
|
||||||
|
fr.offset = offset
|
||||||
|
if ring_sq_submit_read(
|
||||||
|
&fr.loop.ring.sq,
|
||||||
|
fr.fd,
|
||||||
|
fr.data + offset,
|
||||||
|
FILE_READ_BUF_SIZE,
|
||||||
|
offset,
|
||||||
|
&fr.ring_cb,
|
||||||
|
):
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
fr.done_cb.res = -errno.EAGAIN
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
else:
|
||||||
|
fr.done_cb.res = 1
|
||||||
|
fr.offset = offset
|
||||||
|
return fr.done_cb.callback(&fr.done_cb)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int file_reader_start(
|
||||||
|
FileReader* fr, Loop* loop, const char* path
|
||||||
|
) nogil:
|
||||||
|
fr.loop = loop
|
||||||
|
fr.done_cb.res = 0
|
||||||
|
fr.fd = 0
|
||||||
|
fr.data = NULL
|
||||||
|
fr.cancelled = 0
|
||||||
|
fr.ring_cb.callback = file_reader_openat_cb
|
||||||
|
fr.ring_cb.data = <void*>fr
|
||||||
|
return ring_sq_submit_openat(
|
||||||
|
&loop.ring.sq,
|
||||||
|
0, # dfd
|
||||||
|
path, # path
|
||||||
|
0, # flags
|
||||||
|
0, # mode
|
||||||
|
&fr.ring_cb
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int file_reader_done(FileReader* fr) nogil:
|
||||||
|
cdef int fd = fr.fd
|
||||||
|
fr.done_cb.res = 0
|
||||||
|
if fr.data != NULL:
|
||||||
|
PyMem_RawFree(fr.data)
|
||||||
|
fr.data = NULL
|
||||||
|
if fd != 0:
|
||||||
|
fr.fd = 0
|
||||||
|
return ring_sq_submit_close(&fr.loop.ring.sq, fd, NULL)
|
||||||
|
return 1
|
37
src/kloop/handle.pxd
Normal file
37
src/kloop/handle.pxd
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef unsigned char CANCELLED_MASK = 1
|
||||||
|
cdef unsigned char SCHEDULED_MASK = 1 << 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct Callback:
|
||||||
|
unsigned char mask
|
||||||
|
PyObject* handle
|
||||||
|
long long when
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Handle:
|
||||||
|
cdef:
|
||||||
|
Callback cb
|
||||||
|
object callback
|
||||||
|
object args
|
||||||
|
KLoopImpl loop
|
||||||
|
object source_traceback
|
||||||
|
object repr
|
||||||
|
object context
|
||||||
|
|
||||||
|
cdef run(self)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TimerHandle(Handle):
|
||||||
|
cdef:
|
||||||
|
bint scheduled
|
152
src/kloop/handle.pyx
Normal file
152
src/kloop/handle.pyx
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Handle:
|
||||||
|
def __init__(self, callback, args, loop, context=None):
|
||||||
|
if context is None:
|
||||||
|
context = contextvars.copy_context()
|
||||||
|
self.context = context
|
||||||
|
self.loop = loop
|
||||||
|
self.callback = callback
|
||||||
|
self.args = args
|
||||||
|
self.repr = None
|
||||||
|
self.cb.handle = <PyObject*>self
|
||||||
|
# if self._loop.get_debug():
|
||||||
|
# self._source_traceback = format_helpers.extract_stack(
|
||||||
|
# sys._getframe(1))
|
||||||
|
# else:
|
||||||
|
# self._source_traceback = None
|
||||||
|
|
||||||
|
def _repr_info(self):
|
||||||
|
info = [self.__class__.__name__]
|
||||||
|
if self.cb.mask & CANCELLED_MASK:
|
||||||
|
info.append('cancelled')
|
||||||
|
if self.callback is not None:
|
||||||
|
info.append(_format_callback_source(self.callback, self.args))
|
||||||
|
# if self._source_traceback:
|
||||||
|
# frame = self._source_traceback[-1]
|
||||||
|
# info.append(f'created at {frame[0]}:{frame[1]}')
|
||||||
|
return info
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
# if self._repr is not None:
|
||||||
|
# return self._repr
|
||||||
|
info = self._repr_info()
|
||||||
|
return '<{}>'.format(' '.join(info))
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
if self.cb.mask & CANCELLED_MASK == 0:
|
||||||
|
self.cb.mask |= CANCELLED_MASK
|
||||||
|
# if self._loop.get_debug():
|
||||||
|
# # Keep a representation in debug mode to keep callback and
|
||||||
|
# # parameters. For example, to log the warning
|
||||||
|
# # "Executing <Handle...> took 2.5 second"
|
||||||
|
# self._repr = repr(self)
|
||||||
|
self.callback = None
|
||||||
|
self.args = None
|
||||||
|
|
||||||
|
def cancelled(self):
|
||||||
|
return self.cb.mask & CANCELLED_MASK == 1
|
||||||
|
|
||||||
|
cdef run(self):
|
||||||
|
try:
|
||||||
|
self.context.run(self.callback, *self.args)
|
||||||
|
except (SystemExit, KeyboardInterrupt):
|
||||||
|
raise
|
||||||
|
except BaseException as exc:
|
||||||
|
cb = _format_callback_source(self.callback, self.args)
|
||||||
|
msg = f'Exception in callback {cb}'
|
||||||
|
context = {
|
||||||
|
'message': msg,
|
||||||
|
'exception': exc,
|
||||||
|
'handle': self,
|
||||||
|
}
|
||||||
|
if self.source_traceback:
|
||||||
|
context['source_traceback'] = self.source_traceback
|
||||||
|
self.loop.call_exception_handler(context)
|
||||||
|
self = None # Needed to break cycles when an exception occurs.
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TimerHandle(Handle):
|
||||||
|
"""Object returned by timed callback registration methods."""
|
||||||
|
|
||||||
|
def __init__(self, when, callback, args, loop, context=None):
|
||||||
|
assert when is not None
|
||||||
|
super().__init__(callback, args, loop, context)
|
||||||
|
if self.source_traceback:
|
||||||
|
del self.source_traceback[-1]
|
||||||
|
self.cb.when = when
|
||||||
|
|
||||||
|
# def _repr_info(self):
|
||||||
|
# info = super()._repr_info()
|
||||||
|
# pos = 2 if self._cancelled else 1
|
||||||
|
# info.insert(pos, f'when={self._when}')
|
||||||
|
# return info
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
if self.cb.mask & (CANCELLED_MASK | SCHEDULED_MASK) == SCHEDULED_MASK:
|
||||||
|
self.loop.loop.timer_cancelled_count += 1
|
||||||
|
super().cancel()
|
||||||
|
|
||||||
|
def when(self):
|
||||||
|
return self.cb.when
|
||||||
|
|
||||||
|
|
||||||
|
def _get_function_source(func):
|
||||||
|
func = inspect.unwrap(func)
|
||||||
|
if inspect.isfunction(func):
|
||||||
|
code = func.__code__
|
||||||
|
return (code.co_filename, code.co_firstlineno)
|
||||||
|
if isinstance(func, functools.partial):
|
||||||
|
return _get_function_source(func.func)
|
||||||
|
if isinstance(func, functools.partialmethod):
|
||||||
|
return _get_function_source(func.func)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _format_callback_source(func, args):
|
||||||
|
func_repr = _format_callback(func, args, None)
|
||||||
|
source = _get_function_source(func)
|
||||||
|
if source:
|
||||||
|
func_repr += f' at {source[0]}:{source[1]}'
|
||||||
|
return func_repr
|
||||||
|
|
||||||
|
|
||||||
|
def _format_args_and_kwargs(args, kwargs):
|
||||||
|
"""Format function arguments and keyword arguments.
|
||||||
|
|
||||||
|
Special case for a single parameter: ('hello',) is formatted as ('hello').
|
||||||
|
"""
|
||||||
|
# use reprlib to limit the length of the output
|
||||||
|
items = []
|
||||||
|
if args:
|
||||||
|
items.extend(reprlib.repr(arg) for arg in args)
|
||||||
|
if kwargs:
|
||||||
|
items.extend(f'{k}={reprlib.repr(v)}' for k, v in kwargs.items())
|
||||||
|
return '({})'.format(', '.join(items))
|
||||||
|
|
||||||
|
|
||||||
|
def _format_callback(func, args, kwargs, suffix=''):
|
||||||
|
if isinstance(func, functools.partial):
|
||||||
|
suffix = _format_args_and_kwargs(args, kwargs) + suffix
|
||||||
|
return _format_callback(func.func, func.args, func.keywords, suffix)
|
||||||
|
|
||||||
|
if hasattr(func, '__qualname__') and func.__qualname__:
|
||||||
|
func_repr = func.__qualname__
|
||||||
|
elif hasattr(func, '__name__') and func.__name__:
|
||||||
|
func_repr = func.__name__
|
||||||
|
else:
|
||||||
|
func_repr = repr(func)
|
||||||
|
|
||||||
|
func_repr += _format_args_and_kwargs(args, kwargs)
|
||||||
|
if suffix:
|
||||||
|
func_repr += suffix
|
||||||
|
return func_repr
|
15
src/kloop/heapq.pxd
Normal file
15
src/kloop/heapq.pxd
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct HeapQueue:
|
||||||
|
Callback** array
|
||||||
|
int size
|
||||||
|
int tail
|
184
src/kloop/heapq.pyx
Normal file
184
src/kloop/heapq.pyx
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef int HEAP_BLOCK_SIZE = 1024
|
||||||
|
|
||||||
|
|
||||||
|
cdef void siftup(HeapQueue* heap, int pos, int endpos) nogil:
|
||||||
|
cdef:
|
||||||
|
int childpos, limit = endpos >> 1
|
||||||
|
Callback** array = heap.array
|
||||||
|
|
||||||
|
while pos < limit:
|
||||||
|
childpos = 2 * pos + 1
|
||||||
|
if childpos + 1 < endpos:
|
||||||
|
if array[childpos].when >= array[childpos + 1].when:
|
||||||
|
childpos += 1
|
||||||
|
array[childpos], array[pos] = array[pos], array[childpos]
|
||||||
|
pos = childpos
|
||||||
|
siftdown(heap, pos, endpos)
|
||||||
|
|
||||||
|
|
||||||
|
cdef void siftdown(HeapQueue* heap, int pos, int size) nogil:
|
||||||
|
cdef:
|
||||||
|
int parentpos
|
||||||
|
Callback** array = heap.array
|
||||||
|
long long new_when = array[pos].when
|
||||||
|
while pos > 0:
|
||||||
|
parentpos = (pos - 1) >> 1
|
||||||
|
if new_when >= array[parentpos].when:
|
||||||
|
break
|
||||||
|
new_when = array[pos].when
|
||||||
|
array[pos], array[parentpos] = array[parentpos], array[pos]
|
||||||
|
pos = parentpos
|
||||||
|
|
||||||
|
|
||||||
|
cdef int heapq_init(HeapQueue* heap) nogil except 0:
|
||||||
|
heap.array = <Callback**>PyMem_RawMalloc(
|
||||||
|
sizeof(Callback*) * HEAP_BLOCK_SIZE
|
||||||
|
)
|
||||||
|
if heap.array == NULL:
|
||||||
|
with gil:
|
||||||
|
raise MemoryError
|
||||||
|
heap.size = HEAP_BLOCK_SIZE
|
||||||
|
heap.tail = 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef void heapq_uninit(HeapQueue* heap) nogil:
|
||||||
|
cdef:
|
||||||
|
int i = 0, tail = heap.tail
|
||||||
|
Callback** array = heap.array
|
||||||
|
|
||||||
|
if array == NULL:
|
||||||
|
return
|
||||||
|
if i < tail:
|
||||||
|
with gil:
|
||||||
|
while i < tail:
|
||||||
|
Py_DECREF(<object>array[i].handle)
|
||||||
|
PyMem_RawFree(array)
|
||||||
|
heap.array = NULL
|
||||||
|
|
||||||
|
|
||||||
|
cdef heapq_push_py(HeapQueue* heap, Handle handle):
|
||||||
|
cdef Callback* callback = &handle.cb
|
||||||
|
Py_INCREF(handle)
|
||||||
|
with nogil:
|
||||||
|
heapq_push(heap, callback, 1)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int heapq_push(
|
||||||
|
HeapQueue* heap, Callback* callback, int keep
|
||||||
|
) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
int size = heap.size, tail = heap.tail
|
||||||
|
Callback** array = heap.array
|
||||||
|
|
||||||
|
if tail == size:
|
||||||
|
size += HEAP_BLOCK_SIZE
|
||||||
|
array = <Callback**>PyMem_RawRealloc(array, sizeof(Callback*) * size)
|
||||||
|
if array == NULL:
|
||||||
|
with gil:
|
||||||
|
raise MemoryError
|
||||||
|
heap.size = size
|
||||||
|
array[tail] = callback
|
||||||
|
size = heap.tail = tail + 1
|
||||||
|
if keep:
|
||||||
|
siftdown(heap, tail, size)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef Handle heapq_pop_py(HeapQueue* heap):
|
||||||
|
cdef:
|
||||||
|
Handle handle
|
||||||
|
Callback* callback
|
||||||
|
|
||||||
|
with nogil:
|
||||||
|
callback = heapq_pop(heap)
|
||||||
|
if callback == NULL:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
handle = <Handle>callback.handle
|
||||||
|
Py_DECREF(handle)
|
||||||
|
return handle
|
||||||
|
|
||||||
|
|
||||||
|
cdef Callback* heapq_pop(HeapQueue* heap) nogil:
|
||||||
|
cdef:
|
||||||
|
Callback* rv
|
||||||
|
Callback** array = heap.array
|
||||||
|
int size = heap.size, tail = heap.tail
|
||||||
|
|
||||||
|
if tail == 0:
|
||||||
|
return NULL
|
||||||
|
|
||||||
|
tail = heap.tail = tail - 1
|
||||||
|
rv = array[tail]
|
||||||
|
|
||||||
|
if tail == 0:
|
||||||
|
if size > HEAP_BLOCK_SIZE:
|
||||||
|
size = HEAP_BLOCK_SIZE
|
||||||
|
if PyMem_RawRealloc(array, sizeof(Callback*) * size) != NULL:
|
||||||
|
heap.size = size
|
||||||
|
return rv
|
||||||
|
|
||||||
|
rv, array[0] = array[0], rv
|
||||||
|
if tail > 1:
|
||||||
|
siftup(heap, 0, tail)
|
||||||
|
if size - tail >= HEAP_BLOCK_SIZE * 2:
|
||||||
|
size -= HEAP_BLOCK_SIZE
|
||||||
|
if PyMem_RawRealloc(array, sizeof(Callback*) * size) != NULL:
|
||||||
|
heap.size = size
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline int keep_top_bit(int n) nogil:
|
||||||
|
cdef int i = 0
|
||||||
|
while n > 1:
|
||||||
|
n >>= 1
|
||||||
|
i += 1
|
||||||
|
return n << i
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline void heapq_cache_friendly_heapify(HeapQueue* heap, int tail) nogil:
|
||||||
|
cdef:
|
||||||
|
int m = tail >> 1, mhalf = m >> 1
|
||||||
|
int leftmost = keep_top_bit(m + 1) - 1
|
||||||
|
int i = leftmost - 1, j
|
||||||
|
|
||||||
|
while i >= mhalf:
|
||||||
|
j = i
|
||||||
|
while True:
|
||||||
|
siftup(heap, j, tail)
|
||||||
|
if not (j & 1):
|
||||||
|
break
|
||||||
|
j >>= 1
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
i = m - 1
|
||||||
|
while i >= leftmost:
|
||||||
|
j = i
|
||||||
|
while True:
|
||||||
|
siftup(heap, j, tail)
|
||||||
|
if not (j & 1):
|
||||||
|
break
|
||||||
|
j >>= 1
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef void heapq_heapify(HeapQueue* heap) nogil:
|
||||||
|
cdef int tail = heap.tail, i = (tail >> 1) - 1
|
||||||
|
if tail > 2500:
|
||||||
|
heapq_cache_friendly_heapify(heap, tail)
|
||||||
|
else:
|
||||||
|
while i >= 0:
|
||||||
|
siftup(heap, i, tail)
|
||||||
|
i -= 1
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -7,4 +7,3 @@
|
||||||
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
27
src/kloop/includes/atomic.pxd
Normal file
27
src/kloop/includes/atomic.pxd
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "<stdatomic.h>" nogil:
|
||||||
|
ctypedef enum MemoryOrder "memory_order":
|
||||||
|
relaxed "memory_order_relaxed"
|
||||||
|
acquire "memory_order_acquire"
|
||||||
|
release "memory_order_release"
|
||||||
|
seq_cst "memory_order_seq_cst"
|
||||||
|
|
||||||
|
ctypedef unsigned uint "atomic_uint"
|
||||||
|
|
||||||
|
unsigned load_explicit "atomic_load_explicit" (
|
||||||
|
uint* object, MemoryOrder order
|
||||||
|
)
|
||||||
|
void store_explicit "atomic_store_explicit" (
|
||||||
|
uint* object, unsigned desired, MemoryOrder order
|
||||||
|
)
|
||||||
|
void thread_fence "atomic_thread_fence" (MemoryOrder order)
|
79
src/kloop/includes/libc.pxd
Normal file
79
src/kloop/includes/libc.pxd
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "sys/syscall.h" nogil:
|
||||||
|
int SYS_io_uring_setup
|
||||||
|
int SYS_io_uring_enter
|
||||||
|
int SYS_io_uring_register
|
||||||
|
|
||||||
|
cdef extern from "unistd.h" nogil:
|
||||||
|
int syscall(int number, ...)
|
||||||
|
|
||||||
|
cdef extern from "signal.h" nogil:
|
||||||
|
int _NSIG
|
||||||
|
|
||||||
|
cdef extern from "sys/socket.h" nogil:
|
||||||
|
enum:
|
||||||
|
SOCK_DGRAM
|
||||||
|
SOCK_STREAM
|
||||||
|
|
||||||
|
ctypedef int socklen_t
|
||||||
|
ctypedef int sa_family_t
|
||||||
|
ctypedef int in_port_t
|
||||||
|
|
||||||
|
int SOL_TLS
|
||||||
|
|
||||||
|
struct sockaddr:
|
||||||
|
sa_family_t sa_family
|
||||||
|
|
||||||
|
struct msghdr:
|
||||||
|
void* msg_name # Optional address
|
||||||
|
socklen_t msg_namelen # Size of address
|
||||||
|
iovec* msg_iov # Scatter/gather array
|
||||||
|
size_t msg_iovlen # Number of elements in msg_iov
|
||||||
|
void* msg_control # ancillary data, see below
|
||||||
|
size_t msg_controllen # ancillary data buffer len
|
||||||
|
int msg_flags # flags on received message
|
||||||
|
|
||||||
|
struct cmsghdr:
|
||||||
|
socklen_t cmsg_len # data byte count, including header
|
||||||
|
int cmsg_level # originating protocol
|
||||||
|
int cmsg_type # protocol-specific type
|
||||||
|
|
||||||
|
size_t CMSG_LEN(size_t length)
|
||||||
|
cmsghdr* CMSG_FIRSTHDR(msghdr* msgh)
|
||||||
|
unsigned char* CMSG_DATA(cmsghdr* cmsg)
|
||||||
|
size_t CMSG_SPACE(size_t length)
|
||||||
|
|
||||||
|
int socket(int domain, int type, int protocol)
|
||||||
|
int setsockopt(
|
||||||
|
int socket,
|
||||||
|
int level,
|
||||||
|
int option_name,
|
||||||
|
const void* option_value,
|
||||||
|
socklen_t option_len,
|
||||||
|
)
|
||||||
|
int bind(int sockfd, const sockaddr* addr, socklen_t addrlen)
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "arpa/inet.h" nogil:
|
||||||
|
int inet_pton(int af, char* src, void* dst)
|
||||||
|
int htons(short p)
|
||||||
|
|
||||||
|
cdef extern from "sys/uio.h" nogil:
|
||||||
|
struct iovec:
|
||||||
|
void* iov_base
|
||||||
|
size_t iov_len
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "<errno.h>" nogil:
|
||||||
|
enum:
|
||||||
|
ECANCELED
|
135
src/kloop/includes/linux.pxd
Normal file
135
src/kloop/includes/linux.pxd
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/fs.h" nogil:
|
||||||
|
ctypedef int __kernel_rwf_t
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/types.h" nogil:
|
||||||
|
ctypedef int __u8
|
||||||
|
ctypedef int __u16
|
||||||
|
ctypedef int __u64
|
||||||
|
ctypedef int __u32
|
||||||
|
ctypedef int __s32
|
||||||
|
ctypedef int __kernel_time64_t
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/time_types.h" nogil:
|
||||||
|
struct __kernel_timespec:
|
||||||
|
__kernel_time64_t tv_sec
|
||||||
|
long long tv_nsec
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/tcp.h" nogil:
|
||||||
|
int TCP_ULP
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/tls.h" nogil:
|
||||||
|
int TLS_GET_RECORD_TYPE
|
||||||
|
|
||||||
|
__u16 TLS_CIPHER_AES_GCM_256
|
||||||
|
int TLS_CIPHER_AES_GCM_256_IV_SIZE
|
||||||
|
int TLS_CIPHER_AES_GCM_256_SALT_SIZE
|
||||||
|
int TLS_CIPHER_AES_GCM_256_KEY_SIZE
|
||||||
|
int TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE
|
||||||
|
int TLS_TX
|
||||||
|
int TLS_RX
|
||||||
|
|
||||||
|
struct tls_crypto_info:
|
||||||
|
__u16 version
|
||||||
|
__u16 cipher_type
|
||||||
|
|
||||||
|
struct tls12_crypto_info_aes_gcm_256:
|
||||||
|
tls_crypto_info info
|
||||||
|
unsigned char* iv
|
||||||
|
unsigned char* key
|
||||||
|
unsigned char* salt
|
||||||
|
unsigned char* rec_seq
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "linux/io_uring.h" nogil:
|
||||||
|
unsigned IORING_SETUP_SQPOLL
|
||||||
|
unsigned IORING_SETUP_SQ_AFF
|
||||||
|
|
||||||
|
unsigned IORING_ENTER_GETEVENTS
|
||||||
|
unsigned IORING_ENTER_SQ_WAKEUP
|
||||||
|
unsigned IORING_ENTER_EXT_ARG
|
||||||
|
|
||||||
|
unsigned IORING_SQ_NEED_WAKEUP
|
||||||
|
unsigned IORING_SQ_CQ_OVERFLOW
|
||||||
|
|
||||||
|
unsigned long long IORING_OFF_SQ_RING
|
||||||
|
unsigned long long IORING_OFF_SQES
|
||||||
|
|
||||||
|
unsigned IORING_TIMEOUT_ABS
|
||||||
|
|
||||||
|
unsigned IOSQE_IO_LINK
|
||||||
|
|
||||||
|
enum Operation:
|
||||||
|
IORING_OP_NOP
|
||||||
|
IORING_OP_CONNECT
|
||||||
|
IORING_OP_SEND
|
||||||
|
IORING_OP_SENDMSG
|
||||||
|
IORING_OP_RECV
|
||||||
|
IORING_OP_RECVMSG
|
||||||
|
IORING_OP_OPENAT
|
||||||
|
IORING_OP_READ
|
||||||
|
IORING_OP_CLOSE
|
||||||
|
|
||||||
|
struct io_sqring_offsets:
|
||||||
|
__u32 head
|
||||||
|
__u32 tail
|
||||||
|
__u32 ring_mask
|
||||||
|
__u32 ring_entries
|
||||||
|
__u32 flags
|
||||||
|
__u32 dropped
|
||||||
|
__u32 array
|
||||||
|
|
||||||
|
struct io_cqring_offsets:
|
||||||
|
__u32 head
|
||||||
|
__u32 tail
|
||||||
|
__u32 ring_mask
|
||||||
|
__u32 ring_entries
|
||||||
|
__u32 overflow
|
||||||
|
__u32 cqes
|
||||||
|
__u32 flags
|
||||||
|
|
||||||
|
struct io_uring_params:
|
||||||
|
__u32 flags
|
||||||
|
__u32 sq_thread_cpu
|
||||||
|
__u32 sq_thread_idle
|
||||||
|
|
||||||
|
# written by the kernel:
|
||||||
|
__u32 sq_entries
|
||||||
|
__u32 cq_entries
|
||||||
|
__u32 features
|
||||||
|
__u32 resv[4]
|
||||||
|
io_sqring_offsets sq_off
|
||||||
|
io_cqring_offsets cq_off
|
||||||
|
|
||||||
|
struct io_uring_sqe:
|
||||||
|
__u8 opcode # type of operation for this sqe
|
||||||
|
__s32 fd # file descriptor to do IO on
|
||||||
|
__u64 off # offset into file
|
||||||
|
__u64 addr # pointer to buffer or iovecs
|
||||||
|
__u32 len # buffer size or number of iovecs
|
||||||
|
__u64 user_data # data to be passed back at completion time
|
||||||
|
__u8 flags # IOSQE_ flags
|
||||||
|
__u32 open_flags
|
||||||
|
|
||||||
|
struct io_uring_cqe:
|
||||||
|
__u64 user_data # data to be passed back at completion time
|
||||||
|
__s32 res # result code for this event
|
||||||
|
|
||||||
|
struct io_uring_getevents_arg:
|
||||||
|
__u64 sigmask
|
||||||
|
__u32 sigmask_sz
|
||||||
|
__u64 ts
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -7,4 +7,3 @@
|
||||||
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
94
src/kloop/includes/openssl/bio.pxd
Normal file
94
src/kloop/includes/openssl/bio.pxd
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "openssl/bio.h" nogil:
|
||||||
|
enum BIOCtrl:
|
||||||
|
BIO_CTRL_RESET # 1 opt - rewind/zero etc
|
||||||
|
BIO_CTRL_EOF # 2 opt - are we at the eof
|
||||||
|
BIO_CTRL_INFO # 3 opt - extra tit-bits
|
||||||
|
BIO_CTRL_SET # 4 man - set the 'IO' type
|
||||||
|
BIO_CTRL_GET # 5 man - get the 'IO' type
|
||||||
|
BIO_CTRL_PUSH # 6 opt - internal, used to signify change
|
||||||
|
BIO_CTRL_POP # 7 opt - internal, used to signify change
|
||||||
|
BIO_CTRL_GET_CLOSE # 8 man - set the 'close' on free
|
||||||
|
BIO_CTRL_SET_CLOSE # 9 man - set the 'close' on free
|
||||||
|
BIO_CTRL_PENDING # 10 opt - is their more data buffered
|
||||||
|
BIO_CTRL_FLUSH # 11 opt - 'flush' buffered output
|
||||||
|
BIO_CTRL_DUP # 12 man - extra stuff for 'duped' BIO
|
||||||
|
BIO_CTRL_WPENDING # 13 opt - number of bytes still to write
|
||||||
|
BIO_CTRL_SET_CALLBACK # 14 opt - set callback function
|
||||||
|
BIO_CTRL_GET_CALLBACK # 15 opt - set callback function
|
||||||
|
|
||||||
|
ctypedef struct Method "BIO_METHOD":
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef struct BIO:
|
||||||
|
pass
|
||||||
|
|
||||||
|
int get_new_index "BIO_get_new_index" ()
|
||||||
|
|
||||||
|
Method* meth_new "BIO_meth_new" (int type, const char* name)
|
||||||
|
|
||||||
|
int meth_set_write_ex "BIO_meth_set_write_ex" (
|
||||||
|
Method* biom,
|
||||||
|
int (*bwrite)(BIO*, const char*, size_t, size_t*),
|
||||||
|
)
|
||||||
|
int meth_set_write "BIO_meth_set_write" (
|
||||||
|
Method* biom,
|
||||||
|
int (*write)(BIO*, const char*, int),
|
||||||
|
)
|
||||||
|
|
||||||
|
int meth_set_read_ex "BIO_meth_set_read_ex" (
|
||||||
|
Method* biom,
|
||||||
|
int (*bread)(BIO*, char*, size_t, size_t*),
|
||||||
|
)
|
||||||
|
int meth_set_read "BIO_meth_set_read"(
|
||||||
|
Method* biom,
|
||||||
|
int (*read)(BIO*, char*, int),
|
||||||
|
)
|
||||||
|
|
||||||
|
int meth_set_ctrl "BIO_meth_set_ctrl" (
|
||||||
|
Method* biom, long (*ctrl)(BIO*, int, long, void*)
|
||||||
|
)
|
||||||
|
|
||||||
|
int meth_set_create "BIO_meth_set_create" (
|
||||||
|
Method* biom,
|
||||||
|
int (*create)(BIO*),
|
||||||
|
)
|
||||||
|
|
||||||
|
int meth_set_destroy "BIO_meth_set_destroy" (
|
||||||
|
Method* biom,
|
||||||
|
int (*destroy)(BIO*),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctypedef int info_cb "BIO_info_cb" (BIO*, int, int)
|
||||||
|
int meth_set_callback_ctrl "BIO_meth_set_callback_ctrl" (
|
||||||
|
Method* biom,
|
||||||
|
long (*callback_ctrl)(BIO*, int, info_cb*),
|
||||||
|
)
|
||||||
|
|
||||||
|
BIO* new "BIO_new" (const Method* type)
|
||||||
|
int up_ref "BIO_up_ref" (BIO* a)
|
||||||
|
int free "BIO_free" (BIO* a)
|
||||||
|
|
||||||
|
void set_data "BIO_set_data" (BIO* a, void* ptr)
|
||||||
|
void* get_data "BIO_get_data" (BIO* a)
|
||||||
|
void set_init "BIO_set_init" (BIO* a, int init)
|
||||||
|
void set_shutdown "BIO_set_shutdown" (BIO* a, int shut)
|
||||||
|
|
||||||
|
void set_retry_read "BIO_set_retry_read" (BIO* b)
|
||||||
|
void set_retry_write "BIO_set_retry_write" (BIO* b)
|
||||||
|
void clear_retry_flags "BIO_clear_retry_flags" (BIO *b)
|
||||||
|
|
||||||
|
cdef int FLAGS_IN_EOF "BIO_FLAGS_IN_EOF"
|
||||||
|
|
||||||
|
int test_flags "BIO_test_flags" (BIO* b, int flags)
|
||||||
|
void set_flags "BIO_set_flags" (BIO *b, int flags)
|
14
src/kloop/includes/openssl/err.pxd
Normal file
14
src/kloop/includes/openssl/err.pxd
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "openssl/err.h" nogil:
|
||||||
|
unsigned long get_error "ERR_get_error" ()
|
||||||
|
const char* reason_error_string "ERR_reason_error_string" (unsigned long e)
|
43
src/kloop/includes/openssl/ssl.pxd
Normal file
43
src/kloop/includes/openssl/ssl.pxd
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
from .. cimport linux
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "openssl/ssl.h" nogil:
|
||||||
|
ctypedef struct SSL:
|
||||||
|
pass
|
||||||
|
|
||||||
|
int SSL3_RT_APPLICATION_DATA
|
||||||
|
int OP_ENABLE_KTLS "SSL_OP_ENABLE_KTLS"
|
||||||
|
int set_options "SSL_set_options" (SSL* ssl, int options)
|
||||||
|
|
||||||
|
long SSL_MODE_RELEASE_BUFFERS
|
||||||
|
long SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
|
||||||
|
long clear_mode "SSL_clear_mode" (SSL* ssl, long mode)
|
||||||
|
int free_buffers "SSL_free_buffers" (SSL *ssl)
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from *:
|
||||||
|
"""
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
struct tls12_crypto_info_aes_gcm_128 gcm128;
|
||||||
|
struct tls12_crypto_info_aes_gcm_256 gcm256;
|
||||||
|
struct tls12_crypto_info_aes_ccm_128 ccm128;
|
||||||
|
struct tls12_crypto_info_chacha20_poly1305 chacha20poly1305;
|
||||||
|
};
|
||||||
|
size_t tls_crypto_info_len;
|
||||||
|
} ktls_crypto_info_t;
|
||||||
|
"""
|
||||||
|
|
||||||
|
ctypedef struct ktls_crypto_info_t:
|
||||||
|
size_t tls_crypto_info_len
|
34
src/kloop/includes/pyssl.pxd
Normal file
34
src/kloop/includes/pyssl.pxd
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
from .openssl.ssl cimport SSL
|
||||||
|
from .openssl.bio cimport BIO
|
||||||
|
|
||||||
|
cdef extern from *:
|
||||||
|
"""
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *Socket; /* weakref to socket on which we're layered */
|
||||||
|
SSL *ssl;
|
||||||
|
} PySSLSocket;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
BIO *bio;
|
||||||
|
int eof_written;
|
||||||
|
} PySSLMemoryBIO;
|
||||||
|
"""
|
||||||
|
|
||||||
|
ctypedef struct PySSLSocket:
|
||||||
|
SSL* ssl
|
||||||
|
|
||||||
|
ctypedef struct PySSLMemoryBIO:
|
||||||
|
BIO* bio
|
56
src/kloop/loop.pxd
Normal file
56
src/kloop/loop.pxd
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
from cpython cimport PyErr_SetFromErrno
|
||||||
|
from cpython cimport PyMem_RawMalloc, PyMem_RawFree, PyMem_RawRealloc
|
||||||
|
from cpython cimport PyObject, Py_INCREF, Py_DECREF
|
||||||
|
from libc cimport errno, string
|
||||||
|
from posix cimport mman, unistd, time
|
||||||
|
from posix.types cimport mode_t
|
||||||
|
|
||||||
|
from .includes cimport atomic, libc, linux
|
||||||
|
|
||||||
|
include "./handle.pxd"
|
||||||
|
include "./queue.pxd"
|
||||||
|
include "./heapq.pxd"
|
||||||
|
include "./uring.pxd"
|
||||||
|
include "./tcp.pxd"
|
||||||
|
include "./udp.pxd"
|
||||||
|
include "./fileio.pxd"
|
||||||
|
include "./resolver.pxd"
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct Loop:
|
||||||
|
bint stopping
|
||||||
|
Ring ring
|
||||||
|
HeapQueue scheduled
|
||||||
|
Queue ready
|
||||||
|
int timer_cancelled_count
|
||||||
|
PyObject* loop
|
||||||
|
CResolver resolver
|
||||||
|
|
||||||
|
|
||||||
|
cdef class KLoopImpl:
|
||||||
|
cdef:
|
||||||
|
bint closed
|
||||||
|
object thread_id
|
||||||
|
Loop loop
|
||||||
|
Resolver resolver
|
||||||
|
|
||||||
|
cpdef create_future(self)
|
||||||
|
cdef inline check_closed(self)
|
||||||
|
cdef inline bint _is_running(self)
|
||||||
|
cdef inline check_running(self)
|
||||||
|
cdef inline Handle _call_soon(self, callback, args, context)
|
||||||
|
cdef inline _add_callback(self, Handle handle)
|
||||||
|
cdef inline TimerHandle _call_at(
|
||||||
|
self, long long when, callback, args, context
|
||||||
|
)
|
560
src/kloop/loop.pyx
Normal file
560
src/kloop/loop.pyx
Normal file
|
@ -0,0 +1,560 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
import time as py_time
|
||||||
|
import asyncio
|
||||||
|
import contextvars
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import reprlib
|
||||||
|
import threading
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
cdef asyncio_isfuture = asyncio.isfuture
|
||||||
|
cdef asyncio_ensure_future = asyncio.ensure_future
|
||||||
|
cdef asyncio_set_running_loop = asyncio._set_running_loop
|
||||||
|
cdef asyncio_get_running_loop = asyncio._get_running_loop
|
||||||
|
cdef asyncio_Task = asyncio.Task
|
||||||
|
cdef asyncio_Future = asyncio.Future
|
||||||
|
cdef logger = asyncio.log.logger
|
||||||
|
|
||||||
|
cdef long long SECOND_NS = 1_000_000_000
|
||||||
|
cdef long long MAX_SELECT_TIMEOUT = 24 * 3600 * SECOND_NS
|
||||||
|
|
||||||
|
# Minimum number of scheduled timer handles before cleanup of
|
||||||
|
# cancelled handles is performed.
|
||||||
|
cdef int MIN_SCHEDULED_TIMER_HANDLES = 100
|
||||||
|
|
||||||
|
# Maximum ratio of cancelled handles is performed of scheduled timer handles
|
||||||
|
# that are cancelled before cleanup
|
||||||
|
cdef int MAX_CANCELLED_TIMER_HANDLES_RATIO = 2
|
||||||
|
|
||||||
|
include "handle.pyx"
|
||||||
|
include "queue.pyx"
|
||||||
|
include "heapq.pyx"
|
||||||
|
include "uring.pyx"
|
||||||
|
include "tcp.pyx"
|
||||||
|
include "udp.pyx"
|
||||||
|
include "fileio.pyx"
|
||||||
|
include "resolver.pyx"
|
||||||
|
|
||||||
|
|
||||||
|
cdef long long monotonic_ns() nogil except -1:
|
||||||
|
cdef:
|
||||||
|
long long rv
|
||||||
|
time.timespec ts
|
||||||
|
if time.clock_gettime(time.CLOCK_MONOTONIC, &ts):
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(OSError)
|
||||||
|
return -1
|
||||||
|
rv = ts.tv_sec * SECOND_NS
|
||||||
|
return rv + ts.tv_nsec
|
||||||
|
|
||||||
|
|
||||||
|
cdef int loop_init(
|
||||||
|
Loop* loop, linux.__u32 depth, linux.io_uring_params* params
|
||||||
|
) nogil except 0:
|
||||||
|
if not queue_init(&loop.ready):
|
||||||
|
return 0
|
||||||
|
if not heapq_init(&loop.scheduled):
|
||||||
|
queue_uninit(&loop.ready)
|
||||||
|
return 0
|
||||||
|
if not ring_init(&loop.ring, depth, params):
|
||||||
|
queue_uninit(&loop.ready)
|
||||||
|
heapq_uninit(&loop.scheduled)
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int loop_uninit(Loop* loop) nogil except 0:
|
||||||
|
heapq_uninit(&loop.scheduled)
|
||||||
|
queue_uninit(&loop.ready)
|
||||||
|
return ring_uninit(&loop.ring)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int loop_run_forever(Loop* loop) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
Ring* ring = &loop.ring
|
||||||
|
Queue* ready = &loop.ready
|
||||||
|
HeapQueue* scheduled = &loop.scheduled
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if not loop_run_once(loop, ring, ready, scheduled):
|
||||||
|
return 0
|
||||||
|
if loop.stopping:
|
||||||
|
break
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline int filter_cancelled_calls(Loop* loop) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
HeapQueue* scheduled = &loop.scheduled
|
||||||
|
HeapQueue heap, drop
|
||||||
|
Callback** array = scheduled.array
|
||||||
|
Callback* callback
|
||||||
|
int i = 0, size = scheduled.tail
|
||||||
|
|
||||||
|
if (
|
||||||
|
MIN_SCHEDULED_TIMER_HANDLES < size <
|
||||||
|
loop.timer_cancelled_count * MAX_CANCELLED_TIMER_HANDLES_RATIO
|
||||||
|
):
|
||||||
|
# Remove delayed calls that were cancelled if their number
|
||||||
|
# is too high
|
||||||
|
if not heapq_init(&drop):
|
||||||
|
return 0
|
||||||
|
if not heapq_init(&heap):
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
return 0
|
||||||
|
while i < size:
|
||||||
|
callback = array[i]
|
||||||
|
if callback.mask & CANCELLED_MASK:
|
||||||
|
callback.mask &= ~SCHEDULED_MASK
|
||||||
|
if not heapq_push(&drop, callback, 0):
|
||||||
|
heap.tail = 0
|
||||||
|
heapq_uninit(&heap)
|
||||||
|
drop.tail = 0
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
return 0
|
||||||
|
elif not heapq_push(&heap, callback, 0):
|
||||||
|
heap.tail = 0
|
||||||
|
heapq_uninit(&heap)
|
||||||
|
drop.tail = 0
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
return 0
|
||||||
|
heapq_heapify(&heap)
|
||||||
|
heap, scheduled[0] = scheduled[0], heap
|
||||||
|
heap.tail = 0
|
||||||
|
heapq_uninit(&heap)
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
elif array[0].mask & CANCELLED_MASK:
|
||||||
|
if not heapq_init(&drop):
|
||||||
|
return 0
|
||||||
|
while size:
|
||||||
|
callback = heapq_pop(scheduled)
|
||||||
|
if callback.mask & CANCELLED_MASK:
|
||||||
|
loop.timer_cancelled_count -= 1
|
||||||
|
callback.mask &= ~SCHEDULED_MASK
|
||||||
|
if not heapq_push(&drop, callback, 0):
|
||||||
|
with gil:
|
||||||
|
Py_DECREF(<object>callback.handle)
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
return 0
|
||||||
|
if not array[0].mask & CANCELLED_MASK:
|
||||||
|
break
|
||||||
|
size -= 1
|
||||||
|
heapq_uninit(&drop)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef loop_run_ready(Queue* ready, int ntodo):
|
||||||
|
cdef Handle handle
|
||||||
|
|
||||||
|
while ntodo:
|
||||||
|
handle = queue_pop_py(ready)
|
||||||
|
if not handle.cb.mask & CANCELLED_MASK:
|
||||||
|
handle.run()
|
||||||
|
ntodo -= 1
|
||||||
|
handle = None
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline int loop_run_once(
|
||||||
|
Loop* loop, Ring* ring, Queue* ready, HeapQueue* scheduled
|
||||||
|
) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
Callback* callback
|
||||||
|
long long timeout = -1, now
|
||||||
|
int nready
|
||||||
|
RingCallback* cb = NULL
|
||||||
|
|
||||||
|
if scheduled.tail:
|
||||||
|
if not filter_cancelled_calls(loop):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if ready.head >= 0 or loop.stopping:
|
||||||
|
timeout = 0
|
||||||
|
elif scheduled.tail:
|
||||||
|
timeout = min(
|
||||||
|
max(0, scheduled.array[0].when - monotonic_ns()),
|
||||||
|
MAX_SELECT_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
nready = ring_select(ring, timeout)
|
||||||
|
if nready < 0:
|
||||||
|
return 0
|
||||||
|
while nready:
|
||||||
|
ring_cq_pop(&ring.cq, &cb)
|
||||||
|
if cb != NULL and not cb.callback(cb):
|
||||||
|
return 0
|
||||||
|
nready -= 1
|
||||||
|
|
||||||
|
now = monotonic_ns() + 1
|
||||||
|
while scheduled.tail and scheduled.array[0].when < now:
|
||||||
|
callback = heapq_pop(scheduled)
|
||||||
|
callback.mask &= ~SCHEDULED_MASK
|
||||||
|
if not queue_push(ready, callback):
|
||||||
|
if not heapq_push(scheduled, callback, 1):
|
||||||
|
with gil:
|
||||||
|
Py_DECREF(<object>callback.handle)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if ready.head >= 0:
|
||||||
|
with gil:
|
||||||
|
loop_run_ready(ready, queue_size(ready))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
class KLoopPolicy(asyncio.events.BaseDefaultEventLoopPolicy):
|
||||||
|
__slots__ = ("_selector_args",)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, queue_depth=128, sq_thread_idle=2000, sq_thread_cpu=None
|
||||||
|
):
|
||||||
|
super().__init__()
|
||||||
|
self._selector_args = (queue_depth, sq_thread_idle, sq_thread_cpu)
|
||||||
|
|
||||||
|
def _loop_factory(self):
|
||||||
|
return KLoop(*self._selector_args)
|
||||||
|
|
||||||
|
# Child processes handling (Unix only).
|
||||||
|
|
||||||
|
def get_child_watcher(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def set_child_watcher(self, watcher):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
cdef class KLoopImpl:
|
||||||
|
def __init__(self, queue_depth, sq_thread_idle, sq_thread_cpu):
|
||||||
|
cdef:
|
||||||
|
linux.io_uring_params params
|
||||||
|
linux.__u32 depth
|
||||||
|
string.memset(¶ms, 0, sizeof(params))
|
||||||
|
params.flags = linux.IORING_SETUP_SQPOLL
|
||||||
|
params.sq_thread_idle = sq_thread_idle
|
||||||
|
if sq_thread_cpu is not None:
|
||||||
|
params.sq_thread_cpu = sq_thread_cpu
|
||||||
|
params.flags |= linux.IORING_SETUP_SQ_AFF
|
||||||
|
depth = queue_depth
|
||||||
|
self.loop.loop = <PyObject*>self
|
||||||
|
with nogil:
|
||||||
|
loop_init(&self.loop, depth, ¶ms)
|
||||||
|
|
||||||
|
self.resolver = Resolver.new(self)
|
||||||
|
self.closed = False
|
||||||
|
self.thread_id = None
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
with nogil:
|
||||||
|
loop_uninit(&self.loop)
|
||||||
|
|
||||||
|
cdef inline check_closed(self):
|
||||||
|
if self.closed:
|
||||||
|
raise RuntimeError('Event loop is closed')
|
||||||
|
|
||||||
|
cdef inline bint _is_running(self):
|
||||||
|
return self.thread_id is not None
|
||||||
|
|
||||||
|
cdef inline check_running(self):
|
||||||
|
if self._is_running():
|
||||||
|
raise RuntimeError('This event loop is already running')
|
||||||
|
if asyncio_get_running_loop() is not None:
|
||||||
|
raise RuntimeError(
|
||||||
|
'Cannot run the event loop while another loop is running')
|
||||||
|
|
||||||
|
def run_forever(self):
|
||||||
|
"""Run until stop() is called."""
|
||||||
|
self.check_closed()
|
||||||
|
self.check_running()
|
||||||
|
# self._set_coroutine_origin_tracking(self._debug)
|
||||||
|
self.thread_id = threading.get_ident()
|
||||||
|
|
||||||
|
# old_agen_hooks = sys.get_asyncgen_hooks()
|
||||||
|
# sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
|
||||||
|
# finalizer=self._asyncgen_finalizer_hook)
|
||||||
|
try:
|
||||||
|
asyncio_set_running_loop(self)
|
||||||
|
with nogil:
|
||||||
|
loop_run_forever(&self.loop)
|
||||||
|
finally:
|
||||||
|
self.loop.stopping = 0
|
||||||
|
self.thread_id = None
|
||||||
|
asyncio_set_running_loop(None)
|
||||||
|
# self._set_coroutine_origin_tracking(False)
|
||||||
|
# sys.set_asyncgen_hooks(*old_agen_hooks)
|
||||||
|
|
||||||
|
def run_until_complete(self, future):
|
||||||
|
self.check_closed()
|
||||||
|
self.check_running()
|
||||||
|
|
||||||
|
new_task = not asyncio_isfuture(future)
|
||||||
|
future = asyncio_ensure_future(future, loop=self)
|
||||||
|
if new_task:
|
||||||
|
# An exception is raised if the future didn't complete, so there
|
||||||
|
# is no need to log the "destroy pending task" message
|
||||||
|
future._log_destroy_pending = False
|
||||||
|
|
||||||
|
future.add_done_callback(_run_until_complete_cb)
|
||||||
|
try:
|
||||||
|
self.run_forever()
|
||||||
|
except:
|
||||||
|
if new_task and future.done() and not future.cancelled():
|
||||||
|
# The coroutine raised a BaseException. Consume the exception
|
||||||
|
# to not log a warning, the caller doesn't have access to the
|
||||||
|
# local task.
|
||||||
|
future.exception()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
future.remove_done_callback(_run_until_complete_cb)
|
||||||
|
if not future.done():
|
||||||
|
raise RuntimeError('Event loop stopped before Future completed.')
|
||||||
|
|
||||||
|
return future.result()
|
||||||
|
|
||||||
|
def create_task(self, coro, *, name=None):
|
||||||
|
self.check_closed()
|
||||||
|
# if self._task_factory is None:
|
||||||
|
task = asyncio_Task(coro, loop=self, name=name)
|
||||||
|
if task._source_traceback:
|
||||||
|
del task._source_traceback[-1]
|
||||||
|
# else:
|
||||||
|
# task = self._task_factory(self, coro)
|
||||||
|
# tasks._set_task_name(task, name)
|
||||||
|
|
||||||
|
return task
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.loop.stopping = 1
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.is_running():
|
||||||
|
raise RuntimeError("Cannot close a running event loop")
|
||||||
|
if self.closed:
|
||||||
|
return
|
||||||
|
# if self._debug:
|
||||||
|
# logger.debug("Close %r", self)
|
||||||
|
self.closed = True
|
||||||
|
# self.ready.clear()
|
||||||
|
# self._scheduled.clear()
|
||||||
|
# self._executor_shutdown_called = True
|
||||||
|
# executor = self._default_executor
|
||||||
|
# if executor is not None:
|
||||||
|
# self._default_executor = None
|
||||||
|
# executor.shutdown(wait=False)
|
||||||
|
|
||||||
|
def fileno(self):
|
||||||
|
return self.loop.ring.ring_fd
|
||||||
|
|
||||||
|
def is_running(self):
|
||||||
|
return self._is_running()
|
||||||
|
|
||||||
|
def get_debug(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def call_soon(self, callback, *args, context=None):
|
||||||
|
cdef Handle handle
|
||||||
|
self.check_closed()
|
||||||
|
# if self._debug:
|
||||||
|
# self._check_thread()
|
||||||
|
# self._check_callback(callback, 'call_soon')
|
||||||
|
handle = self._call_soon(callback, args, context)
|
||||||
|
if handle.source_traceback:
|
||||||
|
del handle.source_traceback[-1]
|
||||||
|
return handle
|
||||||
|
|
||||||
|
def time(self):
|
||||||
|
return (<float>monotonic_ns()) / SECOND_NS
|
||||||
|
|
||||||
|
def call_later(self, delay, callback, *args, context=None):
|
||||||
|
cdef long long when = monotonic_ns()
|
||||||
|
when += delay * SECOND_NS
|
||||||
|
timer = self._call_at(when, callback, args, context)
|
||||||
|
if timer.source_traceback:
|
||||||
|
del timer.source_traceback[-1]
|
||||||
|
return timer
|
||||||
|
|
||||||
|
def call_at(self, when, callback, *args, context=None):
|
||||||
|
timer = self._call_at(when * SECOND_NS, callback, args, context)
|
||||||
|
if timer.source_traceback:
|
||||||
|
del timer.source_traceback[-1]
|
||||||
|
return timer
|
||||||
|
|
||||||
|
cdef inline TimerHandle _call_at(
|
||||||
|
self, long long when, callback, args, context
|
||||||
|
):
|
||||||
|
cdef TimerHandle timer
|
||||||
|
self.check_closed()
|
||||||
|
# if self._debug:
|
||||||
|
# self._check_thread()
|
||||||
|
# self._check_callback(callback, 'call_at')
|
||||||
|
timer = TimerHandle(when, callback, args, self, context)
|
||||||
|
heapq_push_py(&self.loop.scheduled, timer)
|
||||||
|
# else:
|
||||||
|
# heapq_heappush(self.heapq)
|
||||||
|
timer.cb.mask |= SCHEDULED_MASK
|
||||||
|
return timer
|
||||||
|
|
||||||
|
cdef inline Handle _call_soon(self, callback, args, context):
|
||||||
|
cdef Handle handle = Handle(callback, args, self, context)
|
||||||
|
self._add_callback(handle)
|
||||||
|
return handle
|
||||||
|
|
||||||
|
cdef inline _add_callback(self, Handle handle):
|
||||||
|
queue_push_py(&self.loop.ready, handle)
|
||||||
|
|
||||||
|
def default_exception_handler(self, context):
|
||||||
|
message = context.get('message')
|
||||||
|
if not message:
|
||||||
|
message = 'Unhandled exception in event loop'
|
||||||
|
|
||||||
|
exception = context.get('exception')
|
||||||
|
if exception is not None:
|
||||||
|
exc_info = (type(exception), exception, exception.__traceback__)
|
||||||
|
else:
|
||||||
|
exc_info = False
|
||||||
|
|
||||||
|
# if ('source_traceback' not in context and
|
||||||
|
# self._current_handle is not None and
|
||||||
|
# self._current_handle._source_traceback):
|
||||||
|
# context['handle_traceback'] = \
|
||||||
|
# self._current_handle._source_traceback
|
||||||
|
|
||||||
|
log_lines = [message]
|
||||||
|
for key in sorted(context):
|
||||||
|
if key in {'message', 'exception'}:
|
||||||
|
continue
|
||||||
|
value = context[key]
|
||||||
|
if key == 'source_traceback':
|
||||||
|
tb = ''.join(traceback.format_list(value))
|
||||||
|
value = 'Object created at (most recent call last):\n'
|
||||||
|
value += tb.rstrip()
|
||||||
|
elif key == 'handle_traceback':
|
||||||
|
tb = ''.join(traceback.format_list(value))
|
||||||
|
value = 'Handle created at (most recent call last):\n'
|
||||||
|
value += tb.rstrip()
|
||||||
|
else:
|
||||||
|
value = repr(value)
|
||||||
|
log_lines.append(f'{key}: {value}')
|
||||||
|
|
||||||
|
logger.error('\n'.join(log_lines), exc_info=exc_info)
|
||||||
|
|
||||||
|
def call_exception_handler(self, context):
|
||||||
|
# if self._exception_handler is None:
|
||||||
|
try:
|
||||||
|
self.default_exception_handler(context)
|
||||||
|
except (SystemExit, KeyboardInterrupt):
|
||||||
|
raise
|
||||||
|
except BaseException:
|
||||||
|
# Second protection layer for unexpected errors
|
||||||
|
# in the default implementation, as well as for subclassed
|
||||||
|
# event loops with overloaded "default_exception_handler".
|
||||||
|
logger.error('Exception in default exception handler',
|
||||||
|
exc_info=True)
|
||||||
|
# else:
|
||||||
|
# try:
|
||||||
|
# self._exception_handler(self, context)
|
||||||
|
# except (SystemExit, KeyboardInterrupt):
|
||||||
|
# raise
|
||||||
|
# except BaseException as exc:
|
||||||
|
# # Exception in the user set custom exception handler.
|
||||||
|
# try:
|
||||||
|
# # Let's try default handler.
|
||||||
|
# self.default_exception_handler({
|
||||||
|
# 'message': 'Unhandled error in exception handler',
|
||||||
|
# 'exception': exc,
|
||||||
|
# 'context': context,
|
||||||
|
# })
|
||||||
|
# except (SystemExit, KeyboardInterrupt):
|
||||||
|
# raise
|
||||||
|
# except BaseException:
|
||||||
|
# # Guard 'default_exception_handler' in case it is
|
||||||
|
# # overloaded.
|
||||||
|
# logger.error('Exception in default exception handler '
|
||||||
|
# 'while handling an unexpected error '
|
||||||
|
# 'in custom exception handler',
|
||||||
|
# exc_info=True)
|
||||||
|
|
||||||
|
async def shutdown_asyncgens(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def shutdown_default_executor(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
cpdef create_future(self):
|
||||||
|
return asyncio_Future(loop=self)
|
||||||
|
|
||||||
|
def _timer_handle_cancelled(self, handle):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def create_connection(
|
||||||
|
self,
|
||||||
|
protocol_factory,
|
||||||
|
host=None,
|
||||||
|
port=None,
|
||||||
|
*,
|
||||||
|
ssl=None,
|
||||||
|
family=0,
|
||||||
|
proto=0,
|
||||||
|
flags=0,
|
||||||
|
sock=None,
|
||||||
|
local_addr=None,
|
||||||
|
server_hostname=None,
|
||||||
|
ssl_handshake_timeout=None,
|
||||||
|
happy_eyeballs_delay=None,
|
||||||
|
interleave=None,
|
||||||
|
):
|
||||||
|
cdef:
|
||||||
|
int fd
|
||||||
|
|
||||||
|
if ssl is False:
|
||||||
|
ssl = None
|
||||||
|
elif ssl is not None:
|
||||||
|
from . import tls
|
||||||
|
if ssl is True:
|
||||||
|
import ssl as ssl_mod
|
||||||
|
ssl = ssl_mod.create_default_context()
|
||||||
|
fd = await tcp_connect(self, host, port)
|
||||||
|
protocol = protocol_factory()
|
||||||
|
if ssl is not None:
|
||||||
|
waiter = self.create_future()
|
||||||
|
transport = tls.TLSTransport.new(fd, protocol, self, ssl, waiter=waiter)
|
||||||
|
await waiter
|
||||||
|
else:
|
||||||
|
transport = TCPTransport.new(fd, protocol, self)
|
||||||
|
return transport, protocol
|
||||||
|
|
||||||
|
|
||||||
|
class KLoop(KLoopImpl, asyncio.AbstractEventLoop):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _run_until_complete_cb(fut):
|
||||||
|
if not fut.cancelled():
|
||||||
|
exc = fut.exception()
|
||||||
|
if isinstance(exc, (SystemExit, KeyboardInterrupt)):
|
||||||
|
# Issue #22429: run_forever() already finished, no need to
|
||||||
|
# stop it.
|
||||||
|
return
|
||||||
|
_get_loop(fut).stop()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_loop(fut):
|
||||||
|
# Tries to call Future.get_loop() if it's available.
|
||||||
|
# Otherwise fallbacks to using the old '_loop' property.
|
||||||
|
try:
|
||||||
|
get_loop = fut.get_loop
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return get_loop()
|
||||||
|
return fut._loop
|
16
src/kloop/queue.pxd
Normal file
16
src/kloop/queue.pxd
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct Queue:
|
||||||
|
Callback** array
|
||||||
|
int size
|
||||||
|
int head
|
||||||
|
int tail
|
165
src/kloop/queue.pyx
Normal file
165
src/kloop/queue.pyx
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef int QUEUE_BLOCK_SIZE = 1024
|
||||||
|
|
||||||
|
|
||||||
|
cdef int queue_init(Queue* queue) nogil except 0:
|
||||||
|
queue.array = <Callback**>PyMem_RawMalloc(
|
||||||
|
sizeof(Callback*) * QUEUE_BLOCK_SIZE
|
||||||
|
)
|
||||||
|
if queue.array == NULL:
|
||||||
|
with gil:
|
||||||
|
raise MemoryError
|
||||||
|
queue.head = -1
|
||||||
|
queue.tail = 0
|
||||||
|
queue.size = QUEUE_BLOCK_SIZE
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef void queue_uninit(Queue* queue) nogil:
|
||||||
|
cdef:
|
||||||
|
int i = queue.head, size = queue.size, tail = queue.tail
|
||||||
|
Callback** array = queue.array
|
||||||
|
|
||||||
|
if array == NULL:
|
||||||
|
return
|
||||||
|
if i >= 0:
|
||||||
|
with gil:
|
||||||
|
while True:
|
||||||
|
Py_DECREF(<object>array[i].handle)
|
||||||
|
i = (i + 1) % size
|
||||||
|
if i == tail:
|
||||||
|
break
|
||||||
|
PyMem_RawFree(array)
|
||||||
|
queue.array = NULL
|
||||||
|
|
||||||
|
|
||||||
|
cdef queue_push_py(Queue* queue, Handle handle):
|
||||||
|
cdef Callback* callback = &handle.cb
|
||||||
|
Py_INCREF(handle)
|
||||||
|
with nogil:
|
||||||
|
queue_push(queue, callback)
|
||||||
|
|
||||||
|
|
||||||
|
cdef int queue_push(Queue* queue, Callback* callback) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
Callback** orig = queue.array
|
||||||
|
Callback** array = orig
|
||||||
|
int size = queue.size, head = queue.head, tail = queue.tail
|
||||||
|
|
||||||
|
if head == tail:
|
||||||
|
if head == 0:
|
||||||
|
tail = size
|
||||||
|
size += QUEUE_BLOCK_SIZE
|
||||||
|
array = <Callback**>PyMem_RawRealloc(
|
||||||
|
orig, sizeof(Callback*) * size
|
||||||
|
)
|
||||||
|
if array == NULL:
|
||||||
|
with gil:
|
||||||
|
raise MemoryError
|
||||||
|
else:
|
||||||
|
tail = size + QUEUE_BLOCK_SIZE
|
||||||
|
array = <Callback**>PyMem_RawMalloc(sizeof(Callback*) * tail)
|
||||||
|
if array == NULL:
|
||||||
|
with gil:
|
||||||
|
raise MemoryError
|
||||||
|
queue.array = array
|
||||||
|
string.memcpy(
|
||||||
|
array, orig + head, sizeof(Callback*) * (size - head)
|
||||||
|
)
|
||||||
|
string.memcpy(array + size - head, orig, sizeof(Callback*) * head)
|
||||||
|
size, tail = tail, size
|
||||||
|
queue.head = head = 0
|
||||||
|
PyMem_RawFree(orig)
|
||||||
|
queue.size = size
|
||||||
|
elif head < 0:
|
||||||
|
queue.head = head = 0
|
||||||
|
array[tail] = callback
|
||||||
|
queue.tail = (tail + 1) % size
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef Handle queue_pop_py(Queue* queue):
|
||||||
|
cdef:
|
||||||
|
Handle handle
|
||||||
|
Callback* callback
|
||||||
|
|
||||||
|
with nogil:
|
||||||
|
callback = queue_pop(queue)
|
||||||
|
if callback == NULL:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
handle = <Handle>callback.handle
|
||||||
|
Py_DECREF(handle)
|
||||||
|
return handle
|
||||||
|
|
||||||
|
|
||||||
|
cdef Callback* queue_pop(Queue* queue) nogil:
|
||||||
|
cdef:
|
||||||
|
int size = queue.size, head = queue.head, tail = queue.tail
|
||||||
|
Callback* rv
|
||||||
|
Callback** orig = queue.array
|
||||||
|
Callback** array = orig
|
||||||
|
|
||||||
|
if head < 0:
|
||||||
|
return NULL
|
||||||
|
rv = array[head]
|
||||||
|
queue.head = head = (head + 1) % size
|
||||||
|
if head == tail:
|
||||||
|
queue.head = -1
|
||||||
|
queue.tail = 0
|
||||||
|
if size > QUEUE_BLOCK_SIZE:
|
||||||
|
size = QUEUE_BLOCK_SIZE
|
||||||
|
if PyMem_RawRealloc(
|
||||||
|
array, sizeof(Callback*) * size
|
||||||
|
) != NULL:
|
||||||
|
queue.size = size
|
||||||
|
elif (head - tail) % size >= QUEUE_BLOCK_SIZE * 2:
|
||||||
|
if head < tail:
|
||||||
|
size -= QUEUE_BLOCK_SIZE
|
||||||
|
if tail > size:
|
||||||
|
tail -= head
|
||||||
|
string.memmove(array, array + head, sizeof(Callback*) * tail)
|
||||||
|
queue.tail = tail
|
||||||
|
queue.head = 0
|
||||||
|
if PyMem_RawRealloc(
|
||||||
|
array, sizeof(Callback*) * size
|
||||||
|
) != NULL:
|
||||||
|
queue.size = size
|
||||||
|
queue.tail = tail % size
|
||||||
|
else:
|
||||||
|
array = <Callback**>PyMem_RawMalloc(
|
||||||
|
sizeof(Callback*) * (size - QUEUE_BLOCK_SIZE)
|
||||||
|
)
|
||||||
|
if array != NULL:
|
||||||
|
string.memcpy(
|
||||||
|
array, orig + head, sizeof(Callback*) * (size - head)
|
||||||
|
)
|
||||||
|
string.memcpy(
|
||||||
|
array + size - head, orig, sizeof(Callback*) * tail
|
||||||
|
)
|
||||||
|
queue.array = array
|
||||||
|
queue.head = 0
|
||||||
|
queue.tail = (tail - head) % size
|
||||||
|
queue.size = size - QUEUE_BLOCK_SIZE
|
||||||
|
PyMem_RawFree(orig)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
cdef int queue_size(Queue* queue) nogil:
|
||||||
|
cdef int size = queue.size, head = queue.head, tail = queue.tail
|
||||||
|
if head < 0:
|
||||||
|
return 0
|
||||||
|
elif head == tail:
|
||||||
|
return size
|
||||||
|
else:
|
||||||
|
return (tail - head) % size
|
75
src/kloop/resolver.pxd
Normal file
75
src/kloop/resolver.pxd
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from * nogil:
|
||||||
|
int resolver_init(
|
||||||
|
CResolver* resolver,
|
||||||
|
char* resolv_conf_data,
|
||||||
|
size_t resolv_conf_data_size,
|
||||||
|
char* hosts_conf_data,
|
||||||
|
size_t hosts_conf_data_size,
|
||||||
|
)
|
||||||
|
int resolver_lookup_ip(
|
||||||
|
void* resolver,
|
||||||
|
void* resolve,
|
||||||
|
char* host,
|
||||||
|
size_t length,
|
||||||
|
libc.in_port_t port,
|
||||||
|
)
|
||||||
|
void resolver_run_until_stalled(void* rust_resolver)
|
||||||
|
void waker_wake(void* waker)
|
||||||
|
void waker_forget(void* waker)
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct CResolver:
|
||||||
|
Loop* loop
|
||||||
|
Callback* cb
|
||||||
|
FileReader resolv_conf
|
||||||
|
FileReader hosts_conf
|
||||||
|
int res
|
||||||
|
void* rust_resolver
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resolver:
|
||||||
|
cdef:
|
||||||
|
CResolver resolver
|
||||||
|
KLoopImpl loop
|
||||||
|
Handle handle
|
||||||
|
object waiter
|
||||||
|
bint initialized
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef Resolver new(KLoopImpl loop)
|
||||||
|
cdef init_cb(self)
|
||||||
|
cdef err_cb(self, exc)
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct CResolve:
|
||||||
|
CResolver* resolver
|
||||||
|
libc.sockaddr* result
|
||||||
|
size_t result_len, result_size
|
||||||
|
Callback* cb
|
||||||
|
int res
|
||||||
|
char* host
|
||||||
|
size_t host_len
|
||||||
|
libc.in_port_t port
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resolve:
|
||||||
|
cdef:
|
||||||
|
CResolve r
|
||||||
|
Handle handle
|
||||||
|
object waiter
|
||||||
|
object host
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef new(Resolver resolver, host, port)
|
||||||
|
cdef resolve_cb(self)
|
179
src/kloop/resolver.pyx
Normal file
179
src/kloop/resolver.pyx
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef const char* RESOLV_CONF = "/etc/resolv.conf"
|
||||||
|
cdef const char* HOSTS_CONF = "/etc/hosts"
|
||||||
|
cdef size_t SOCKADDR_CHUNK_SIZE = 4
|
||||||
|
|
||||||
|
|
||||||
|
cdef int resolve_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
CResolver* r = <CResolver*>cb.data
|
||||||
|
int rv = 1
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
cdef int resolver_read_file_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef:
|
||||||
|
CResolver* r = <CResolver*>cb.data
|
||||||
|
int rv = 1
|
||||||
|
void* ptr
|
||||||
|
|
||||||
|
if r.hosts_conf.done_cb.res == 0 or r.resolv_conf.done_cb.res == 0:
|
||||||
|
if cb.res < 0:
|
||||||
|
r.resolv_conf.cancelled = r.hosts_conf.cancelled = 1
|
||||||
|
else:
|
||||||
|
r.res = min(r.hosts_conf.done_cb.res, r.resolv_conf.done_cb.res)
|
||||||
|
if r.res > 0:
|
||||||
|
r.res = resolver_init(
|
||||||
|
r,
|
||||||
|
r.resolv_conf.data,
|
||||||
|
r.resolv_conf.offset,
|
||||||
|
r.hosts_conf.data,
|
||||||
|
r.hosts_conf.offset,
|
||||||
|
)
|
||||||
|
rv = queue_push(&r.loop.ready, r.cb)
|
||||||
|
if not file_reader_done(&r.resolv_conf):
|
||||||
|
# TODO: fd not closed?
|
||||||
|
pass
|
||||||
|
if not file_reader_done(&r.hosts_conf):
|
||||||
|
# TODO: fd not closed?
|
||||||
|
pass
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern libc.sockaddr* resolve_prep_addr(CResolve* r) nogil:
|
||||||
|
cdef size_t l = r.result_len, size = r.result_size
|
||||||
|
if l == size:
|
||||||
|
size += SOCKADDR_CHUNK_SIZE
|
||||||
|
if PyMem_RawRealloc(r.result, sizeof(libc.sockaddr) * size) == NULL:
|
||||||
|
return NULL
|
||||||
|
r.result_size = size
|
||||||
|
r.result_len = l + 1
|
||||||
|
return r.result + l
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern int resolve_done_cb(CResolve* r) nogil:
|
||||||
|
return queue_push(&r.resolver.loop.ready, r.cb)
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern void resolver_set(CResolver* resolver, void* rust_resolver) nogil:
|
||||||
|
resolver.rust_resolver = rust_resolver
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resolver:
|
||||||
|
@staticmethod
|
||||||
|
cdef Resolver new(KLoopImpl loop):
|
||||||
|
cdef:
|
||||||
|
Resolver rv = Resolver.__new__(Resolver)
|
||||||
|
CResolver* r = &rv.resolver
|
||||||
|
rv.loop = loop
|
||||||
|
r.loop = &loop.loop
|
||||||
|
r.resolv_conf.done_cb.callback = resolver_read_file_cb
|
||||||
|
r.resolv_conf.done_cb.data = r
|
||||||
|
r.hosts_conf.done_cb.callback = resolver_read_file_cb
|
||||||
|
r.hosts_conf.done_cb.data = r
|
||||||
|
return rv
|
||||||
|
|
||||||
|
async def ensure_initialized(self):
|
||||||
|
cdef CResolver* r
|
||||||
|
|
||||||
|
if self.initialized:
|
||||||
|
return
|
||||||
|
waiter = self.waiter
|
||||||
|
if waiter is None:
|
||||||
|
r = &self.resolver
|
||||||
|
waiter = self.waiter = self.loop.create_future()
|
||||||
|
if not file_reader_start(&r.resolv_conf, r.loop, RESOLV_CONF):
|
||||||
|
self.err_cb(ValueError("Submission queue is full!"))
|
||||||
|
elif not file_reader_start(&r.hosts_conf, r.loop, HOSTS_CONF):
|
||||||
|
r.resolv_conf.cancelled = 1
|
||||||
|
self.err_cb(ValueError("Submission queue is full!"))
|
||||||
|
else:
|
||||||
|
self.handle = Handle(self.init_cb, (self,), self.loop, None)
|
||||||
|
r.cb = &self.handle.cb
|
||||||
|
await waiter
|
||||||
|
|
||||||
|
async def reload_config(self, *, force=False):
|
||||||
|
if self.initialized:
|
||||||
|
waiter = self.waiter
|
||||||
|
if waiter is None:
|
||||||
|
waiter = self.waiter = self.loop.create_future()
|
||||||
|
self.err_cb(NotImplementedError())
|
||||||
|
await waiter
|
||||||
|
else:
|
||||||
|
await self.ensure_initialized()
|
||||||
|
|
||||||
|
cdef init_cb(self):
|
||||||
|
cdef int res = self.resolver.res
|
||||||
|
|
||||||
|
if self.waiter.done():
|
||||||
|
return
|
||||||
|
|
||||||
|
if res < 0:
|
||||||
|
try:
|
||||||
|
errno.errno = -res
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
except IOError as e:
|
||||||
|
self.waiter.set_exception(e)
|
||||||
|
else:
|
||||||
|
self.waiter.set_result(None)
|
||||||
|
|
||||||
|
cdef err_cb(self, exc):
|
||||||
|
waiter, self.waiter = self.waiter, None
|
||||||
|
if waiter is not None:
|
||||||
|
if not waiter.done():
|
||||||
|
waiter.set_exception(exc)
|
||||||
|
|
||||||
|
async def lookup_ip(self, host, port):
|
||||||
|
await self.ensure_initialized()
|
||||||
|
return await Resolve.new(self, host, port)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class Resolve:
|
||||||
|
@staticmethod
|
||||||
|
cdef new(Resolver resolver, host, port):
|
||||||
|
cdef:
|
||||||
|
Resolve rv = Resolve.__new__(Resolve)
|
||||||
|
CResolve* r = &rv.r
|
||||||
|
rv.host = host.encode("utf-8")
|
||||||
|
rv.waiter = resolver.loop.create_future()
|
||||||
|
r.resolver = &resolver.resolver
|
||||||
|
r.host = <char*>rv.host
|
||||||
|
r.host_len = len(rv.host)
|
||||||
|
r.port = port
|
||||||
|
r.result = <libc.sockaddr*>PyMem_RawMalloc(
|
||||||
|
sizeof(libc.sockaddr) * SOCKADDR_CHUNK_SIZE
|
||||||
|
)
|
||||||
|
if r.result == NULL:
|
||||||
|
raise MemoryError
|
||||||
|
r.result_size = SOCKADDR_CHUNK_SIZE
|
||||||
|
rv.handle = Handle(rv.resolve_cb, (rv,), resolver.loop, None)
|
||||||
|
r.cb = &rv.handle.cb
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def __await__(self):
|
||||||
|
cdef CResolve* r = &self.r
|
||||||
|
resolver_lookup_ip(r.resolver.rust_resolver, r, r.host, r.host_len, r.port)
|
||||||
|
return self.waiter.__await__()
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
cdef CResolve* r = &self.r
|
||||||
|
r.host = NULL
|
||||||
|
r.host_len = 0
|
||||||
|
if r.result != NULL:
|
||||||
|
PyMem_RawFree(r.result)
|
||||||
|
r.result = NULL
|
||||||
|
r.result_size = 0
|
||||||
|
|
||||||
|
cdef resolve_cb(self):
|
||||||
|
self.waiter.set_result(self)
|
25
src/kloop/tcp.pxd
Normal file
25
src/kloop/tcp.pxd
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct TCPConnect:
|
||||||
|
RingCallback ring_cb
|
||||||
|
Loop* loop
|
||||||
|
Callback* cb
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TCPTransport:
|
||||||
|
cdef:
|
||||||
|
KLoopImpl loop
|
||||||
|
int fd
|
||||||
|
object protocol
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
cdef TCPTransport new(int fd, object protocol, KLoopImpl loop)
|
78
src/kloop/tcp.pyx
Normal file
78
src/kloop/tcp.pyx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
async def tcp_connect(KLoopImpl loop, host, port):
|
||||||
|
cdef:
|
||||||
|
Resolve resolve
|
||||||
|
TCPConnect connector
|
||||||
|
int fd, res
|
||||||
|
libc.sockaddr * addr
|
||||||
|
Handle handle
|
||||||
|
size_t i
|
||||||
|
|
||||||
|
resolve = await loop.resolver.lookup_ip(host, port)
|
||||||
|
if not resolve.r.result_len:
|
||||||
|
raise RuntimeError(f"Cannot resolve host: {host!r}")
|
||||||
|
|
||||||
|
connector.loop = &loop.loop
|
||||||
|
connector.ring_cb.callback = tcp_connect_cb
|
||||||
|
connector.ring_cb.data = &connector
|
||||||
|
|
||||||
|
exceptions = []
|
||||||
|
for i in range(resolve.r.result_len):
|
||||||
|
addr = resolve.r.result + i
|
||||||
|
fd = libc.socket(addr.sa_family, libc.SOCK_STREAM, 0)
|
||||||
|
if fd == -1:
|
||||||
|
raise IOError("Cannot create socket")
|
||||||
|
|
||||||
|
try:
|
||||||
|
waiter = loop.create_future()
|
||||||
|
handle = Handle(waiter.set_result, (None,), loop, None)
|
||||||
|
connector.cb = &handle.cb
|
||||||
|
|
||||||
|
if not ring_sq_submit_connect(
|
||||||
|
&loop.loop.ring.sq,
|
||||||
|
fd,
|
||||||
|
addr,
|
||||||
|
&connector.ring_cb,
|
||||||
|
):
|
||||||
|
raise ValueError("Submission queue is full!")
|
||||||
|
|
||||||
|
await waiter
|
||||||
|
|
||||||
|
res = abs(connector.ring_cb.res)
|
||||||
|
if res != 0:
|
||||||
|
raise IOError(res, string.strerror(res))
|
||||||
|
return fd
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
os.close(fd)
|
||||||
|
exceptions.append(e)
|
||||||
|
raise exceptions[0]
|
||||||
|
|
||||||
|
|
||||||
|
cdef int tcp_connect_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef TCPConnect* connector = <TCPConnect*>cb.data
|
||||||
|
return queue_push(&connector.loop.ready, connector.cb)
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TCPTransport:
|
||||||
|
@staticmethod
|
||||||
|
cdef TCPTransport new(int fd, object protocol, KLoopImpl loop):
|
||||||
|
cdef TCPTransport rv = TCPTransport.__new__(TCPTransport)
|
||||||
|
rv.fd = fd
|
||||||
|
rv.protocol = protocol
|
||||||
|
rv.loop = loop
|
||||||
|
loop.call_soon(protocol.connection_made, rv)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def get_extra_info(self, x):
|
||||||
|
return None
|
60
src/kloop/tls.pxd
Normal file
60
src/kloop/tls.pxd
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
from cpython cimport PyObject
|
||||||
|
from .includes cimport libc
|
||||||
|
from .includes.openssl cimport bio
|
||||||
|
from .loop cimport KLoopImpl, Loop, RingCallback
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct Proxy:
|
||||||
|
PyObject* transport
|
||||||
|
libc.iovec send_vec
|
||||||
|
libc.msghdr send_msg
|
||||||
|
RingCallback send_callback
|
||||||
|
libc.iovec recv_vec
|
||||||
|
libc.msghdr recv_msg
|
||||||
|
RingCallback recv_callback
|
||||||
|
unsigned char flags
|
||||||
|
char* read_buffer
|
||||||
|
void* cmsg
|
||||||
|
|
||||||
|
Loop* loop
|
||||||
|
int fd
|
||||||
|
|
||||||
|
|
||||||
|
cdef enum State:
|
||||||
|
UNWRAPPED
|
||||||
|
HANDSHAKING
|
||||||
|
WRAPPED
|
||||||
|
WRAPPED_KTLS
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TLSTransport:
|
||||||
|
cdef:
|
||||||
|
KLoopImpl loop
|
||||||
|
int fd
|
||||||
|
bio.BIO* bio
|
||||||
|
object protocol
|
||||||
|
object sslctx
|
||||||
|
object sslobj
|
||||||
|
object waiter
|
||||||
|
Proxy proxy
|
||||||
|
State state
|
||||||
|
object write_buffer
|
||||||
|
bint sending
|
||||||
|
|
||||||
|
|
||||||
|
cdef do_handshake(self)
|
||||||
|
cdef do_read(self)
|
||||||
|
cdef do_read_ktls(self)
|
||||||
|
cdef write_cb(self, int res)
|
||||||
|
cdef read_cb(self, int res)
|
611
src/kloop/tls.pyx
Normal file
611
src/kloop/tls.pyx
Normal file
|
@ -0,0 +1,611 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import socket
|
||||||
|
import ssl
|
||||||
|
from cpython cimport PyMem_RawMalloc, PyMem_RawFree
|
||||||
|
from libc cimport errno, string
|
||||||
|
|
||||||
|
from .includes.openssl cimport err, ssl as ssl_h
|
||||||
|
from .includes cimport pyssl, linux
|
||||||
|
from .loop cimport ring_sq_submit_sendmsg, ring_sq_submit_recvmsg
|
||||||
|
|
||||||
|
|
||||||
|
cdef int BIO_CTRL_SET_KTLS = 72
|
||||||
|
cdef int BIO_CTRL_GET_KTLS_SEND = 73
|
||||||
|
cdef int BIO_CTRL_GET_KTLS_RECV = 76
|
||||||
|
|
||||||
|
cdef int FLAGS_KTLS_TX_CTRL_MSG = 0x1000
|
||||||
|
cdef int FLAGS_KTLS_RX = 0x2000
|
||||||
|
cdef int FLAGS_KTLS_TX = 0x4000
|
||||||
|
|
||||||
|
cdef unsigned char FLAGS_PROXY_SEND_SUBMITTED = 1 << 0
|
||||||
|
cdef unsigned char FLAGS_PROXY_SEND_COMPLETED = 1 << 1
|
||||||
|
cdef unsigned char FLAGS_PROXY_SEND_IN_PROXY = 1 << 2
|
||||||
|
cdef unsigned char FLAGS_PROXY_SEND_ALL = (
|
||||||
|
FLAGS_PROXY_SEND_SUBMITTED |
|
||||||
|
FLAGS_PROXY_SEND_COMPLETED |
|
||||||
|
FLAGS_PROXY_SEND_IN_PROXY
|
||||||
|
)
|
||||||
|
cdef unsigned char FLAGS_PROXY_RECV_SUBMITTED = 1 << 4
|
||||||
|
cdef unsigned char FLAGS_PROXY_RECV_COMPLETED = 1 << 5
|
||||||
|
cdef unsigned char FLAGS_PROXY_RECV_KTLS = 1 << 6
|
||||||
|
cdef unsigned char FLAGS_PROXY_RECV_ALL = (
|
||||||
|
FLAGS_PROXY_RECV_SUBMITTED |
|
||||||
|
FLAGS_PROXY_RECV_COMPLETED
|
||||||
|
)
|
||||||
|
|
||||||
|
cdef size_t CMSG_SIZE = libc.CMSG_SPACE(sizeof(unsigned char))
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline void reset_msg(libc.msghdr* msg, void* cmsg) nogil:
|
||||||
|
msg.msg_name = NULL
|
||||||
|
msg.msg_namelen = 0
|
||||||
|
msg.msg_flags = 0
|
||||||
|
if cmsg == NULL:
|
||||||
|
msg.msg_control = NULL
|
||||||
|
msg.msg_controllen = 0
|
||||||
|
else:
|
||||||
|
msg.msg_control = cmsg
|
||||||
|
msg.msg_controllen = CMSG_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
cdef object fromOpenSSLError(object err_type):
|
||||||
|
cdef:
|
||||||
|
unsigned long e = err.get_error()
|
||||||
|
const char* msg = err.reason_error_string(e)
|
||||||
|
if msg == NULL:
|
||||||
|
return err_type()
|
||||||
|
else:
|
||||||
|
return err_type(msg.decode("ISO-8859-1"))
|
||||||
|
|
||||||
|
|
||||||
|
cdef int bio_write_ex(
|
||||||
|
bio.BIO* b, const char* data, size_t datal, size_t* written
|
||||||
|
) nogil:
|
||||||
|
cdef:
|
||||||
|
Proxy* proxy = <Proxy*>bio.get_data(b)
|
||||||
|
int res
|
||||||
|
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex(data=%x, datal=%d)" % (<long long>data, datal))
|
||||||
|
|
||||||
|
if proxy.flags & FLAGS_PROXY_SEND_SUBMITTED:
|
||||||
|
if proxy.send_vec.iov_base != data:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() error: concurrent call")
|
||||||
|
return 0
|
||||||
|
if proxy.send_vec.iov_len > datal:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() error: short rewrite")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
bio.clear_retry_flags(b)
|
||||||
|
if proxy.flags & FLAGS_PROXY_SEND_COMPLETED:
|
||||||
|
proxy.flags &= ~FLAGS_PROXY_SEND_ALL
|
||||||
|
res = proxy.send_callback.res
|
||||||
|
if res < 0:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() error:", -res)
|
||||||
|
errno.errno = -res
|
||||||
|
return 0
|
||||||
|
|
||||||
|
written[0] = res
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() written:", res)
|
||||||
|
print(">>> ", end="")
|
||||||
|
for i in range(res):
|
||||||
|
print(
|
||||||
|
"%02x " % <unsigned char>data[i],
|
||||||
|
end="" if (i + 1) % 16 or i == res - 1 else "\n>>> ",
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
else:
|
||||||
|
written[0] = 0
|
||||||
|
bio.set_retry_write(b)
|
||||||
|
|
||||||
|
if not proxy.flags & FLAGS_PROXY_SEND_SUBMITTED:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() submit")
|
||||||
|
proxy.send_vec.iov_base = data
|
||||||
|
proxy.send_vec.iov_len = datal
|
||||||
|
reset_msg(&proxy.send_msg, NULL)
|
||||||
|
if not ring_sq_submit_sendmsg(
|
||||||
|
&proxy.loop.ring.sq,
|
||||||
|
proxy.fd,
|
||||||
|
&proxy.send_msg,
|
||||||
|
&proxy.send_callback,
|
||||||
|
):
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_write_ex() error: SQ full")
|
||||||
|
return 0
|
||||||
|
proxy.flags |= FLAGS_PROXY_SEND_SUBMITTED
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int bio_read_ex(
|
||||||
|
bio.BIO* b, char* data, size_t datal, size_t* readbytes
|
||||||
|
) nogil:
|
||||||
|
cdef:
|
||||||
|
Proxy* proxy = <Proxy*>bio.get_data(b)
|
||||||
|
libc.cmsghdr* cmsg = NULL
|
||||||
|
int res
|
||||||
|
int is_ktls = bio.test_flags(b, FLAGS_KTLS_RX)
|
||||||
|
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print('bio_read_ex(data=%x, datal=%d)' % (<long long>data, datal))
|
||||||
|
|
||||||
|
if proxy.flags & FLAGS_PROXY_RECV_SUBMITTED:
|
||||||
|
if proxy.recv_vec.iov_base != (data + 5 if is_ktls else data):
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error: concurrent call")
|
||||||
|
return 0
|
||||||
|
if proxy.recv_vec.iov_len > (datal - 21 if is_ktls else datal):
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error: short reread")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
bio.clear_retry_flags(b)
|
||||||
|
if (
|
||||||
|
proxy.flags & FLAGS_PROXY_RECV_KTLS and
|
||||||
|
proxy.flags & FLAGS_PROXY_RECV_COMPLETED
|
||||||
|
):
|
||||||
|
proxy.flags &= ~FLAGS_PROXY_RECV_ALL
|
||||||
|
res = proxy.recv_callback.res
|
||||||
|
if datal < res + 5:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error: datal too short")
|
||||||
|
errno.errno = errno.EINVAL
|
||||||
|
return 0
|
||||||
|
|
||||||
|
cmsg = libc.CMSG_FIRSTHDR(&proxy.recv_msg)
|
||||||
|
if cmsg.cmsg_type == linux.TLS_GET_RECORD_TYPE:
|
||||||
|
data[0] = (<unsigned char *> libc.CMSG_DATA(cmsg))[0]
|
||||||
|
data[1] = 0x03 # TLS1_2_VERSION_MAJOR
|
||||||
|
data[2] = 0x03 # TLS1_2_VERSION_MINOR
|
||||||
|
# returned length is limited to msg_iov.iov_len above
|
||||||
|
data[3] = (res >> 8) & 0xff
|
||||||
|
data[4] = res & 0xff
|
||||||
|
string.memcpy(data + 5, proxy.read_buffer, res)
|
||||||
|
res += 5
|
||||||
|
else:
|
||||||
|
string.memcpy(data, proxy.read_buffer, res)
|
||||||
|
readbytes[0] = res
|
||||||
|
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() read:", res, "(forwarded TLS record)")
|
||||||
|
print("<<< ", end="")
|
||||||
|
for i in range(res):
|
||||||
|
print(
|
||||||
|
"%02x " % <unsigned char>data[i],
|
||||||
|
end="" if (i + 1) % 16 or i == res - 1 else "\n<<< ",
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
elif proxy.flags & FLAGS_PROXY_RECV_COMPLETED:
|
||||||
|
proxy.flags &= ~FLAGS_PROXY_RECV_ALL
|
||||||
|
res = proxy.recv_callback.res
|
||||||
|
if res < 0:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error:", -res)
|
||||||
|
errno.errno = -res
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if is_ktls:
|
||||||
|
if proxy.recv_msg.msg_controllen:
|
||||||
|
cmsg = libc.CMSG_FIRSTHDR(&proxy.recv_msg)
|
||||||
|
if cmsg.cmsg_type == linux.TLS_GET_RECORD_TYPE:
|
||||||
|
data[0] = (<unsigned char*>libc.CMSG_DATA(cmsg))[0]
|
||||||
|
data[1] = 0x03 # TLS1_2_VERSION_MAJOR
|
||||||
|
data[2] = 0x03 # TLS1_2_VERSION_MINOR
|
||||||
|
# returned length is limited to msg_iov.iov_len above
|
||||||
|
data[3] = (res >> 8) & 0xff
|
||||||
|
data[4] = res & 0xff
|
||||||
|
res += 5
|
||||||
|
|
||||||
|
if res == 0:
|
||||||
|
bio.set_flags(b, bio.FLAGS_IN_EOF)
|
||||||
|
readbytes[0] = res
|
||||||
|
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print(
|
||||||
|
"bio_read_ex() read:", res, "(TLS record)" if cmsg else ""
|
||||||
|
)
|
||||||
|
print("<<< ", end="")
|
||||||
|
for i in range(res):
|
||||||
|
print(
|
||||||
|
"%02x " % <unsigned char>data[i],
|
||||||
|
end="" if (i + 1) % 16 or i == res - 1 else "\n<<< ",
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
else:
|
||||||
|
bio.set_retry_read(b)
|
||||||
|
readbytes[0] = 0
|
||||||
|
if not proxy.flags & FLAGS_PROXY_RECV_SUBMITTED:
|
||||||
|
if proxy.flags & FLAGS_PROXY_RECV_KTLS:
|
||||||
|
reset_msg(&proxy.recv_msg, proxy.cmsg)
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() submit(%x, %d)" % (
|
||||||
|
<long long>proxy.recv_vec.iov_base,
|
||||||
|
proxy.recv_vec.iov_len,
|
||||||
|
))
|
||||||
|
elif is_ktls:
|
||||||
|
if datal < 21:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error: datal too short")
|
||||||
|
errno.errno = errno.EINVAL
|
||||||
|
return 0
|
||||||
|
|
||||||
|
proxy.recv_vec.iov_base = data + 5
|
||||||
|
proxy.recv_vec.iov_len = datal - 21
|
||||||
|
reset_msg(&proxy.recv_msg, proxy.cmsg)
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() submit(%x, %d)" % (
|
||||||
|
<long long>proxy.recv_vec.iov_base,
|
||||||
|
proxy.recv_vec.iov_len,
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
proxy.recv_vec.iov_base = data
|
||||||
|
proxy.recv_vec.iov_len = datal
|
||||||
|
reset_msg(&proxy.recv_msg, NULL)
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() submit")
|
||||||
|
|
||||||
|
if not ring_sq_submit_recvmsg(
|
||||||
|
&proxy.loop.ring.sq,
|
||||||
|
proxy.fd,
|
||||||
|
&proxy.recv_msg,
|
||||||
|
&proxy.recv_callback,
|
||||||
|
):
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("bio_read_ex() error: SQ full")
|
||||||
|
return 0
|
||||||
|
proxy.flags |= FLAGS_PROXY_RECV_SUBMITTED
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef long bio_ctrl(bio.BIO* b, int cmd, long num, void* ptr) nogil:
|
||||||
|
cdef:
|
||||||
|
ssl_h.ktls_crypto_info_t* crypto_info
|
||||||
|
long ret = 0
|
||||||
|
if cmd == bio.BIO_CTRL_EOF:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("BIO_CTRL_EOF", ret)
|
||||||
|
elif cmd == bio.BIO_CTRL_PUSH:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("BIO_CTRL_PUSH", ret)
|
||||||
|
elif cmd == bio.BIO_CTRL_POP:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("BIO_CTRL_POP", ret)
|
||||||
|
elif cmd == bio.BIO_CTRL_FLUSH:
|
||||||
|
ret = 1
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print('BIO_CTRL_FLUSH', ret)
|
||||||
|
elif cmd == BIO_CTRL_SET_KTLS:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print("BIO_CTRL_SET_KTLS", "TX end" if num else "RX end")
|
||||||
|
crypto_info = <ssl_h.ktls_crypto_info_t*>ptr
|
||||||
|
if libc.setsockopt(
|
||||||
|
(<Proxy*>bio.get_data(b)).fd,
|
||||||
|
libc.SOL_TLS,
|
||||||
|
linux.TLS_TX if num else linux.TLS_RX,
|
||||||
|
crypto_info,
|
||||||
|
crypto_info.tls_crypto_info_len,
|
||||||
|
) == 0:
|
||||||
|
bio.set_flags(b, FLAGS_KTLS_TX if num else FLAGS_KTLS_RX)
|
||||||
|
else:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print(
|
||||||
|
"BIO_CTRL_SET_KTLS",
|
||||||
|
"TX end" if num else "RX end",
|
||||||
|
"failed",
|
||||||
|
)
|
||||||
|
elif cmd == BIO_CTRL_GET_KTLS_SEND:
|
||||||
|
return bio.test_flags(b, FLAGS_KTLS_TX) != 0
|
||||||
|
elif cmd == BIO_CTRL_GET_KTLS_RECV:
|
||||||
|
return bio.test_flags(b, FLAGS_KTLS_RX) != 0
|
||||||
|
else:
|
||||||
|
IF DEBUG:
|
||||||
|
with gil:
|
||||||
|
print('bio_ctrl', cmd, num)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
cdef int bio_create(bio.BIO* b) nogil:
|
||||||
|
bio.set_init(b, 1)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int bio_destroy(bio.BIO* b) nogil:
|
||||||
|
bio.set_shutdown(b, 1)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int tls_send_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef Proxy* proxy = <Proxy*>cb.data
|
||||||
|
proxy.flags |= FLAGS_PROXY_SEND_COMPLETED
|
||||||
|
with gil:
|
||||||
|
(<TLSTransport>proxy.transport).write_cb(cb.res)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int tls_recv_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef Proxy* proxy = <Proxy*>cb.data
|
||||||
|
proxy.flags |= FLAGS_PROXY_RECV_COMPLETED
|
||||||
|
with gil:
|
||||||
|
(<TLSTransport>proxy.transport).read_cb(cb.res)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef class TLSTransport:
|
||||||
|
@staticmethod
|
||||||
|
def new(
|
||||||
|
int fd,
|
||||||
|
protocol,
|
||||||
|
KLoopImpl loop,
|
||||||
|
sslctx,
|
||||||
|
server_side=False,
|
||||||
|
server_hostname=None,
|
||||||
|
session=None,
|
||||||
|
waiter=None,
|
||||||
|
):
|
||||||
|
cdef:
|
||||||
|
TLSTransport rv = TLSTransport.__new__(TLSTransport)
|
||||||
|
pyssl.PySSLMemoryBIO* c_bio
|
||||||
|
pyssl.SSL* s
|
||||||
|
|
||||||
|
libc.setsockopt(fd, socket.SOL_TCP, linux.TCP_ULP, b"tls", 3)
|
||||||
|
|
||||||
|
py_bio = ssl.MemoryBIO()
|
||||||
|
c_bio = <pyssl.PySSLMemoryBIO*>py_bio
|
||||||
|
c_bio.bio, rv.bio = rv.bio, c_bio.bio
|
||||||
|
try:
|
||||||
|
rv.sslobj = sslctx.wrap_bio(
|
||||||
|
py_bio, py_bio, server_side, server_hostname, session
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
c_bio.bio, rv.bio = rv.bio, c_bio.bio
|
||||||
|
del py_bio
|
||||||
|
|
||||||
|
s = (<pyssl.PySSLSocket*>rv.sslobj._sslobj).ssl
|
||||||
|
ssl_h.set_options(s, ssl_h.OP_ENABLE_KTLS)
|
||||||
|
ssl_h.clear_mode(
|
||||||
|
s,
|
||||||
|
ssl_h.SSL_MODE_RELEASE_BUFFERS |
|
||||||
|
ssl_h.SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
|
||||||
|
)
|
||||||
|
rv.fd = fd
|
||||||
|
rv.protocol = protocol
|
||||||
|
rv.loop = loop
|
||||||
|
rv.sslctx = sslctx
|
||||||
|
rv.proxy.loop = &loop.loop
|
||||||
|
rv.proxy.fd = fd
|
||||||
|
rv.waiter = waiter
|
||||||
|
rv.write_buffer = collections.deque()
|
||||||
|
|
||||||
|
rv.do_handshake()
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def __cinit__(self):
|
||||||
|
self.state = UNWRAPPED
|
||||||
|
self.bio = bio.new(KTLS_BIO_METHOD)
|
||||||
|
self.proxy.transport = <PyObject*>self
|
||||||
|
self.proxy.send_msg.msg_iov = &self.proxy.send_vec
|
||||||
|
self.proxy.send_msg.msg_iovlen = 1
|
||||||
|
self.proxy.send_callback.data = <void*>&self.proxy
|
||||||
|
self.proxy.send_callback.callback = tls_send_cb
|
||||||
|
self.proxy.cmsg = PyMem_RawMalloc(CMSG_SIZE)
|
||||||
|
if self.proxy.cmsg == NULL:
|
||||||
|
raise MemoryError
|
||||||
|
self.proxy.recv_msg.msg_iov = &self.proxy.recv_vec
|
||||||
|
self.proxy.recv_msg.msg_iovlen = 1
|
||||||
|
self.proxy.recv_callback.data = <void*>&self.proxy
|
||||||
|
self.proxy.recv_callback.callback = tls_recv_cb
|
||||||
|
bio.set_data(self.bio, <void*>&self.proxy)
|
||||||
|
|
||||||
|
def __dealloc__(self):
|
||||||
|
self.sslobj = None
|
||||||
|
bio.free(self.bio)
|
||||||
|
PyMem_RawFree(self.proxy.read_buffer)
|
||||||
|
PyMem_RawFree(self.proxy.cmsg)
|
||||||
|
|
||||||
|
cdef do_handshake(self):
|
||||||
|
if self.state == UNWRAPPED:
|
||||||
|
self.state = HANDSHAKING
|
||||||
|
elif self.state != HANDSHAKING:
|
||||||
|
raise RuntimeError("Cannot do handshake now")
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_handshake()")
|
||||||
|
self.sslobj.do_handshake()
|
||||||
|
except ssl.SSLWantReadError:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_handshake() SSLWantReadError")
|
||||||
|
except ssl.SSLWantWriteError:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_handshake() SSLWantWriteError")
|
||||||
|
except Exception as ex:
|
||||||
|
IF DEBUG:
|
||||||
|
print('do_handshake() error:', ex)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
IF DEBUG:
|
||||||
|
print('do_handshake() done')
|
||||||
|
|
||||||
|
self.state = WRAPPED
|
||||||
|
if self.waiter:
|
||||||
|
self.waiter.set_result(self)
|
||||||
|
self.waiter = None
|
||||||
|
|
||||||
|
if bio.test_flags(self.bio, FLAGS_KTLS_RX):
|
||||||
|
self.proxy.read_buffer = <char*>PyMem_RawMalloc(65536)
|
||||||
|
if self.proxy.read_buffer == NULL:
|
||||||
|
raise MemoryError
|
||||||
|
self.proxy.flags |= FLAGS_PROXY_RECV_KTLS
|
||||||
|
self.proxy.recv_vec.iov_base = self.proxy.read_buffer
|
||||||
|
self.proxy.recv_vec.iov_len = 65536
|
||||||
|
self.do_read_ktls()
|
||||||
|
else:
|
||||||
|
self.do_read()
|
||||||
|
|
||||||
|
cdef do_read_ktls(self):
|
||||||
|
cdef:
|
||||||
|
int res
|
||||||
|
libc.cmsghdr* cmsg
|
||||||
|
unsigned char record_type
|
||||||
|
|
||||||
|
if self.proxy.flags & FLAGS_PROXY_RECV_COMPLETED:
|
||||||
|
self.proxy.flags &= ~FLAGS_PROXY_RECV_ALL
|
||||||
|
res = self.proxy.recv_callback.res
|
||||||
|
if res < 0:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read_ktls() error:", -res)
|
||||||
|
self.loop.call_soon(
|
||||||
|
self.protocol.connection_lost,
|
||||||
|
IOError(-res, string.strerror(-res))
|
||||||
|
)
|
||||||
|
elif res == 0:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read_ktls() EOF")
|
||||||
|
self.loop.call_soon(self.protocol.eof_received)
|
||||||
|
self.loop.call_soon(self.protocol.connection_lost, None)
|
||||||
|
else:
|
||||||
|
if self.proxy.recv_msg.msg_controllen:
|
||||||
|
cmsg = libc.CMSG_FIRSTHDR(&self.proxy.recv_msg)
|
||||||
|
if cmsg.cmsg_type == linux.TLS_GET_RECORD_TYPE:
|
||||||
|
record_type = (<unsigned char*>libc.CMSG_DATA(cmsg))[0]
|
||||||
|
if record_type != ssl_h.SSL3_RT_APPLICATION_DATA:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read_ktls() forward CMSG")
|
||||||
|
self.proxy.flags |= FLAGS_PROXY_RECV_COMPLETED
|
||||||
|
return self.do_read()
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read_ktls() received", res, "bytes")
|
||||||
|
self.loop.call_soon(
|
||||||
|
self.protocol.data_received,
|
||||||
|
bytes(self.proxy.read_buffer[:res]),
|
||||||
|
)
|
||||||
|
self.loop.call_soon(self.do_read_ktls, self)
|
||||||
|
|
||||||
|
elif not self.proxy.flags & FLAGS_PROXY_RECV_SUBMITTED:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read_ktls() submit")
|
||||||
|
reset_msg(&self.proxy.recv_msg, self.proxy.cmsg)
|
||||||
|
if not ring_sq_submit_recvmsg(
|
||||||
|
&self.proxy.loop.ring.sq,
|
||||||
|
self.fd,
|
||||||
|
&self.proxy.recv_msg,
|
||||||
|
&self.proxy.recv_callback,
|
||||||
|
):
|
||||||
|
raise RuntimeError("SQ full")
|
||||||
|
self.proxy.flags |= FLAGS_PROXY_RECV_SUBMITTED
|
||||||
|
|
||||||
|
cdef do_read(self):
|
||||||
|
try:
|
||||||
|
data = self.sslobj.read(65536)
|
||||||
|
except ssl.SSLWantReadError:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read() SSLWantReadError")
|
||||||
|
except ssl.SSLWantWriteError:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read() SSLWantWriteError")
|
||||||
|
except Exception as ex:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read() error:", ex)
|
||||||
|
self.loop.call_soon(self.protocol.connection_lost, ex)
|
||||||
|
else:
|
||||||
|
if data:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read() received", len(data), bytes)
|
||||||
|
print(data)
|
||||||
|
self.loop.call_soon(self.protocol.data_received, data)
|
||||||
|
if self.proxy.flags & FLAGS_PROXY_RECV_KTLS:
|
||||||
|
self.loop.call_soon(self.do_read_ktls, self)
|
||||||
|
else:
|
||||||
|
self.loop.call_soon(self.do_read, self)
|
||||||
|
else:
|
||||||
|
IF DEBUG:
|
||||||
|
print("do_read() EOF")
|
||||||
|
self.loop.call_soon(self.protocol.eof_received)
|
||||||
|
self.loop.call_soon(self.protocol.connection_lost, None)
|
||||||
|
|
||||||
|
cdef write_cb(self, int res):
|
||||||
|
IF DEBUG:
|
||||||
|
print("write_cb", res, "state:", self.state)
|
||||||
|
if self.state == HANDSHAKING:
|
||||||
|
self.do_handshake()
|
||||||
|
|
||||||
|
cdef read_cb(self, int res):
|
||||||
|
IF DEBUG:
|
||||||
|
print("read_cb", res, "state:", self.state)
|
||||||
|
if self.state == HANDSHAKING:
|
||||||
|
self.do_handshake()
|
||||||
|
elif self.state == WRAPPED:
|
||||||
|
if self.proxy.flags & FLAGS_PROXY_RECV_KTLS:
|
||||||
|
self.do_read_ktls()
|
||||||
|
else:
|
||||||
|
self.do_read()
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
if self.sending:
|
||||||
|
self.write_buffer.append(data)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.sslobj.write(data)
|
||||||
|
except ssl.SSLWantWriteError:
|
||||||
|
IF DEBUG:
|
||||||
|
print("write() SSLWantWriteError")
|
||||||
|
|
||||||
|
|
||||||
|
cdef bio.Method* KTLS_BIO_METHOD = bio.meth_new(
|
||||||
|
bio.get_new_index(), "kTLS BIO"
|
||||||
|
)
|
||||||
|
if not bio.meth_set_write_ex(KTLS_BIO_METHOD, bio_write_ex):
|
||||||
|
raise fromOpenSSLError(ImportError)
|
||||||
|
if not bio.meth_set_read_ex(KTLS_BIO_METHOD, bio_read_ex):
|
||||||
|
raise fromOpenSSLError(ImportError)
|
||||||
|
if not bio.meth_set_ctrl(KTLS_BIO_METHOD, bio_ctrl):
|
||||||
|
raise fromOpenSSLError(ImportError)
|
||||||
|
if not bio.meth_set_create(KTLS_BIO_METHOD, bio_create):
|
||||||
|
raise fromOpenSSLError(ImportError)
|
||||||
|
if not bio.meth_set_destroy(KTLS_BIO_METHOD, bio_destroy):
|
||||||
|
raise fromOpenSSLError(ImportError)
|
19
src/kloop/udp.pxd
Normal file
19
src/kloop/udp.pxd
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct UDPAction:
|
||||||
|
int fd
|
||||||
|
libc.iovec vec
|
||||||
|
libc.msghdr msg
|
||||||
|
RingCallback callback
|
||||||
|
CResolver* resolver
|
||||||
|
void* rust_waker
|
||||||
|
libc.sockaddr addr
|
104
src/kloop/udp.pyx
Normal file
104
src/kloop/udp.pyx
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern int udp_bind(libc.sockaddr* addr, libc.socklen_t addrlen) nogil:
|
||||||
|
cdef int fd = libc.socket(addr.sa_family, libc.SOCK_DGRAM, 0)
|
||||||
|
if fd == -1:
|
||||||
|
return -1
|
||||||
|
if libc.bind(fd, addr, addrlen) == -1:
|
||||||
|
# TODO: close fd
|
||||||
|
return -1
|
||||||
|
return fd
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern unsigned long udp_action_init(int fd, CResolver* resolver) nogil:
|
||||||
|
cdef UDPAction* rv
|
||||||
|
rv = <UDPAction*>PyMem_RawMalloc(sizeof(UDPAction))
|
||||||
|
if rv == NULL:
|
||||||
|
return 0
|
||||||
|
string.memset(rv, 0, sizeof(UDPAction))
|
||||||
|
rv.fd = fd
|
||||||
|
rv.resolver = resolver
|
||||||
|
rv.msg.msg_iov = &rv.vec
|
||||||
|
rv.msg.msg_iovlen = 1
|
||||||
|
rv.callback.data = <void*>rv
|
||||||
|
rv.callback.callback = udp_action_cb
|
||||||
|
rv.msg.msg_name = <void*>&rv.addr
|
||||||
|
rv.msg.msg_namelen = sizeof(rv.addr)
|
||||||
|
return <unsigned long>rv
|
||||||
|
|
||||||
|
|
||||||
|
cdef int udp_action_cb(RingCallback* cb) nogil except 0:
|
||||||
|
cdef UDPAction* action = <UDPAction*>cb.data
|
||||||
|
waker_wake(action.rust_waker)
|
||||||
|
resolver_run_until_stalled(action.resolver.rust_resolver)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern int udp_send_poll(
|
||||||
|
unsigned long send_in,
|
||||||
|
const char* data,
|
||||||
|
size_t datalen,
|
||||||
|
libc.sockaddr* addr,
|
||||||
|
libc.socklen_t addrlen,
|
||||||
|
void* waker,
|
||||||
|
) nogil:
|
||||||
|
cdef UDPAction* send = <UDPAction*>send_in
|
||||||
|
if send.vec.iov_base == NULL:
|
||||||
|
send.vec.iov_base = data
|
||||||
|
send.vec.iov_len = datalen
|
||||||
|
send.addr = addr[0]
|
||||||
|
send.msg.msg_namelen = addrlen
|
||||||
|
send.rust_waker = waker
|
||||||
|
return ring_sq_submit_sendmsg(
|
||||||
|
&send.resolver.loop.ring.sq,
|
||||||
|
send.fd,
|
||||||
|
&send.msg,
|
||||||
|
&send.callback,
|
||||||
|
) - 1
|
||||||
|
else:
|
||||||
|
waker_forget(waker)
|
||||||
|
if send.vec.iov_base != data or send.vec.iov_len != datalen:
|
||||||
|
return -1
|
||||||
|
return send.callback.res or -1
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern int udp_recv_poll(
|
||||||
|
unsigned long recv_in,
|
||||||
|
char* data,
|
||||||
|
size_t datalen,
|
||||||
|
void* waker,
|
||||||
|
) nogil:
|
||||||
|
cdef UDPAction* recv = <UDPAction*>recv_in
|
||||||
|
if recv.vec.iov_base == NULL:
|
||||||
|
recv.vec.iov_base = data
|
||||||
|
recv.vec.iov_len = datalen
|
||||||
|
recv.rust_waker = waker
|
||||||
|
return ring_sq_submit_recvmsg(
|
||||||
|
&recv.resolver.loop.ring.sq,
|
||||||
|
recv.fd,
|
||||||
|
&recv.msg,
|
||||||
|
&recv.callback,
|
||||||
|
) - 1
|
||||||
|
else:
|
||||||
|
waker_forget(waker)
|
||||||
|
if recv.vec.iov_base != data or recv.vec.iov_len != datalen:
|
||||||
|
return -1
|
||||||
|
return recv.callback.res or -1
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern libc.sockaddr* udp_get_addr(unsigned long recv_in) nogil:
|
||||||
|
cdef UDPAction* recv = <UDPAction*>recv_in
|
||||||
|
return &recv.addr
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern void udp_action_free(unsigned long action) nogil:
|
||||||
|
PyMem_RawFree(<void*>action)
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -8,3 +8,67 @@
|
||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct SubmissionQueue:
|
||||||
|
unsigned* khead
|
||||||
|
unsigned* ktail
|
||||||
|
unsigned* kring_mask
|
||||||
|
unsigned* kring_entries
|
||||||
|
unsigned* kflags
|
||||||
|
unsigned* kdropped
|
||||||
|
unsigned* array
|
||||||
|
linux.io_uring_sqe* sqes
|
||||||
|
|
||||||
|
unsigned sqe_head
|
||||||
|
unsigned sqe_tail
|
||||||
|
|
||||||
|
size_t ring_size
|
||||||
|
void* ring_ptr
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct CompletionQueue:
|
||||||
|
unsigned* khead
|
||||||
|
unsigned* ktail
|
||||||
|
unsigned* kring_mask
|
||||||
|
unsigned* kring_entries
|
||||||
|
unsigned* kflags
|
||||||
|
unsigned* koverflow
|
||||||
|
linux.io_uring_cqe* cqes
|
||||||
|
|
||||||
|
size_t ring_size
|
||||||
|
void* ring_ptr
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct Ring:
|
||||||
|
SubmissionQueue sq
|
||||||
|
CompletionQueue cq
|
||||||
|
unsigned flags
|
||||||
|
int ring_fd
|
||||||
|
|
||||||
|
unsigned features
|
||||||
|
int enter_ring_fd
|
||||||
|
linux.__u8 int_flags
|
||||||
|
linux.__u8 pad[3]
|
||||||
|
unsigned pad2
|
||||||
|
|
||||||
|
|
||||||
|
cdef struct RingCallback:
|
||||||
|
void* data
|
||||||
|
int res
|
||||||
|
int (*callback)(RingCallback* cb) nogil except 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_sendmsg(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
const libc.msghdr *msg,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_recvmsg(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
const libc.msghdr *msg,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2022 Fantix King http://fantix.pro
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
# kLoop is licensed under Mulan PSL v2.
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
# You may obtain a copy of Mulan PSL v2 at:
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
@ -8,3 +8,327 @@
|
||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
cdef linux.__u32 SIG_SIZE = libc._NSIG // 8
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_init(
|
||||||
|
Ring* ring,
|
||||||
|
linux.__u32 queue_depth,
|
||||||
|
linux.io_uring_params* params
|
||||||
|
) nogil except 0:
|
||||||
|
# SYSCALL: SYS_io_uring_setup
|
||||||
|
ring.ring_fd = ring.enter_ring_fd = libc.syscall(
|
||||||
|
libc.SYS_io_uring_setup, queue_depth, params
|
||||||
|
)
|
||||||
|
if ring.ring_fd < 0:
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# mmap the ring_ptr
|
||||||
|
ring.sq.ring_size = ring.cq.ring_size = max(
|
||||||
|
params.sq_off.array + params.sq_entries * sizeof(unsigned),
|
||||||
|
params.cq_off.cqes + params.cq_entries * sizeof(linux.io_uring_cqe)
|
||||||
|
)
|
||||||
|
ring.sq.ring_ptr = ring.cq.ring_ptr = mman.mmap(
|
||||||
|
NULL,
|
||||||
|
ring.sq.ring_size,
|
||||||
|
mman.PROT_READ | mman.PROT_WRITE,
|
||||||
|
mman.MAP_SHARED | mman.MAP_POPULATE,
|
||||||
|
ring.ring_fd,
|
||||||
|
linux.IORING_OFF_SQ_RING,
|
||||||
|
)
|
||||||
|
if ring.sq.ring_ptr == mman.MAP_FAILED:
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Initialize the SubmissionQueue
|
||||||
|
ring.sq.khead = <unsigned*>(ring.sq.ring_ptr + params.sq_off.head)
|
||||||
|
ring.sq.ktail = <unsigned*>(ring.sq.ring_ptr + params.sq_off.tail)
|
||||||
|
ring.sq.kring_mask = <unsigned*>(ring.sq.ring_ptr + params.sq_off.ring_mask)
|
||||||
|
ring.sq.kring_entries = <unsigned*>(ring.sq.ring_ptr + params.sq_off.ring_entries)
|
||||||
|
ring.sq.kflags = <unsigned*>(ring.sq.ring_ptr + params.sq_off.flags)
|
||||||
|
ring.sq.kdropped = <unsigned*>(ring.sq.ring_ptr + params.sq_off.dropped)
|
||||||
|
ring.sq.array = <unsigned*>(ring.sq.ring_ptr + params.sq_off.array)
|
||||||
|
ring.sq.sqes = <linux.io_uring_sqe*>mman.mmap(
|
||||||
|
NULL,
|
||||||
|
params.sq_entries * sizeof(linux.io_uring_sqe),
|
||||||
|
mman.PROT_READ | mman.PROT_WRITE,
|
||||||
|
mman.MAP_SHARED | mman.MAP_POPULATE,
|
||||||
|
ring.ring_fd,
|
||||||
|
linux.IORING_OFF_SQES,
|
||||||
|
)
|
||||||
|
if ring.sq.sqes == mman.MAP_FAILED:
|
||||||
|
mman.munmap(ring.sq.ring_ptr, ring.sq.ring_size)
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Initialize the CompletionQueue
|
||||||
|
ring.cq.khead = <unsigned*>(ring.cq.ring_ptr + params.cq_off.head)
|
||||||
|
ring.cq.ktail = <unsigned*>(ring.cq.ring_ptr + params.cq_off.tail)
|
||||||
|
ring.cq.kring_mask = <unsigned*>(ring.cq.ring_ptr + params.cq_off.ring_mask)
|
||||||
|
ring.cq.kring_entries = <unsigned*>(ring.cq.ring_ptr + params.cq_off.ring_entries)
|
||||||
|
ring.cq.koverflow = <unsigned*>(ring.cq.ring_ptr + params.cq_off.overflow)
|
||||||
|
ring.cq.cqes = <linux.io_uring_cqe*>(ring.cq.ring_ptr + params.cq_off.cqes)
|
||||||
|
if params.cq_off.flags:
|
||||||
|
ring.cq.kflags = <unsigned*>(ring.cq.ring_ptr + params.cq_off.flags)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_uninit(Ring* ring) nogil except 0:
|
||||||
|
if ring.sq.sqes != NULL:
|
||||||
|
mman.munmap(
|
||||||
|
ring.sq.sqes,
|
||||||
|
ring.sq.kring_entries[0] * sizeof(linux.io_uring_sqe),
|
||||||
|
)
|
||||||
|
if ring.sq.ring_ptr != NULL:
|
||||||
|
mman.munmap(ring.sq.ring_ptr, ring.sq.ring_size)
|
||||||
|
if ring.ring_fd:
|
||||||
|
if unistd.close(ring.ring_fd) != 0:
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline int ring_sq_flush(SubmissionQueue* sq) nogil:
|
||||||
|
cdef:
|
||||||
|
unsigned mask = sq.kring_mask[0]
|
||||||
|
unsigned tail = sq.ktail[0]
|
||||||
|
unsigned to_submit = sq.sqe_tail - sq.sqe_head
|
||||||
|
|
||||||
|
if to_submit:
|
||||||
|
while to_submit:
|
||||||
|
sq.array[tail & mask] = sq.sqe_head & mask
|
||||||
|
tail += 1
|
||||||
|
sq.sqe_head += 1
|
||||||
|
to_submit -= 1
|
||||||
|
atomic.store_explicit(<atomic.uint*>sq.ktail, tail, atomic.release)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_select(Ring* ring, long long timeout) nogil except -1:
|
||||||
|
cdef:
|
||||||
|
int flags = linux.IORING_ENTER_EXT_ARG
|
||||||
|
bint need_enter = 0
|
||||||
|
unsigned ready
|
||||||
|
unsigned wait_nr = 0
|
||||||
|
linux.io_uring_getevents_arg arg
|
||||||
|
linux.__kernel_timespec ts
|
||||||
|
CompletionQueue* cq = &ring.cq
|
||||||
|
SubmissionQueue* sq = &ring.sq
|
||||||
|
|
||||||
|
string.memset(&arg, 0, sizeof(arg))
|
||||||
|
# Call enter if we have no CQE ready and timeout is not 0, or else we
|
||||||
|
# handle the ready CQEs first.
|
||||||
|
ready = atomic.load_explicit(
|
||||||
|
<atomic.uint*>cq.ktail, atomic.acquire
|
||||||
|
) - cq.khead[0]
|
||||||
|
if not ready and timeout != 0:
|
||||||
|
flags |= linux.IORING_ENTER_GETEVENTS
|
||||||
|
if timeout > 0:
|
||||||
|
ts.tv_sec = timeout // SECOND_NS
|
||||||
|
ts.tv_nsec = timeout % SECOND_NS
|
||||||
|
arg.ts = <linux.__u64> &ts
|
||||||
|
wait_nr = 1
|
||||||
|
need_enter = 1
|
||||||
|
|
||||||
|
# Flush the submission queue, and only wakeup the SQ polling thread if
|
||||||
|
# there is something for the kernel to handle.
|
||||||
|
if ring_sq_flush(sq):
|
||||||
|
atomic.thread_fence(atomic.seq_cst)
|
||||||
|
if atomic.load_explicit(
|
||||||
|
<atomic.uint*>sq.kflags, atomic.relaxed
|
||||||
|
) & linux.IORING_SQ_NEED_WAKEUP:
|
||||||
|
arg.ts = 0
|
||||||
|
flags |= linux.IORING_ENTER_SQ_WAKEUP
|
||||||
|
need_enter = 1
|
||||||
|
|
||||||
|
if need_enter:
|
||||||
|
arg.sigmask = 0
|
||||||
|
arg.sigmask_sz = SIG_SIZE
|
||||||
|
if libc.syscall(
|
||||||
|
libc.SYS_io_uring_enter,
|
||||||
|
ring.enter_ring_fd,
|
||||||
|
0,
|
||||||
|
wait_nr,
|
||||||
|
flags,
|
||||||
|
&arg,
|
||||||
|
sizeof(arg),
|
||||||
|
) < 0:
|
||||||
|
if errno.errno != errno.ETIME:
|
||||||
|
with gil:
|
||||||
|
PyErr_SetFromErrno(IOError)
|
||||||
|
return -1
|
||||||
|
|
||||||
|
ready = atomic.load_explicit(
|
||||||
|
<atomic.uint*>cq.ktail, atomic.acquire
|
||||||
|
) - cq.khead[0]
|
||||||
|
|
||||||
|
return ready
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline void ring_cq_pop(CompletionQueue* cq, RingCallback** callback) nogil:
|
||||||
|
cdef:
|
||||||
|
unsigned head
|
||||||
|
linux.io_uring_cqe* cqe
|
||||||
|
RingCallback* ret
|
||||||
|
head = cq.khead[0]
|
||||||
|
cqe = cq.cqes + (head & cq.kring_mask[0])
|
||||||
|
ret = <RingCallback*>cqe.user_data
|
||||||
|
if ret != NULL:
|
||||||
|
ret.res = cqe.res
|
||||||
|
callback[0] = ret
|
||||||
|
atomic.store_explicit(<atomic.uint*>cq.khead, head + 1, atomic.release)
|
||||||
|
|
||||||
|
|
||||||
|
cdef inline linux.io_uring_sqe* ring_sq_submit(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
linux.__u8 op,
|
||||||
|
int fd,
|
||||||
|
unsigned long addr,
|
||||||
|
unsigned len,
|
||||||
|
linux.__u64 offset,
|
||||||
|
bint link,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil:
|
||||||
|
cdef:
|
||||||
|
unsigned int head, next
|
||||||
|
linux.io_uring_sqe* sqe
|
||||||
|
head = atomic.load_explicit(<atomic.uint*>sq.khead, atomic.acquire)
|
||||||
|
next = sq.sqe_tail + 1
|
||||||
|
if next - head <= sq.kring_entries[0]:
|
||||||
|
sqe = &sq.sqes[sq.sqe_tail & sq.kring_mask[0]]
|
||||||
|
sq.sqe_tail = next
|
||||||
|
|
||||||
|
string.memset(sqe, 0, sizeof(linux.io_uring_sqe))
|
||||||
|
sqe.opcode = op
|
||||||
|
sqe.fd = fd
|
||||||
|
sqe.off = offset
|
||||||
|
sqe.addr = addr
|
||||||
|
sqe.len = len
|
||||||
|
if link:
|
||||||
|
sqe.flags = linux.IOSQE_IO_LINK
|
||||||
|
sqe.user_data = <linux.__u64>callback
|
||||||
|
return sqe
|
||||||
|
else:
|
||||||
|
return NULL
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_connect(
|
||||||
|
SubmissionQueue* sq, int fd, libc.sockaddr* addr, RingCallback* callback
|
||||||
|
) nogil:
|
||||||
|
return 1 if ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_CONNECT,
|
||||||
|
fd,
|
||||||
|
<unsigned long>addr,
|
||||||
|
0,
|
||||||
|
sizeof(addr[0]),
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
) else 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_openat(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int dfd,
|
||||||
|
const char* path,
|
||||||
|
int flags,
|
||||||
|
mode_t mode,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil:
|
||||||
|
cdef linux.io_uring_sqe* sqe = ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_OPENAT,
|
||||||
|
dfd,
|
||||||
|
<unsigned long>path,
|
||||||
|
mode,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
)
|
||||||
|
if sqe == NULL:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
sqe.open_flags = flags
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_read(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
char* buf,
|
||||||
|
unsigned nbytes,
|
||||||
|
linux.__u64 offset,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil:
|
||||||
|
return 1 if ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_READ,
|
||||||
|
fd,
|
||||||
|
<unsigned long>buf,
|
||||||
|
nbytes,
|
||||||
|
offset,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
) else 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_close(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
RingCallback * callback,
|
||||||
|
) nogil:
|
||||||
|
return 1 if ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_CLOSE,
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
) else 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_sendmsg(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
const libc.msghdr *msg,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil:
|
||||||
|
return 1 if ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_SENDMSG,
|
||||||
|
fd,
|
||||||
|
<unsigned long>msg,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
) else 0
|
||||||
|
|
||||||
|
|
||||||
|
cdef int ring_sq_submit_recvmsg(
|
||||||
|
SubmissionQueue* sq,
|
||||||
|
int fd,
|
||||||
|
const libc.msghdr *msg,
|
||||||
|
RingCallback* callback,
|
||||||
|
) nogil:
|
||||||
|
return 1 if ring_sq_submit(
|
||||||
|
sq,
|
||||||
|
linux.IORING_OP_RECVMSG,
|
||||||
|
fd,
|
||||||
|
<unsigned long>msg,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
callback,
|
||||||
|
) else 0
|
||||||
|
|
28
tests/cert.pem
Normal file
28
tests/cert.pem
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIErjCCApYCCQDzWJgnuLzufzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5s
|
||||||
|
b2NhbGhvc3Q6ODA4ODAeFw0yMjA3MDIyMDQ5MzZaFw0yMzA3MDIyMDQ5MzZaMBkx
|
||||||
|
FzAVBgNVBAMMDmxvY2FsaG9zdDo4MDg4MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
|
||||||
|
MIICCgKCAgEAsTtnZV5hayOTVfyq6MbkLRaJj4tVoamMXmqTepqpEwQFxUY80MUi
|
||||||
|
iTcnoxeKTHyToEcPDBINxDAQyvYZQyReo2CScPEBeYlW8Ur6DcZ6CslcrQKIAQEp
|
||||||
|
JQ6a/DwZT/GS71qQfaOkiO+tKU7TbaPvF3a6kK+JFOgBaFbkfcb6Ef0xUfhrgfut
|
||||||
|
fQFDrF4up3otlFY3thofTgoLcpx04OHIeveMWHEemEi8LOr/LRV5MDAwlWSqSHuF
|
||||||
|
JMn/U9fPXZXiv450QINtrBY43n9xAP1sQZL4YowYbkbxWjj/ghpzwqqWZz/sxzHr
|
||||||
|
ayy6UJcOkYiY+RzkLPZ5GLU1BJlWUKNueUtZZQ+n3OOYUGFpbeGnOl6YB1qqvNtn
|
||||||
|
8Lvsmqu0nc3gDsINnxi4E3KEstAJlk7X1M8l7XCgjXhTXrv2ebvQyd7gEAMIXtuy
|
||||||
|
Rpv75Kga+ydapxLnI1spF0q31eYuA0EJlWBcLd0nnU/e0GuhmqWB7PCFm2JQ3LyY
|
||||||
|
1GFUuvR0/VAoDf06vyicpTIkuCMrSsbSUqNZamyKyfujUgLqbUlfBfkV++q1cFGY
|
||||||
|
MQJBTI6U9qNc4JBxuiHbPENMVLQQAUsSEbdltsaOYV9rMhZn5FdZa/plOuC7Q5+C
|
||||||
|
7SllnnRKWJIl7pQh3bF6NNq3cjtuu3suDipFoFcyo5f06wpxJHbiZH0CAwEAATAN
|
||||||
|
BgkqhkiG9w0BAQsFAAOCAgEAcc7L+4z7B9SESGKeVVZavt6pvApM9JIJ7SsRjuDp
|
||||||
|
I026Ld7LkOp+QyQ30yVhmYCxKYFCaJSngHsusk8/TMr5OU80ixGEzUXzaq82UJkQ
|
||||||
|
zkfMHm7k3/UKkJ4I4pT7lLeG20M+QQVB5a8tTFBGDt3Rkc54nuxKNq5JKwQ0AaHz
|
||||||
|
aid8LhHZwQMOiRMJgaga0bjrKo8I9yTcOvip+4UFitJe0EiSjLKWS4eyrWN7I7t9
|
||||||
|
T1OctNWb8aILnO9SvjREvVFk5rN70F0nOM0PSQGUdO0KfNAuE+2e47avlTHwH+I9
|
||||||
|
0m1PEDcLikcoo0rCC+8tLDvwuBS+GdH+NKQYSdlnik9EU7dSo+cRHDzTS9oU8/Kv
|
||||||
|
81teIY45pUN0AzoySxgjBr8Z/lKiEXhUq/OCZHEl6lOZGtExuwlwr8ZSZNqNFZkh
|
||||||
|
iqNb8p81ER8nbk6U2T6tx7heBFb0hipfp5hywLL7DMffx1iqEdsIEqYJiFPT4oen
|
||||||
|
34zM0vft/zMys8oqvo82sNSeQUwFfq0Tr5IopJSaGjud+x1qqRW8XEsyW5t5YEsk
|
||||||
|
BjEcRgXWHsXOKRGuTdosftJyf6S/3MCysLrkwftOxWNi8Hin55XxzS6nmoGy0Y4y
|
||||||
|
SOvyGjrlkzRdb187E5Exm2gqArTDJgvl0wFegRxqmQnAe0ABdLz/BYm9nt4AUQca
|
||||||
|
ZeY=
|
||||||
|
-----END CERTIFICATE-----
|
54
tests/debug.py
Normal file
54
tests/debug.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import asyncio
|
||||||
|
import ssl
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
import kloop
|
||||||
|
|
||||||
|
|
||||||
|
def server():
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
cur = pathlib.Path(__file__).parent
|
||||||
|
ctx.load_cert_chain(cur / "cert.pem", cur / "key.pem")
|
||||||
|
|
||||||
|
ss = socket.socket()
|
||||||
|
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
ss.bind(("localhost", 8088))
|
||||||
|
ss.listen(1)
|
||||||
|
s, addr = ss.accept()
|
||||||
|
s = ctx.wrap_socket(s, server_side=True)
|
||||||
|
print(s)
|
||||||
|
while True:
|
||||||
|
req = s.recv(65536)
|
||||||
|
print("<<<", req)
|
||||||
|
if req == b'Hello':
|
||||||
|
s.send(b'world\n')
|
||||||
|
elif req.startswith(b'Sleep'):
|
||||||
|
time.sleep(float(req.split()[1]))
|
||||||
|
s.send(b'Sleep done\n')
|
||||||
|
elif req == b'Bye':
|
||||||
|
s.send(b'So long!\n')
|
||||||
|
s.close()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
s.send(b'unknown command\n')
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
ctx = ssl.create_default_context(cafile="tests/cert.pem")
|
||||||
|
r, w = await asyncio.open_connection("localhost", 8088, ssl=ctx)
|
||||||
|
print(r, w)
|
||||||
|
w.write(b'Sleep 3')
|
||||||
|
print(await r.readline())
|
||||||
|
w.write(b'Hello')
|
||||||
|
print(await r.readline())
|
||||||
|
|
||||||
|
|
||||||
|
t = threading.Thread(target=server)
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
asyncio.set_event_loop_policy(kloop.KLoopPolicy())
|
||||||
|
asyncio.run(main())
|
52
tests/key.pem
Normal file
52
tests/key.pem
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCxO2dlXmFrI5NV
|
||||||
|
/KroxuQtFomPi1WhqYxeapN6mqkTBAXFRjzQxSKJNyejF4pMfJOgRw8MEg3EMBDK
|
||||||
|
9hlDJF6jYJJw8QF5iVbxSvoNxnoKyVytAogBASklDpr8PBlP8ZLvWpB9o6SI760p
|
||||||
|
TtNto+8XdrqQr4kU6AFoVuR9xvoR/TFR+GuB+619AUOsXi6nei2UVje2Gh9OCgty
|
||||||
|
nHTg4ch694xYcR6YSLws6v8tFXkwMDCVZKpIe4Ukyf9T189dleK/jnRAg22sFjje
|
||||||
|
f3EA/WxBkvhijBhuRvFaOP+CGnPCqpZnP+zHMetrLLpQlw6RiJj5HOQs9nkYtTUE
|
||||||
|
mVZQo255S1llD6fc45hQYWlt4ac6XpgHWqq822fwu+yaq7SdzeAOwg2fGLgTcoSy
|
||||||
|
0AmWTtfUzyXtcKCNeFNeu/Z5u9DJ3uAQAwhe27JGm/vkqBr7J1qnEucjWykXSrfV
|
||||||
|
5i4DQQmVYFwt3SedT97Qa6GapYHs8IWbYlDcvJjUYVS69HT9UCgN/Tq/KJylMiS4
|
||||||
|
IytKxtJSo1lqbIrJ+6NSAuptSV8F+RX76rVwUZgxAkFMjpT2o1zgkHG6Ids8Q0xU
|
||||||
|
tBABSxIRt2W2xo5hX2syFmfkV1lr+mU64LtDn4LtKWWedEpYkiXulCHdsXo02rdy
|
||||||
|
O267ey4OKkWgVzKjl/TrCnEkduJkfQIDAQABAoICAQCTIsYMGfa2g6pl0IXzCmuU
|
||||||
|
IwnA6eQFekTWfZRCTPPgnY5M4KrMh1zMncWCWKCwLxxAC74qPzK+lUjbtsPyQddv
|
||||||
|
u1TX/r1CsQpE+AHwPb5EBjeySk+uxc/qu91fWmWJQPzDSWR7acfHB3Oyv4Y3l8l+
|
||||||
|
qUrpo1Ei0hmZDcpTwUUKejDf5GUcXF0DUnFkQCxrTbtZUtqdi8JBf4VReSJvMALI
|
||||||
|
U4toQUEtG/VdNRrzysf5gYhT/maAFkvK5wfaC2hUQYMllJZemL4MB8besN6X+8xM
|
||||||
|
BD/BUJ1Xr/3e8ULuOBeynPyQazmfhYn0SAXFsiKeH9EfvySPT2/pW0bQspw+BfBH
|
||||||
|
0IyZXaH0tqvvTLfhfUBB5CtW8KnJ7n2r9QZbX8+Y8WnyD/BmWhAjN/wihph12vNO
|
||||||
|
wxPlA95yKX3KTNDwTCjvtmgNQU+IkujTfBsH9v701ASQcZhScYLQZ6kksiFTagbf
|
||||||
|
Sk9Pk3i2Et465wBMOrPi2GmcPEXLz4IKK0k5ex3WYi3mIBuCZx/vFOOo/K6x637k
|
||||||
|
g5NeunFJg1n3w/igVOCK2sQQWU9bQHwwVlj0YX4ZrUeC+/l3V5xTZVrqn8TWiHfx
|
||||||
|
8N3DF5oEkW/jbEuI1rrNo1X8Ma+Ij2E1rDPYcNh/4/STIg/i4FRM0qYf13cpzgE0
|
||||||
|
XwBrR8BdWhgSZODGuen44QKCAQEA3m8TtziLbxD0Uc/Xe8ax7eHmi1Bvn4G95dgM
|
||||||
|
TI5ZKOVFEIObe2036cwvTvIsJbDi+AbjXpswQdyxwGku9nZRv8jgiMyNGJKMiyPc
|
||||||
|
RQKFkxfvL+SpQnjITFvPiXdXBVUWWKhBjsNXn29iG9a43WI0yJc8DVEzeOMncwvP
|
||||||
|
gErHu8wXSweBmCSwWD8EE7+CLHJyEB7klF314hkYImSDepKAhXZEsQ0u2BYOsmtD
|
||||||
|
Gie4F8U5kDHJgTV1riMEvLYJFjcYxvhcgTaZOCP4GgVUrnZ8zk1PFQjA2Rc3np5b
|
||||||
|
HCL3Q5aCU/V/tDFKXSCY7GoMbqid4GCsF/wQes0/KDgHpDiutwKCAQEAy/oeQWHx
|
||||||
|
xHnuXtbWtAM+E9M/gbHhnZSxcxwexe87kW4a6kDY15GPpe8BvbfIxoafEv8UzKGf
|
||||||
|
kzZnXwXyXVgUX99rz3gcu40ohT0LIBlE+E4j7TT5DXwD9l9Hd3LHbqkykxiRUEW8
|
||||||
|
2G2PL45qGu9JSA5Yh2QgQxRy8BNe/ilzDE14YWnr+5XaLaMDOAAFwpwHQkzEpAqh
|
||||||
|
o2eLJ6BHn0qL328uX30FlnURjKig4Ag8uPuDmf6UIyIqs4IlfnML+x7UKNprTBgl
|
||||||
|
5vctgcD6xt6fYYcA4GHyibJWqPWw2+6wQgEuCSCP5qqIqksj2ageB4nU3acF5mZR
|
||||||
|
V7Mcta2iP9uSawKCAQACbvqrPX4hB/F0V67G1uSpcphAG/AssZGvSw7PY5tMHD4G
|
||||||
|
MTppPkenUimOVo3vF6FUD1c7eL0ta0myjjQKVD8OtxblyD8e7rOE7i6BvsZRVqiy
|
||||||
|
QHEtnf6q/zIlEd4s28Jz09trwW1a/C/5l/7LxUBIlYb9qk7C2tFaq91oTOkkf9Yd
|
||||||
|
Zwae8H/RZ1cXFDhLXjErRftAOErxX07pSWo61BF95E5aFYfWq18bypqgVaDo+apo
|
||||||
|
jnLi6//OmOl7Ww58ZLvrqVnxu/QvLg2P01Ea24cABs5/r2nUtjFQlfDvPN6xqZrh
|
||||||
|
akUsZoGmxS2HIJ3kNAoOI2Ceno5bmZVATmSfAlO/AoIBACtKUSOFOv989Ucxh227
|
||||||
|
BnDEs8S90OlVxndsr2dIx7aszI+M5biSjw8jc5YlLDpeFeK9OlfXsXtX34Z+R5rh
|
||||||
|
96kFTOdCUa7IXaIxe2v7kPS1+M/+HyFqgZjHTe6e8I6e4Dnxy91if5rbXbk1G3XN
|
||||||
|
9RqS2N0bNfGmpGIpak6800r4IiViio/hlOV8pFE9R/uui6fZjR5Xl0iL0C+/x5Oa
|
||||||
|
CaeI/CmN6iKtg+T7YPFLLkAAOUT0j9IJDVD9vSFiH1fTiimFeaIzuc+UBBd2soKE
|
||||||
|
ewUC1v/IKeZpcBT51+hFdyj7AR80xvWomcqymdA26vCkowXLzefBLWBUhjJUwIuW
|
||||||
|
+TMCggEBAIe10HbMp0nbogdAGE28gEpjzkBBeEl/EgMhN8EjemrLmjWI4rK3+Nyh
|
||||||
|
Jo6W4QG4dT9C7KUP9RzDOoF4kkaFaPZ/KqlSn2qUVYLf9N4s95qQvGlVFIjy5tEp
|
||||||
|
ChYdiuP71DNXCjolQIq7nb1zkgbTbvuwVSnw2QwQnYdkZMulgjVrvsK55jrOnsuO
|
||||||
|
EMxjVrlNxeUrb2zLaBsfA5uTPgKgtDGmTDMx+wFAay5jKc9Zmgr1+z834bCKStAh
|
||||||
|
7gcRoVAjkiu2hoNxBTXgqtFtCizyajiiUUYfFAswJ9RLv7qdrqnpuCv1cffrALmd
|
||||||
|
PcTNbfrOMsRvyHwyfUIHDaBT62YTztQ=
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -1,9 +1,58 @@
|
||||||
|
# Copyright (c) 2022 Fantix King https://fantix.pro
|
||||||
|
# kLoop is licensed under Mulan PSL v2.
|
||||||
|
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
# You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
|
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import ssl
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from kloop import uring, ktls
|
import kloop
|
||||||
|
|
||||||
|
|
||||||
class TestLoop(unittest.TestCase):
|
class TestLoop(unittest.TestCase):
|
||||||
def test_loop(self):
|
def setUp(self):
|
||||||
self.assertIsNotNone(uring)
|
asyncio.set_event_loop_policy(kloop.KLoopPolicy())
|
||||||
self.assertIsNotNone(ktls)
|
self.loop = asyncio.new_event_loop()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.loop.close()
|
||||||
|
asyncio.set_event_loop_policy(None)
|
||||||
|
|
||||||
|
def test_call_soon(self):
|
||||||
|
self.loop.call_soon(self.loop.stop)
|
||||||
|
self.loop.run_forever()
|
||||||
|
|
||||||
|
def test_call_later(self):
|
||||||
|
secs = 0.1
|
||||||
|
self.loop.call_later(secs, self.loop.stop)
|
||||||
|
start = time.monotonic()
|
||||||
|
self.loop.run_forever()
|
||||||
|
self.assertGreaterEqual(time.monotonic() - start, secs)
|
||||||
|
|
||||||
|
def test_connect(self):
|
||||||
|
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
||||||
|
# ctx.check_hostname = False
|
||||||
|
# ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
# ctx.minimum_version = ssl.TLSVersion.TLSv1_3
|
||||||
|
host = "www.google.com"
|
||||||
|
r, w = self.loop.run_until_complete(
|
||||||
|
# asyncio.open_connection("127.0.0.1", 8080, ssl=ctx)
|
||||||
|
asyncio.open_connection(host, 443, ssl=ctx)
|
||||||
|
)
|
||||||
|
self.loop.run_until_complete(asyncio.sleep(1))
|
||||||
|
print('send request')
|
||||||
|
w.write(b"GET / HTTP/1.1\r\n" +
|
||||||
|
f"Host: {host}\r\n".encode("ISO-8859-1") +
|
||||||
|
b"Connection: close\r\n"
|
||||||
|
b"\r\n")
|
||||||
|
while line := self.loop.run_until_complete(r.read()):
|
||||||
|
print(line)
|
||||||
|
w.close()
|
||||||
|
self.loop.run_until_complete(w.wait_closed())
|
||||||
|
|
Loading…
Reference in a new issue