Compare commits

...

50 commits

Author SHA1 Message Date
Émilien Devos
c273c83b98
Merge pull request #15 from iv-org/syeopite-patch-1 2021-10-15 15:52:28 +02:00
syeopite
a4ccf0dc6d
Change CI to run daily on multiple crystal vers 2021-10-15 13:30:43 +00:00
Emilien Devos
26ccce9a29 enable debug symbols 2021-09-03 21:23:52 +02:00
Emilien Devos
0b9f16aa2f build lsquic compiled docker image 2021-08-24 22:23:03 +02:00
syeopite
632d632bfa
Actually use correct shard version 2021-06-10 04:54:43 -07:00
syeopite
52bf01c559
Fix shard version 2021-06-10 04:53:21 -07:00
syeopite
cdec31a66e
Merge pull request #11 from syeopite/add-ci
Add CI workflow
2021-06-10 04:38:21 -07:00
syeopite
9eb767d668
Merge pull request #10 from syeopite/master
Upgrade to crystal 1.0
2021-06-10 04:37:30 -07:00
syeopite
4ba1a4a4dd
Add CI workflow 2021-06-09 16:10:58 -07:00
syeopite
c417433a47
Support crystal 1.0 2021-06-09 15:56:24 -07:00
syeopite
53f31fd94c
Revert "Update recieve function to read full data from socket at once"
This reverts commit a3d2458c28.
2021-06-09 15:52:46 -07:00
syeopite
40b9ccc733
Revert "upgrade to 2.23.1 of lsquic (#6)"
This reverts commit cb01d0fbb8.
2021-06-09 15:50:28 -07:00
syeopite
db4f73c25c
Revert "Merge pull request #7 from tenpura-shrimp/processconns5"
This reverts commit 3aab3dd7f1, reversing
changes made to cb01d0fbb8.
2021-06-09 15:50:15 -07:00
Perflyst
3aab3dd7f1
Merge pull request #7 from tenpura-shrimp/processconns5
Update lsquic binding lifecycle
2021-02-18 15:42:38 +01:00
Van Huynh
cb01d0fbb8
upgrade to 2.23.1 of lsquic (#6)
* upgrade to 2.23.1 of lsquic

* Update shard.yml

* Update version in README

* Use compiled liblsquic.a from static alpine build

https://github.com/iv-org/lsquic-static-alpine/releases/tag/v2.23.1
https://github.com/iv-org/lsquic-static-alpine/releases/download/v2.23.1/liblsquic.a

* Update README.md

Co-authored-by: saltycrys <73420320+saltycrys@users.noreply.github.com>

Co-authored-by: Perflyst <mail@perflyst.de>
Co-authored-by: saltycrys <73420320+saltycrys@users.noreply.github.com>
2021-02-18 08:40:35 +01:00
Andrew Zhao
a3d2458c28 Update recieve function to read full data from socket at once
The existing code reads each message from the socket individually and calls
engine_process_conns after each message. This seems inefficient as it leads
to excess calls engine_process_conns.

This changes the code that reads from the socket to read all available messages
before calling engine_process_conns based off of the logic in the reference
implementation.

reference: https://github.com/litespeedtech/lsquic/blob/master/bin/test_common.c#L737
2021-02-10 00:13:36 -05:00
Andrew Zhao
c965e0a380 Implement engine_process_connections timer
The previous code did not have an implementation to set timers for processing
connections. This leads to an occasional deadlock where process_connections
must be called to continue. This change implements this timer modelled after
the lsquic reference implementation.

Reference: https://github.com/litespeedtech/lsquic/blob/master/bin/prog.c#L535
2021-02-10 00:13:36 -05:00
Perflyst
cd6ec66292
Merge pull request #5 from vhuynh3000/fix_closed_stream_with_open_engine
Remove the ensure block and close the socket only if there is no exception
2021-02-08 19:38:01 +01:00
vhuynh3000
5ef544c1bd Remove the ensure block and close the socket only if there is no exception so that exception during reads do not leave an engine running with no socket. 2020-10-22 21:44:58 -07:00
Van Huynh
4855742c45
close socket when the connection is closed (#2)
Close socket when the connection is closed
2020-10-16 08:41:24 +00:00
TheFrenchGhosty
24dc58db99
Merge pull request #3 from SuperSandro2000/patch-1
Update org to iv-org
2020-10-06 11:10:22 +00:00
Sandro
2eceb3c279
Update org to iv-org 2020-10-04 13:30:32 +02:00
Omar Roth
195bb9e480
Update README 2020-07-19 12:00:08 -04:00
Omar Roth
96a75aae05
Update to latest version of lsquic 2020-07-19 11:42:57 -04:00
Omar Roth
5261034c0d
Update UA 2020-05-25 12:51:25 -05:00
Omar Roth
392a57aa8f
Update to latest version of lsquic 2020-05-20 12:00:38 -05:00
Omar Roth
a6033ff7df Bump version 2019-12-14 16:06:48 -05:00
Omar Roth
e9db6c73b5 Keep reference so GC doesn't collect stream_ctx 2019-12-14 16:06:12 -05:00
Omar Roth
0d1ee4e712 Bump version 2019-11-28 08:56:37 -05:00
Omar Roth
bc101423fa Fix binding for IPv6 2019-11-28 08:56:01 -05:00
Omar Roth
fd48ef3471 Bump version 2019-11-27 11:54:08 -06:00
Omar Roth
023b4d9195 Add specs 2019-11-27 11:53:45 -06:00
Omar Roth
32aee44d8e Add support for specifying family 2019-11-27 11:53:14 -06:00
Omar Roth
31ad3664a5 Bump version 2019-11-24 15:25:35 -05:00
Omar Roth
1f7c9c652f Catch closed stream in readf 2019-11-24 14:24:45 -05:00
Omar Roth
50041fa046 Use buffered write 2019-11-24 13:57:21 -05:00
Omar Roth
31403055e7 Bump version 2019-11-24 13:37:13 -05:00
Omar Roth
016f733109 Replace IO::FileDescriptor with IO::ChanneledPipe 2019-11-24 13:22:08 -05:00
Omar Roth
0c0d8748a7 Catch exception in on_close 2019-11-23 18:16:28 -05:00
Omar Roth
aa94a6ea1b Catch exception in on_read 2019-11-23 18:11:54 -05:00
Omar Roth
7f2ecbfcdc Handle engine in separate fiber 2019-11-23 17:35:46 -05:00
Omar Roth
3c7d1aeeb3 Bump version 2019-11-18 15:26:36 -05:00
Omar Roth
d11cafa0ee Add patch for binding on musl 2019-11-18 15:21:18 -05:00
Omar Roth
7c0ade984c Change default user-agent 2019-11-18 14:49:28 -05:00
Omar Roth
d85dd0b29b Undefine missing functions 2019-11-18 14:43:37 -05:00
Omar Roth
e5c49ba0c0 Update static libs and bump version 2019-11-16 16:08:27 -05:00
Omar Roth
b84cc84aa1 Fix local variable stream_ctx 2019-11-16 14:50:07 -05:00
Omar Roth
820c169849 Bump version 2019-11-16 14:43:21 -05:00
Omar Roth
a50712f308 Fix require 2019-11-16 14:42:28 -05:00
Omar Roth
09b3f94426 Add licenses and update README 2019-11-16 14:15:26 -05:00
17 changed files with 1256 additions and 565 deletions

66
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,66 @@
name: lsquic.cr CI
on:
schedule:
- cron: "0 0 * * *" # Every day at 00:00
push:
branches:
- "master"
- "api-only"
pull_request:
branches: "*"
jobs:
build:
runs-on: ubuntu-latest
name: "build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }}"
continue-on-error: ${{ !matrix.stable }}
strategy:
fail-fast: false
matrix:
stable: [true]
crystal:
- 1.0.0
- 1.1.1
- 1.2.0
include:
- crystal: nightly
stable: false
steps:
- uses: actions/checkout@v2
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.5.3
with:
crystal: ${{ matrix.crystal }}
- name: Cache Shards
uses: actions/cache@v2
with:
path: ./lib
key: shards-${{ hashFiles('shard.lock') }}
- name: Install Shards
run: |
if ! shards check; then
shards install
fi
- name: Run tests
run: crystal spec --warnings all --error-on-warnings --error-trace
- name: Run lint
run: |
if ! crystal tool format --check; then
crystal tool format
git diff
exit 1
fi
- name: Build
run: crystal build --warnings all --error-on-warnings --error-trace src/lsquic.cr

39
.github/workflows/container-release.yml vendored Normal file
View file

@ -0,0 +1,39 @@
name: Build and release container
on:
push:
branches:
- "master"
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to registry
uses: docker/login-action@v1
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Build and push Docker image for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v2
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64,linux/arm64/v8
push: true
tags: quay.io/invidious/lsquic-compiled:${{ github.sha }},quay.io/invidious/lsquic-compiled:latest

251
LICENSE.boringssl Normal file
View file

@ -0,0 +1,251 @@
BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
licensing. Files that are completely new have a Google copyright and an ISC
license. This license is reproduced at the bottom of this file.
Contributors to BoringSSL are required to follow the CLA rules for Chromium:
https://cla.developers.google.com/clas
Files in third_party/ have their own licenses, as described therein. The MIT
license, for third_party/fiat, which, unlike other third_party directories, is
compiled into non-test libraries, is included below.
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the
OpenSSL License and the original SSLeay license apply to the toolkit. See below
for the actual license texts. Actually both licenses are BSD-style Open Source
licenses. In case of any license issues related to OpenSSL please contact
openssl-core@openssl.org.
The following are Google-internal bug numbers where explicit permission from
some authors is recorded for use of their work. (This is purely for our own
record keeping.)
27287199
27287880
27287883
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
*
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called "OpenSSL"
* nor may "OpenSSL" appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
ISC license used for completely new code in BoringSSL:
/* Copyright (c) 2015, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
The code in third_party/fiat carries the MIT license:
Copyright (c) 2015-2016 the fiat-crypto authors (see
https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Licenses for support code
-------------------------
Parts of the TLS test suite are under the Go license. This code is not included
in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so
distributing code linked against BoringSSL does not trigger this license:
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
BoringSSL uses the Chromium test infrastructure to run a continuous build,
trybots etc. The scripts which manage this, and the script for generating build
metadata, are under the Chromium license. Distributing code linked against
BoringSSL does not trigger this license.
Copyright 2015 The Chromium Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

32
LICENSE.chrome Normal file
View file

@ -0,0 +1,32 @@
A few parts of LiteSpeed QUIC library are based on proto-quic. That
code is covered by this additional license:
------------------------------
Copyright 2015 The Chromium Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

21
LICENSE.lsquic Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -2,6 +2,14 @@
Crystal bindings to the excellent [LSQUIC](https://github.com/litespeedtech/lsquic) library. Crystal bindings to the excellent [LSQUIC](https://github.com/litespeedtech/lsquic) library.
Releases track lsquic's versioning starting with `v2.18.1`.
`liblsquic.a` is licensed under `LICENSE.lsquic` and `LICENSE.chrome`.
Lsquic uses [boringssl](https://github.com/google/boringssl), which is licensed under `LICENSE.boringssl`.
This library is available under the MIT license.
## Installation ## Installation
1. Add the dependency to your `shard.yml`: 1. Add the dependency to your `shard.yml`:
@ -9,14 +17,29 @@ Crystal bindings to the excellent [LSQUIC](https://github.com/litespeedtech/lsqu
```yaml ```yaml
dependencies: dependencies:
lsquic: lsquic:
github: omarroth/lsquic.cr github: iv-org/lsquic.cr
``` ```
2. Run `shards install` 2. Run `shards install`
## Usage
```crystal
require "lsquic"
client = QUIC::Client.new("www.youtube.com")
client.get("/") # => #<HTTP::Client::Response>
client.get("/", headers: HTTP::Headers{
"cookie" => "Some value",
# ...
}) # => #<HTTP::Client::Response>
```
## Contributing ## Contributing
1. Fork it (<https://github.com/omarroth/lsquic.cr/fork>) 1. Fork it (<https://github.com/iv-org/lsquic.cr/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`) 2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`) 3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`) 4. Push to the branch (`git push origin my-new-feature`)

46
docker/APKBUILD-boringssl Normal file
View file

@ -0,0 +1,46 @@
# Based on https://aur.archlinux.org/packages/boringssl-git/
# Maintainer: Omar Roth <omarroth@protonmail.com>
pkgname=boringssl
pkgver=1.1.0
pkgrel=0
pkgdesc="BoringSSL is a fork of OpenSSL that is designed to meet Google's needs"
url="https://boringssl.googlesource.com/boringssl"
arch="all"
license="MIT"
replaces="openssl libressl"
depends="!openssl-libs-static"
makedepends_host="linux-headers"
makedepends="cmake git go perl"
subpackages="$pkgname-static $pkgname-dev $pkgname-doc"
source="251b516.tar.gz::https://github.com/google/boringssl/tarball/251b516"
builddir="$srcdir/google-boringssl-251b516"
prepare() {
:
}
build() {
cmake -DCMAKE_BUILD_TYPE=Debug .
make ssl crypto
}
check() {
make all_tests
}
package() {
for i in *.md ; do
install -Dm644 $i "$pkgdir/usr/share/doc/$pkgname/$i"
done
install -d "$pkgdir/usr/lib"
install -d "$pkgdir/usr/include"
cp -R include/openssl "$pkgdir/usr/include"
install -Dm755 crypto/libcrypto.a "$pkgdir/usr/lib/libcrypto.a"
install -Dm755 ssl/libssl.a "$pkgdir/usr/lib/libssl.a"
# install -Dm755 decrepit/libdecrepit.a "$pkgdir/usr/lib/libdecrepit.a"
# install -Dm755 libboringssl_gtest.a "$pkgdir/usr/lib/libboringssl_gtest.a"
}
sha512sums="
b1d42ed188cf0cce89d40061fa05de85b387ee4244f1236ea488a431536a2c6b657b4f03daed0ac9328c7f5c4c9330499283b8a67f1444dcf9ba5e97e1199c4e 251b516.tar.gz
"

43
docker/APKBUILD-lsquic Normal file
View file

@ -0,0 +1,43 @@
# Maintainer: Omar Roth <omarroth@protonmail.com>
pkgname=lsquic
pkgver=2.18.1
pkgrel=0
pkgdesc="LiteSpeed QUIC and HTTP/3 Library"
url="https://github.com/litespeedtech/lsquic"
arch="all"
license="MIT"
depends="boringssl-dev boringssl-static zlib-static libevent-static"
makedepends="cmake git go perl bsd-compat-headers linux-headers"
subpackages="$pkgname-static"
source="v$pkgver.tar.gz::https://github.com/litespeedtech/lsquic/tarball/v2.18.1
ls-qpack-$pkgver.tar.gz::https://github.com/litespeedtech/ls-qpack/tarball/a8ae6ef
ls-hpack-$pkgver.tar.gz::https://github.com/litespeedtech/ls-hpack/tarball/bd5d589"
builddir="$srcdir/litespeedtech-$pkgname-692a910"
prepare() {
cp -r -T "$srcdir/litespeedtech-ls-qpack-a8ae6ef" "$builddir/src/liblsquic/ls-qpack"
cp -r -T "$srcdir/litespeedtech-ls-hpack-bd5d589" "$builddir/src/lshpack"
}
build() {
cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DBORINGSSL_INCLUDE=/usr/include/openssl \
-DBORINGSSL_LIB_crypto=/usr/lib \
-DBORINGSSL_LIB_ssl=/usr/lib .
make lsquic
}
check() {
make tests
}
package() {
install -d "$pkgdir/usr/lib"
install -Dm755 src/liblsquic/liblsquic.a "$pkgdir/usr/lib/liblsquic.a"
}
sha512sums="
d015a72f1e88750ecb364768a40f532678f11ded09c6447a2e698b20f43fa499ef143a53f4c92a5938dfece0e39e687dc9df4aea97c618faee0c63da771561c3 v2.18.1.tar.gz
c5629085a3881815fb0b72a321eeba8de093eff9417b8ac7bde1ee1264971be0dca6d61d74799b02ae03a4c629b2a9cf21387deeb814935339a8a2503ea33fee ls-qpack-2.18.1.tar.gz
1b9f7ce4c82dadfca8154229a415b0335a61761eba698f814d4b94195c708003deb5cb89318a1ab78ac8fa88b141bc9df283fb1c6e40b3ba399660feaae353a0 ls-hpack-2.18.1.tar.gz
"

28
docker/Dockerfile Normal file
View file

@ -0,0 +1,28 @@
FROM alpine:edge AS liblsquic-builder
WORKDIR /src
RUN apk add --no-cache build-base git apk-tools abuild cmake go perl linux-headers
RUN abuild-keygen -a -n && \
cp /root/.abuild/-*.rsa.pub /etc/apk/keys/
COPY docker/APKBUILD-boringssl boringssl/APKBUILD
RUN cd boringssl && abuild -F -r && cd ..
RUN apk add --repository /root/packages/src boringssl boringssl-dev boringssl-static
RUN apk add --no-cache zlib-dev zlib-static libevent-dev libevent-static
COPY docker/APKBUILD-lsquic lsquic/APKBUILD
RUN cd lsquic && abuild -F -r && cd ..
RUN apk add --repository /root/packages/src lsquic-static
RUN mkdir tmp && cd tmp && \
ar -x /usr/lib/libssl.a && \
ar -x /usr/lib/libcrypto.a && \
ar -x /usr/lib/liblsquic.a && \
ar rc liblsquic.a *.o && \
ranlib liblsquic.a && \
cp liblsquic.a /root/liblsquic.a && \
cd .. && rm -rf tmp

View file

@ -1,9 +1,9 @@
name: lsquic name: lsquic
version: 0.1.0 version: 2.18.1-2
authors: authors:
- Omar Roth <omarroth@protonmail.com> - Omar Roth <omarroth@protonmail.com>
crystal: 0.31.1 crystal: ">= 0.35.0, < 2.0.0"
license: MIT license: MIT

View file

@ -1,9 +1,42 @@
require "./spec_helper" require "./spec_helper"
describe Lsquic do describe QUIC do
# TODO: Write tests
it "works" do it "works" do
false.should eq(true) client = QUIC::Client.new("www.youtube.com")
5.times do
client.get("/").status_code.should eq(200)
end
client.close
end
it "works with fibers" do
ch = Channel(Int32).new
5.times do
spawn do
client = QUIC::Client.new("www.youtube.com")
5.times do
ch.send client.get("/").status_code
end
client.close
end
end
(5 * 5).times do
ch.receive.should eq(200)
end
end
it "restarts engine after closing" do
client = QUIC::Client.new("www.youtube.com")
client.get("/").status_code.should eq(200)
client.close
Fiber.yield
client.get("/").status_code.should eq(200)
end end
end end

View file

@ -1,7 +1,5 @@
require "./lsquic/*" require "./lsquic/*"
require "socket"
module QUIC module QUIC
VERSION = "0.1.0" VERSION = "#{LibLsquic::MAJOR_VERSION}.#{LibLsquic::MINOR_VERSION}.#{LibLsquic::PATCH_VERSION}"
QUIC_VERSION = "#{LibLsquic::MAJOR_VERSION}.#{LibLsquic::MINOR_VERSION}.#{LibLsquic::PATCH_VERSION}"
end end

View file

@ -0,0 +1,89 @@
# Based on https://github.com/anykeyh/channeled_pipe/blob/master/src/channeled_pipe/channeled_pipe.cr
class IO::ChanneledPipe < IO
BUFFER_SIZE = 8192
include IO::Buffered
@channel : Channel(Bytes?)
@direction : Symbol
@buffer : Bytes?
getter? closed = false
protected def initialize(@channel, @direction)
end
def unbuffered_read(slice : Bytes)
raise "Cannot read from write side" if @direction == :w
return 0 if @channel.closed? && !@buffer
buffer = @buffer
if buffer
bytes_read = {slice.size, buffer.size}.min
slice.copy_from(buffer.to_unsafe, bytes_read)
if buffer.size == bytes_read
@buffer = nil
else
@buffer = buffer[bytes_read, buffer.size - bytes_read]
end
return bytes_read
else
buffer = @channel.receive
if buffer
bytes_read = {slice.size, buffer.size}.min
slice.copy_from(buffer.to_unsafe, bytes_read)
if buffer.size > bytes_read
@buffer = buffer[bytes_read, buffer.size - bytes_read]
end
return bytes_read
else
@channel.close
return 0
end
end
end
def unbuffered_write(slice : Bytes)
raise "Write not allowed on read side" if @direction == :r
raise "Closed stream" if @closed
@channel.send slice.clone
end
def close_channel
@channel.close
end
def unbuffered_flush
# Nothing
end
def unbuffered_rewind
raise IO::Error.new("Can't rewind")
end
def unbuffered_close
return if @closed
@closed = true
@channel.send nil
end
def self.new(mem = BUFFER_SIZE)
mem = BUFFER_SIZE if mem <= 0
capacity = (mem / BUFFER_SIZE) +
((mem % BUFFER_SIZE != 0) ? 1 : 0)
channel = Channel(Bytes?).new(capacity: mem)
{
ChanneledPipe.new(channel, :r),
ChanneledPipe.new(channel, :w),
}
end
end

View file

@ -1,36 +1,137 @@
require "http/headers" require "http"
require "http/client" require "socket"
require "socket/udp_socket"
struct QUIC::PeerCtx module QUIC
property socket : UDPSocket class StreamCtx
property request : HTTP::Request
property io : IO::ChanneledPipe
def initialize(@socket) def initialize(@request, @io)
end
def local_address
@socket.local_address
end
def remote_address
@socket.remote_address
end end
end end
struct QUIC::StreamCtx class Client
property requests : Array(HTTP::Request) REQUIRED_HEADERS = {":method", ":scheme", ":path", ":authority"}
property io : IO
def initialize def self.stream_readf(stream_if_ctx : Void*, buf : UInt8*, buf_len : LibC::SizeT, fin : LibC::Int)
@requests = [] of HTTP::Request stream_ctx = Box(StreamCtx).unbox(stream_if_ctx)
@io = IO::Memory.new stream_ctx.io.write Slice.new(buf, buf_len)
buf_len
end
def self.on_new_conn(stream_if_ctx : Void*, c : LibLsquic::ConnT)
stream_if_ctx
end
def self.on_conn_closed(c : LibLsquic::ConnT)
Box.box(nil)
end
def self.on_new_stream(stream_if_ctx : Void*, s : LibLsquic::StreamT)
stream_ctx = LibLsquic.stream_conn(s)
.try { |c| LibLsquic.conn_get_ctx(c) }
.try { |c| Box(StreamCtx).unbox(c) }
return Box.box(stream_ctx) if LibLsquic.stream_is_pushed(s) != 0
LibLsquic.stream_wantwrite(s, 1)
Box.box(stream_ctx)
end
def self.on_write(s : LibLsquic::StreamT, stream_if_ctx : Void*)
stream_ctx = Box(StreamCtx).unbox(stream_if_ctx)
request_headers = stream_ctx.request.headers
headers = [] of LibLsquic::LsxpackHeader
REQUIRED_HEADERS.each do |name|
value = stream_ctx.request.headers[name]
headers << LibLsquic::LsxpackHeader.new(
buf: "#{name}#{value}",
name_len: name.bytesize,
name_offset: 0,
val_len: value.bytesize,
val_offset: name.bytesize
)
end
request_headers.each do |name, values|
name = name.downcase
next if REQUIRED_HEADERS.includes? name
headers << LibLsquic::LsxpackHeader.new(
buf: "#{name}#{values[0]}",
name_len: name.bytesize,
name_offset: 0,
val_len: values[0].bytesize,
val_offset: name.bytesize
)
end
http_headers = LibLsquic::HttpHeaders.new(count: headers.size, headers: headers.to_unsafe)
raise "Could not send headers" if LibLsquic.stream_send_headers(s, pointerof(http_headers), stream_ctx.request.body ? 0 : 1) != 0
if body = stream_ctx.request.body.try &.gets_to_end
LibLsquic.stream_write(s, body, body.bytesize)
LibLsquic.stream_flush(s)
end
LibLsquic.stream_shutdown(s, 1)
LibLsquic.stream_wantwrite(s, 0)
LibLsquic.stream_wantread(s, 1)
Box.box(stream_ctx)
end
def self.on_read(s : LibLsquic::StreamT, stream_if_ctx : Void*)
stream_ctx = Box(StreamCtx).unbox(stream_if_ctx)
bytes_read = LibLsquic.stream_readf(s, ->stream_readf, Box.box(stream_ctx))
if bytes_read > 0
# Nothing
elsif bytes_read == 0
LibLsquic.stream_shutdown(s, 0)
LibLsquic.stream_wantread(s, 0)
elsif LibLsquic.stream_is_rejected(s) == 1
LibLsquic.stream_close(s)
else
# raise "Could not read response"
end
stream_if_ctx
end
def self.on_close(s : LibLsquic::StreamT, stream_if_ctx : Void*)
stream_ctx = Box(StreamCtx).unbox(stream_if_ctx)
stream_ctx.io.close
GC.free stream_if_ctx
stream_if_ctx
end
def self.ea_packets_out(peer_ctx : Void*, specs : LibLsquic::OutSpec*, count : LibC::UInt)
packets_out = 0
count.times do |i|
spec = specs[i]
socket = Box(UDPSocket).unbox(spec.peer_ctx)
spec.iovlen.times do |j|
iov = spec.iov[j]
begin
socket.send(iov.iov_base.to_slice(iov.iov_len), to: socket.remote_address)
packets_out += 1
rescue ex
break
end
end end
end end
class QUIC::Client packets_out
end
ENGINE_FLAGS = LibLsquic::LSENG_HTTP ENGINE_FLAGS = LibLsquic::LSENG_HTTP
LibLsquic.global_init(ENGINE_FLAGS & LibLsquic::LSENG_SERVER ? LibLsquic::GLOBAL_SERVER : LibLsquic::GLOBAL_CLIENT) LibLsquic.global_init(ENGINE_FLAGS & LibLsquic::LSENG_SERVER ? LibLsquic::GLOBAL_SERVER : LibLsquic::GLOBAL_CLIENT)
property family : Socket::Family = Socket::Family::INET
# The set of possible valid body types. # The set of possible valid body types.
alias BodyType = String | Bytes | IO | Nil alias BodyType = String | Bytes | IO | Nil
@ -38,17 +139,12 @@ class QUIC::Client
getter port : Int32 getter port : Int32
getter! tls : OpenSSL::SSL::Context::Client getter! tls : OpenSSL::SSL::Context::Client
@peer_ctx : PeerCtx | Nil @stream_channel : Channel(StreamCtx?)
@engine : LibLsquic::EngineT | Nil
@conn : LibLsquic::ConnT | Nil
@engine : LibLsquic::EngineT | Nil
@engine_settings : LibLsquic::EngineSettings
@stream_if : LibLsquic::StreamIf
@engine_api : LibLsquic::EngineApi
@dns_timeout : Float64? @dns_timeout : Float64?
@connect_timeout : Float64? @connect_timeout : Float64?
@read_timeout : Float64? @read_timeout : Float64?
@socket : UDPSocket?
@stream_ctx : StreamCtx?
def initialize(@host : String, port = nil, tls : Bool | OpenSSL::SSL::Context::Client = false) def initialize(@host : String, port = nil, tls : Bool | OpenSSL::SSL::Context::Client = false)
check_host_only(@host) check_host_only(@host)
@ -63,125 +159,105 @@ class QUIC::Client
end end
@port = (port || 443).to_i @port = (port || 443).to_i
@stream_channel = Channel(StreamCtx?).new(20)
@stream_ctx = nil
@engine_open = false
end
LibLsquic.engine_init_settings(out @engine_settings, ENGINE_FLAGS) def run_engine
@engine_settings.es_ua = "Chrome/78.0.3904.97 Linux x86_64" LibLsquic.engine_init_settings(out engine_settings, ENGINE_FLAGS)
@engine_settings.es_ecn = 0 engine_settings.es_ua = "Chrome/83.0.4103.61 Linux x86_64"
engine_settings.es_ecn = 0
err_buf = Bytes.new(0x100) err_buf = Bytes.new(0x100)
err_code = LibLsquic.engine_check_settings(pointerof(@engine_settings), ENGINE_FLAGS, err_buf, err_buf.size) err_code = LibLsquic.engine_check_settings(pointerof(engine_settings), ENGINE_FLAGS, err_buf, err_buf.size)
raise String.new(err_buf) if err_code != 0 raise String.new(err_buf) if err_code != 0
@stream_if = LibLsquic::StreamIf.new stream_if = LibLsquic::StreamIf.new
@stream_if.on_new_conn = ->(stream_if_ctx : Void*, c : LibLsquic::ConnT) { stream_if_ctx } stream_if.on_new_conn = ->QUIC::Client.on_new_conn(Void*, LibLsquic::ConnT)
@stream_if.on_conn_closed = ->(c : LibLsquic::ConnT) do stream_if.on_conn_closed = ->QUIC::Client.on_conn_closed(LibLsquic::ConnT)
Box.box(nil) stream_if.on_new_stream = ->QUIC::Client.on_new_stream(Void*, LibLsquic::StreamT)
stream_if.on_write = ->QUIC::Client.on_write(LibLsquic::StreamT, Void*)
stream_if.on_read = ->QUIC::Client.on_read(LibLsquic::StreamT, Void*)
stream_if.on_close = ->QUIC::Client.on_close(LibLsquic::StreamT, Void*)
engine_api = LibLsquic::EngineApi.new
engine_api.ea_settings = pointerof(engine_settings)
engine_api.ea_stream_if = pointerof(stream_if)
engine_api.ea_packets_out = ->QUIC::Client.ea_packets_out(Void*, LibLsquic::OutSpec*, LibC::UInt)
# logger_if = LibLsquic::LoggerIf.new
# logger_if.log_buf = ->(logger_ctx : Void*, msg_buf : LibC::Char*, msg_size : LibC::SizeT) { puts String.new(msg_buf); 0 }
# LibLsquic.logger_init(pointerof(logger_if), nil, LibLsquic::LoggerTimestampStyle::LltsHhmmssms)
# LibLsquic.set_log_level("debug")
engine = LibLsquic.engine_new(ENGINE_FLAGS, pointerof(engine_api))
hostname = host.starts_with?('[') && host.ends_with?(']') ? host[1..-2] : host
@engine_open = true
conn = LibLsquic.engine_connect(
engine,
LibLsquic::Version::Lsqver050,
socket.local_address,
socket.remote_address,
Box.box(socket), nil,
hostname, 0,
nil, 0,
nil, 0
)
spawn do
while stream_ctx = @stream_channel.receive
LibLsquic.conn_set_ctx(conn, Box.box(stream_ctx))
LibLsquic.conn_make_stream(conn)
LibLsquic.engine_process_conns(engine)
end
@engine_open = false
LibLsquic.engine_destroy(engine)
@socket.try &.close
@socket = nil
end end
@stream_if.on_new_stream = ->(stream_if_ctx : Void*, s : LibLsquic::StreamT) do
if LibLsquic.stream_is_pushed(s) != 0
return stream_if_ctx
end
LibLsquic.stream_wantwrite(s, 1)
stream_if_ctx
end
@stream_if.on_write = ->(s : LibLsquic::StreamT, stream_if_ctx : Void*) do
request = Box(StreamCtx).unbox(stream_if_ctx).requests.shift
raise "No request" if !request
headers = [] of LibLsquic::HttpHeader
(request.headers.to_a.sort_by { |k, v| {":authority", ":path", ":scheme", ":method"}.index(k) || -1 }).reverse.each do |tuple|
name, values = tuple
name = name.downcase
values.each do |value|
name_vec = LibLsquic::Iovec.new
name_vec.iov_base = name.to_slice
name_vec.iov_len = name.bytesize
value_vec = LibLsquic::Iovec.new
value_vec.iov_base = value.to_slice
value_vec.iov_len = value.bytesize
header = LibLsquic::HttpHeader.new
header.name = name_vec
header.value = value_vec
headers << header
end
end
http_headers = LibLsquic::HttpHeaders.new
http_headers.count = headers.size
http_headers.headers = headers.to_unsafe
# For payload, last argument is 0
raise "Could not send headers" if LibLsquic.stream_send_headers(s, pointerof(http_headers), request.body ? 0 : 1) != 0
if request.body
body = request.body.not_nil!.gets_to_end
LibLsquic.stream_write(s, body, body.bytesize)
LibLsquic.stream_flush(s)
end
LibLsquic.stream_shutdown(s, 1)
LibLsquic.stream_wantwrite(s, 0)
LibLsquic.stream_wantread(s, 1)
stream_if_ctx
end
@stream_if.on_read = ->(s : LibLsquic::StreamT, stream_if_ctx : Void*) do
stream_ctx = Box(StreamCtx).unbox(stream_if_ctx)
buffer = Bytes.new(0x200)
bytes_read = LibLsquic.stream_read(s, buffer, buffer.size)
if bytes_read > 0
stream_ctx.io.write buffer[0, bytes_read]
elsif bytes_read == 0
LibLsquic.stream_shutdown(s, 0)
elsif LibLsquic.stream_is_rejected(s)
LibLsquic.stream_close(s)
else
raise "Could not read stream"
end
stream_if_ctx
end
# TODO: Allow engine to break with existing connections
@stream_if.on_close = ->(s : LibLsquic::StreamT, stream_if_ctx : Void*) do
LibLsquic.conn_close(LibLsquic.stream_conn(s))
stream_if_ctx
end
@engine_api = LibLsquic::EngineApi.new
@engine_api.ea_settings = pointerof(@engine_settings)
@engine_api.ea_stream_if = pointerof(@stream_if)
@stream_ctx = StreamCtx.new
@engine_api.ea_stream_if_ctx = Box.box(@stream_ctx) # TODO
@engine_api.ea_packets_out = ->(peer_ctx : Void*, specs : LibLsquic::OutSpec*, count : LibC::UInt) do
packets_out = 0
count.times do |i|
spec = specs[i]
peer_ctx = Box(PeerCtx).unbox(spec.peer_ctx)
spec.iovlen.times do |j|
iov = spec.iov[j]
begin begin
peer_ctx.socket.send(iov.iov_base.to_slice(iov.iov_len), to: peer_ctx.remote_address) buffer = Bytes.new(0x600)
packets_out += 1 loop do
rescue ex bytes_read = socket.read buffer
break if !@engine_open
LibLsquic.engine_packet_in(engine, buffer[0, bytes_read], bytes_read, socket.local_address, socket.remote_address, Box.box(socket), 0) if bytes_read != 0
LibLsquic.engine_process_conns(engine)
end end
rescue IO::Error
# may have already been closed
ensure
@socket.try &.close
@socket = nil
end end
end end
packets_out def socket : UDPSocket
socket = @socket
return socket.not_nil! if @socket
socket = UDPSocket.new @family
case @family
when Socket::Family::INET
socket.bind Socket::IPAddress.new("0.0.0.0", 0)
when Socket::Family::INET6
socket.bind Socket::IPAddress.new("::", 0)
else
socket.bind Socket::IPAddress.new("0.0.0.0", 0)
end end
Socket::Addrinfo.udp(@host, @port, timeout: @dns_timeout, family: @family) do |addrinfo|
socket.connect(addrinfo, timeout: @connect_timeout) do |error|
close
error
end
end
socket.read_timeout = @read_timeout if @read_timeout
socket.sync = false
@socket = socket
end end
private def check_host_only(string : String) private def check_host_only(string : String)
@ -333,24 +409,16 @@ class QUIC::Client
response = exec_internal_single(request) response = exec_internal_single(request)
return handle_response(response) if response return handle_response(response) if response
# Server probably closed the connection, so retry one
close
request.body.try &.rewind
response = exec_internal_single(request)
return handle_response(response) if response
raise "Unexpected end of http response" raise "Unexpected end of http response"
end end
private def exec_internal_single(request) private def exec_internal_single(request)
send_request(request) io = send_request(request)
@stream_ctx.io.rewind HTTP::Client::Response.from_io?(io, ignore_body: request.ignore_body?)
HTTP::Client::Response.from_io?(@stream_ctx.io, ignore_body: request.ignore_body?)
end end
private def handle_response(response) private def handle_response(response)
close # unless response.keep_alive? # close unless response.keep_alive?
response response
end end
@ -360,29 +428,18 @@ class QUIC::Client
end end
end end
private def exec_internal(request, &block : Response -> T) : T forall T private def exec_internal(request, &block : HTTP::Client::Response -> T) : T forall T
exec_internal_single(request) do |response| exec_internal_single(request) do |response|
if response if response
return handle_response(response) { yield response } return handle_response(response) { yield response }
end end
# Server probably closed the connection, so retry once
close
request.body.try &.rewind
exec_internal_single(request) do |response|
if response
return handle_response(response) do
yield response
end
end
end
end end
raise "Unexpected end of http response" raise "Unexpected end of http response"
end end
private def exec_internal_single(request) private def exec_internal_single(request)
send_request(request) io = send_request(request)
HTTP::Client::Response.from_io?(stream_ctx.io, ignore_body: request.ignore_body?) do |response| HTTP::Client::Response.from_io?(io, ignore_body: request.ignore_body?) do |response|
yield response yield response
end end
end end
@ -390,7 +447,7 @@ class QUIC::Client
private def handle_response(response) private def handle_response(response)
value = yield value = yield
response.body_io?.try &.close response.body_io?.try &.close
close # unless response.keep_alive? # close unless response.keep_alive?
value value
end end
@ -398,10 +455,13 @@ class QUIC::Client
set_defaults request set_defaults request
run_before_request_callbacks(request) run_before_request_callbacks(request)
@stream_ctx.requests << request spawn run_engine if !@engine_open
LibLsquic.conn_make_stream(conn)
run_engine reader, writer = IO::ChanneledPipe.new
# See https://github.com/crystal-lang/crystal/blob/0.32.0/src/openssl/ssl/context.cr#L126
@stream_ctx = StreamCtx.new(request, writer)
@stream_channel.send @stream_ctx
reader
end end
private def set_defaults(request) private def set_defaults(request)
@ -409,12 +469,12 @@ class QUIC::Client
request.headers[":scheme"] ||= "https" request.headers[":scheme"] ||= "https"
request.headers[":path"] ||= request.resource request.headers[":path"] ||= request.resource
request.headers[":authority"] ||= host_header request.headers[":authority"] ||= host_header
request.headers["user-agent"] ||= "Chrome/78.0.3904.97 Linux x86_64" request.headers["user-agent"] ||= "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"
end end
private def self.default_one_shot_headers(headers) private def self.default_one_shot_headers(headers)
headers ||= HTTP::Headers.new headers ||= HTTP::Headers.new
headers["Connection"] ||= "close" headers["connection"] ||= "close"
headers headers
end end
@ -449,68 +509,14 @@ class QUIC::Client
end end
def close def close
# @conn.try { |c| LibLsquic.conn_close(c) } @stream_channel.send nil
@conn = nil Fiber.yield
end end
private def new_request(method, path, headers, body : BodyType) private def new_request(method, path, headers, body : BodyType)
HTTP::Request.new(method, path, headers, body) HTTP::Request.new(method, path, headers, body)
end end
private def engine
engine = @engine
return engine if engine
engine = LibLsquic.engine_new(ENGINE_FLAGS, pointerof(@engine_api))
@engine = engine
end
def run_engine
buffer = Bytes.new(0x600)
loop do
LibLsquic.engine_process_conns(engine)
if LibLsquic.engine_earliest_adv_tick(engine, out diff) == 0
break
# else
# sleep (diff / 1000000).seconds
# sleep (diff % 1000000).microseconds
end
bytes_read = peer_ctx.socket.read(buffer)
LibLsquic.engine_packet_in(engine, buffer[0, bytes_read], bytes_read, peer_ctx.local_address, peer_ctx.remote_address, Box.box(peer_ctx), 0)
end
end
private def peer_ctx
peer_ctx = @peer_ctx
return peer_ctx if peer_ctx
hostname = @host.starts_with?('[') && @host.ends_with?(']') ? @host[1..-2] : @host
socket = UDPSocket.new
socket.bind Socket::IPAddress.new("0.0.0.0", 0)
socket.read_timeout = @read_timeout if @read_timeout
Socket::Addrinfo.udp(host, port, timeout: @dns_timeout) do |addrinfo|
socket.connect(addrinfo, timeout: @connect_timeout) do |error|
error
end
end
socket.sync = false
peer_ctx = PeerCtx.new(socket)
@peer_ctx = peer_ctx
end
def conn
conn = @conn
return conn if conn
hostname = @host.starts_with?('[') && @host.ends_with?(']') ? @host[1..-2] : @host
conn = LibLsquic.engine_connect(engine, LibLsquic::Version::Lsqver046, peer_ctx.local_address, peer_ctx.remote_address, Box.box(peer_ctx), nil, hostname, 0, nil, 0, nil, 0)
@conn = conn
end
private def host_header private def host_header
if (@tls && @port != 443) || (!@tls && @port != 80) if (@tls && @port != 443) || (!@tls && @port != 80)
"#{@host}:#{@port}" "#{@host}:#{@port}"
@ -574,3 +580,4 @@ class QUIC::Client
end end
end end
end end
end

BIN
src/lsquic/ext/liblsquic.a Normal file

Binary file not shown.

View file

@ -1,40 +1,26 @@
@[Link(ldflags: "#{__DIR__}/ext/liblsquic.a -lz")] @[Link(ldflags: "#{__DIR__}/ext/liblsquic.a")]
lib LibLsquic lib LibLsquic
MAX_CID_LEN = 20
QQUIC_CID_LEN = 8
LSENG_SERVER = 1 LSENG_SERVER = 1
LSENG_HTTP = 2 LSENG_HTTP = 2
LSENG_HTTP_SERVER = LSENG_SERVER | LSENG_HTTP LSENG_HTTP_SERVER = LSENG_SERVER | LSENG_HTTP
GLOBAL_CLIENT = 1 GLOBAL_CLIENT = 1
GLOBAL_SERVER = 2 GLOBAL_SERVER = 2
MAJOR_VERSION = 2 MAJOR_VERSION = 2
MINOR_VERSION = 6 MINOR_VERSION = 18
PATCH_VERSION = 1 PATCH_VERSION = 1
EXPERIMENTAL_Q098 = 0 EXPERIMENTAL_Q098 = 0
DEPRECATED_VERSIONS = 0 DEPRECATED_VERSIONS = 0
MIN_CFW = 16 * 1024
DF_CFCW_SERVER = 3 * 1024 * 1024 / 2
DF_CFCW_CLIENT = 15 * 1024 * 1024
DF_SFCW_SERVER = 1 * 1024 * 1024
DF_SFCW_CLIENT = 6 * 1024 * 1024
DF_MAX_STREAMS_IN = 100 DF_MAX_STREAMS_IN = 100
DF_INIT_MAX_STREAM_DATA_BIDI_LOCAL_SERVER = 0 DF_INIT_MAX_STREAM_DATA_BIDI_LOCAL_SERVER = 0
DF_INIT_MAX_STREAM_DATA_BIDI_REMOTE_CLIENT = 0 DF_INIT_MAX_STREAM_DATA_BIDI_REMOTE_CLIENT = 0
DF_INIT_MAX_STREAMS_UNI_CLIENT = 100 DF_INIT_MAX_STREAMS_UNI_CLIENT = 100
DF_INIT_MAX_STREAMS_UNI_SERVER = 3 DF_INIT_MAX_STREAMS_UNI_SERVER = 3
DF_INIT_MAX_STREAM_DATA_UNI_CLIENT = 32 * 1024
DF_INIT_MAX_STREAM_DATA_UNI_SERVER = 12 * 1024
DF_IDLE_TIMEOUT = 30 DF_IDLE_TIMEOUT = 30
DF_PING_PERIOD = 15 DF_PING_PERIOD = 15
DF_HANDSHAKE_TO = 10 * 1000 * 1000
DF_IDLE_CONN_TO = DF_IDLE_TIMEOUT * 1000 * 1000
DF_SILENT_CLOSE = 1 DF_SILENT_CLOSE = 1
DF_MAX_HEADER_LIST_SIZE = 0 DF_MAX_HEADER_LIST_SIZE = 0
DF_UA = "LSQUIC" DF_UA = "LSQUIC"
DF_STTL = 86400 DF_STTL = 86400
DF_MAX_INCHOATE = 1 * 1000 * 1000
DF_SUPPORT_SREJ_SERVER = 1
DF_SUPPORT_SREJ_CLIENT = 0
DF_SUPPORT_NSTP = 0 DF_SUPPORT_NSTP = 0
DF_SUPPORT_PUSH = 1 DF_SUPPORT_PUSH = 1
DF_SUPPORT_TCID0 = 1 DF_SUPPORT_TCID0 = 1
@ -53,24 +39,26 @@ lib LibLsquic
DF_QPACK_ENC_MAX_SIZE = 4096 DF_QPACK_ENC_MAX_SIZE = 4096
DF_ECN = 0 DF_ECN = 0
DF_ALLOW_MIGRATION = 1 DF_ALLOW_MIGRATION = 1
DF_CC_ALGO = 2 DF_QL_BITS = 2
DF_SPIN = 1
DF_DELAYED_ACKS = 0
DF_TIMESTAMPS = 1
DF_CC_ALGO = 1
DF_MAX_UDP_PAYLOAD_SIZE_RX = 0
DF_GREASE_QUIC_BIT = 1
DF_NOPROGRESS_TIMEOUT_SERVER = 60
DF_NOPROGRESS_TIMEOUT_CLIENT = 0
struct Cid struct Cid
len : UintFast8T len : UInt8
u_cid : CidUCid u_cid : CidUCid
end end
alias UintFast8T = UInt8
union CidUCid union CidUCid
buf : Uint8T[20] buf : LibC::UInt8T[20]
id : Uint64T id : LibC::UInt64T
end end
alias X__Uint8T = UInt8
alias Uint8T = X__Uint8T
alias X__Uint64T = LibC::ULong
alias Uint64T = X__Uint64T
alias Engine = Void alias Engine = Void
alias Conn = Void alias Conn = Void
alias ConnCtx = Void alias ConnCtx = Void
@ -79,16 +67,26 @@ lib LibLsquic
struct HttpHeaders struct HttpHeaders
count : LibC::Int count : LibC::Int
headers : HttpHeader* headers : LsxpackHeader*
end end
struct HttpHeader struct LsxpackHeader
name : Iovec buf : LibC::Char*
value : Iovec name_hash : LibC::UInt32T
nameval_hash : LibC::UInt32T
name_offset : LibC::UInt16T
name_len : LibC::UInt16T
val_offset : LibC::UInt16T
val_len : LibC::UInt16T
chain_next_idx : LibC::UInt16T
hpack_index : LibC::UInt8T
qpack_index : LibC::UInt8T
app_index : LibC::UInt8T
flags : LibC::UInt8T
indexed_type : LibC::UInt8T
dec_overhead : LibC::UInt8T
end end
type HttpHeaderT = HttpHeader
struct Iovec struct Iovec
iov_base : UInt8* iov_base : UInt8*
iov_len : LibC::SizeT iov_len : LibC::SizeT
@ -108,9 +106,7 @@ lib LibLsquic
end end
type ConnT = Void* type ConnT = Void*
type ConnCtxT = Void*
type StreamT = Void* type StreamT = Void*
type StreamCtxT = Void*
enum HskStatus enum HskStatus
LsqHskFail = 0 LsqHskFail = 0
LsqHskOk = 1 LsqHskOk = 1
@ -130,12 +126,11 @@ lib LibLsquic
es_silent_close : LibC::Int es_silent_close : LibC::Int
es_max_header_list_size : LibC::UInt es_max_header_list_size : LibC::UInt
es_ua : LibC::Char* es_ua : LibC::Char*
es_sttl : Uint64T es_sttl : LibC::UInt64T
es_pdmd : Uint32T es_pdmd : LibC::UInt32T
es_aead : Uint32T es_aead : LibC::UInt32T
es_kexs : Uint32T es_kexs : LibC::UInt32T
es_max_inchoate : LibC::UInt es_max_inchoate : LibC::UInt
es_support_srej : LibC::Int
es_support_push : LibC::Int es_support_push : LibC::Int
es_support_tcid0 : LibC::Int es_support_tcid0 : LibC::Int
es_support_nstp : LibC::Int es_support_nstp : LibC::Int
@ -163,10 +158,13 @@ lib LibLsquic
es_ecn : LibC::Int es_ecn : LibC::Int
es_allow_migration : LibC::Int es_allow_migration : LibC::Int
es_cc_algo : LibC::UInt es_cc_algo : LibC::UInt
es_ql_bits : LibC::Int
es_spin : LibC::Int
es_delayed_acks : LibC::Int
es_timestamps : LibC::Int
es_max_packet_size_rx : LibC::UInt16T
end end
alias X__Uint32T = LibC::UInt
alias Uint32T = X__Uint32T
fun engine_init_settings = lsquic_engine_init_settings(x0 : EngineSettings*, engine_flags : LibC::UInt) fun engine_init_settings = lsquic_engine_init_settings(x0 : EngineSettings*, engine_flags : LibC::UInt)
fun engine_check_settings = lsquic_engine_check_settings(settings : EngineSettings*, engine_flags : LibC::UInt, err_buf : LibC::Char*, err_buf_sz : LibC::SizeT) : LibC::Int fun engine_check_settings = lsquic_engine_check_settings(settings : EngineSettings*, engine_flags : LibC::UInt, err_buf : LibC::Char*, err_buf_sz : LibC::SizeT) : LibC::Int
@ -185,8 +183,7 @@ lib LibLsquic
shi_lookup : (Void*, Void*, LibC::UInt, Void**, LibC::UInt* -> LibC::Int) shi_lookup : (Void*, Void*, LibC::UInt, Void**, LibC::UInt* -> LibC::Int)
end end
alias X__TimeT = LibC::Long alias TimeT = LibC::Long
alias TimeT = X__TimeT
struct PackoutMemIf struct PackoutMemIf
pmi_allocate : (Void*, Void*, LibC::UShort, LibC::Char -> Void*) pmi_allocate : (Void*, Void*, LibC::UShort, LibC::Char -> Void*)
@ -195,25 +192,17 @@ lib LibLsquic
end end
struct HsetIf struct HsetIf
hsi_create_header_set : (Void*, LibC::Int -> Void*) hsi_create_header_set : (Void*, StreamT, LibC::Int -> Void*)
hsi_process_header : (Void*, LibC::UInt, LibC::Char*, LibC::UInt, LibC::Char*, LibC::UInt -> HeaderStatus) hsi_prepare_decode : (Void*, LsxpackHeader*, LibC::SizeT -> LsxpackHeader*)
hsi_process_header : (Void*, LsxpackHeader* -> LibC::Int)
hsi_discard_header_set : (Void* -> Void) hsi_discard_header_set : (Void* -> Void)
hsi_flags : HsiFlag
end end
enum HeaderStatus enum HsiFlag
HdrOk = 0 HsiHttp1X = 2
HdrErrDuplicatePsdoHdr = 1 HsiHashName = 4
HdrErrIncomplReqPsdoHdr = 2 HsiHashNameval = 8
HdrErrUnnecReqPsdoHdr = 3
HdrErrBadReqHeader = 4
HdrErrIncomplRespPsdoHdr = 5
HdrErrUnnecRespPsdoHdr = 6
HdrErrUnknownPsdoHdr = 7
HdrErrUppercaseHeader = 8
HdrErrMisplacedPsdoHdr = 9
HdrErrMissingPsdoHdr = 10
HdrErrHeadersTooLarge = 11
HdrErrNomem = 12
end end
struct KeylogIf struct KeylogIf
@ -245,46 +234,49 @@ lib LibLsquic
ea_hsi_ctx : Void* ea_hsi_ctx : Void*
ea_keylog_if : KeylogIf* ea_keylog_if : KeylogIf*
ea_keylog_ctx : Void* ea_keylog_ctx : Void*
ea_alpn : LibC::Char*
end end
alias PacketsOutF = (Void*, OutSpec*, LibC::UInt -> LibC::Int) alias PacketsOutF = (Void*, OutSpec*, LibC::UInt -> LibC::Int)
alias SslCtxSt = Void alias SslCtxSt = Void
alias LookupCertF = (Void*, LibC::Sockaddr*, LibC::Char* -> SslCtxSt*) alias Sockaddr = LibC::Sockaddr
alias LookupCertF = (Void*, Sockaddr*, LibC::Char* -> SslCtxSt*)
type CidT = Cid type CidT = Cid
alias CidsUpdateF = (Void*, Void**, CidT*, LibC::UInt -> Void) alias CidsUpdateF = (Void*, Void**, CidT*, LibC::UInt -> Void)
alias StackStX509 = Void alias StackStX509 = Void
fun engine_new = lsquic_engine_new(engine_flags : LibC::UInt, x1 : EngineApi*) : EngineT fun engine_new = lsquic_engine_new(engine_flags : LibC::UInt, api : EngineApi*) : EngineT
type EngineT = Void* type EngineT = Void*
fun engine_connect = lsquic_engine_connect(x0 : EngineT, x1 : Version, local_sa : LibC::Sockaddr*, peer_sa : LibC::Sockaddr*, peer_ctx : Void*, conn_ctx : Void*, hostname : LibC::Char*, max_packet_size : LibC::UShort, zero_rtt : UInt8*, zero_rtt_len : LibC::SizeT, token : UInt8*, token_sz : LibC::SizeT) : ConnT fun engine_connect = lsquic_engine_connect(x0 : EngineT, x1 : Version, local_sa : Sockaddr*, peer_sa : Sockaddr*, peer_ctx : Void*, conn_ctx : Void*, hostname : LibC::Char*, max_packet_size : LibC::UShort, zero_rtt : UInt8*, zero_rtt_len : LibC::SizeT, token : UInt8*, token_sz : LibC::SizeT) : ConnT
enum Version enum Version
Lsqver039 = 0 Lsqver043 = 0
Lsqver043 = 1 Lsqver046 = 1
Lsqver046 = 2 Lsqver050 = 2
LsqverId23 = 3 LsqverId27 = 3
LsqverId24 = 4 LsqverId28 = 4
LsqverVerneg = 5 LsqverId29 = 5
NLsqver = 6 LsqverVerneg = 6
NLsqver = 7
end end
fun engine_packet_in = lsquic_engine_packet_in(x0 : EngineT, packet_in_data : UInt8*, packet_in_size : LibC::SizeT, sa_local : LibC::Sockaddr*, sa_peer : LibC::Sockaddr*, peer_ctx : Void*, ecn : LibC::Int) : LibC::Int
fun engine_packet_in = lsquic_engine_packet_in(x0 : EngineT, packet_in_data : UInt8*, packet_in_size : LibC::SizeT, sa_local : Sockaddr*, sa_peer : Sockaddr*, peer_ctx : Void*, ecn : LibC::Int) : LibC::Int
fun engine_process_conns = lsquic_engine_process_conns(engine : EngineT) fun engine_process_conns = lsquic_engine_process_conns(engine : EngineT)
fun engine_has_unsent_packets = lsquic_engine_has_unsent_packets(engine : EngineT) : LibC::Int fun engine_has_unsent_packets = lsquic_engine_has_unsent_packets(engine : EngineT) : LibC::Int
fun engine_send_unsent_packets = lsquic_engine_send_unsent_packets(engine : EngineT) fun engine_send_unsent_packets = lsquic_engine_send_unsent_packets(engine : EngineT)
fun engine_destroy = lsquic_engine_destroy(x0 : EngineT) fun engine_destroy = lsquic_engine_destroy(x0 : EngineT)
fun conn_n_avail_streams = lsquic_conn_n_avail_streams(x0 : ConnT) : LibC::UInt fun conn_n_avail_streams = lsquic_conn_n_avail_streams(x0 : ConnT) : LibC::UInt
fun conn_make_stream = lsquic_conn_make_stream(x0 : ConnT) : ConnCtxT fun conn_make_stream = lsquic_conn_make_stream(x0 : ConnT) : Void*
fun conn_n_pending_streams = lsquic_conn_n_pending_streams(x0 : ConnT) : LibC::UInt fun conn_n_pending_streams = lsquic_conn_n_pending_streams(x0 : ConnT) : LibC::UInt
fun conn_cancel_pending_streams = lsquic_conn_cancel_pending_streams(x0 : ConnT, n : LibC::UInt) : LibC::UInt fun conn_cancel_pending_streams = lsquic_conn_cancel_pending_streams(x0 : ConnT, n : LibC::UInt) : LibC::UInt
fun conn_going_away = lsquic_conn_going_away(x0 : ConnT) fun conn_going_away = lsquic_conn_going_away(x0 : ConnT)
fun conn_close = lsquic_conn_close(x0 : ConnT) fun conn_close = lsquic_conn_close(x0 : ConnT)
fun stream_wantread = lsquic_stream_wantread(s : StreamT, is_want : LibC::Int) : LibC::Int fun stream_wantread = lsquic_stream_wantread(s : StreamT, is_want : LibC::Int) : LibC::Int
fun stream_read = lsquic_stream_read(s : StreamT, buf : Void*, len : LibC::SizeT) : SsizeT fun stream_read = lsquic_stream_read(s : StreamT, buf : Void*, len : LibC::SizeT) : LibC::SizeT
alias X__SsizeT = LibC::Long fun stream_readv = lsquic_stream_readv(s : StreamT, vec : Iovec*, iovcnt : LibC::Int) : LibC::SizeT
alias SsizeT = X__SsizeT fun stream_readf = lsquic_stream_readf(s : StreamT, readf : (Void*, UInt8*, LibC::SizeT, LibC::Int -> LibC::SizeT), ctx : Void*) : LibC::SizeT
fun stream_readv = lsquic_stream_readv(s : StreamT, x1 : Iovec*, iovcnt : LibC::Int) : SsizeT
fun stream_readf = lsquic_stream_readf(s : StreamT, readf : (Void*, UInt8*, LibC::SizeT, LibC::Int -> LibC::SizeT), ctx : Void*) : SsizeT
fun stream_wantwrite = lsquic_stream_wantwrite(s : StreamT, is_want : LibC::Int) : LibC::Int fun stream_wantwrite = lsquic_stream_wantwrite(s : StreamT, is_want : LibC::Int) : LibC::Int
fun stream_write = lsquic_stream_write(s : StreamT, buf : Void*, len : LibC::SizeT) : SsizeT fun stream_write = lsquic_stream_write(s : StreamT, buf : Void*, len : LibC::SizeT) : LibC::SizeT
fun stream_writev = lsquic_stream_writev(s : StreamT, vec : Iovec*, count : LibC::Int) : SsizeT fun stream_writev = lsquic_stream_writev(s : StreamT, vec : Iovec*, count : LibC::Int) : LibC::SizeT
struct Reader struct Reader
lsqr_read : (Void*, Void*, LibC::SizeT -> LibC::SizeT) lsqr_read : (Void*, Void*, LibC::SizeT -> LibC::SizeT)
@ -292,19 +284,18 @@ lib LibLsquic
lsqr_ctx : Void* lsqr_ctx : Void*
end end
fun stream_writef = lsquic_stream_writef(x0 : StreamT, x1 : Reader*) : SsizeT fun stream_writef = lsquic_stream_writef(x0 : StreamT, x1 : Reader*) : LibC::SizeT
fun stream_flush = lsquic_stream_flush(s : StreamT) : LibC::Int fun stream_flush = lsquic_stream_flush(s : StreamT) : LibC::Int
fun stream_send_headers = lsquic_stream_send_headers(s : StreamT, h : HttpHeaders*, eos : LibC::Int) : LibC::Int fun stream_send_headers = lsquic_stream_send_headers(s : StreamT, headers : HttpHeaders*, eos : LibC::Int) : LibC::Int
type HttpHeadersT = HttpHeaders
fun stream_get_hset = lsquic_stream_get_hset(x0 : StreamT) : Void* fun stream_get_hset = lsquic_stream_get_hset(x0 : StreamT) : Void*
fun conn_push_stream = lsquic_conn_push_stream(c : ConnT, hdr_set : Void*, s : StreamT, url : Iovec*, authority : Iovec*, headers : HttpHeaders*) : LibC::Int fun conn_push_stream = lsquic_conn_push_stream(c : ConnT, hdr_set : Void*, s : StreamT, headers : HttpHeaders*) : LibC::Int
fun conn_is_push_enabled = lsquic_conn_is_push_enabled(x0 : ConnT) : LibC::Int fun conn_is_push_enabled = lsquic_conn_is_push_enabled(x0 : ConnT) : LibC::Int
fun stream_shutdown = lsquic_stream_shutdown(s : StreamT, how : LibC::Int) : LibC::Int fun stream_shutdown = lsquic_stream_shutdown(s : StreamT, how : LibC::Int) : LibC::Int
fun stream_close = lsquic_stream_close(s : StreamT) : LibC::Int fun stream_close = lsquic_stream_close(s : StreamT) : LibC::Int
fun conn_get_server_cert_chain = lsquic_conn_get_server_cert_chain(x0 : ConnT) : StackStX509* fun conn_get_server_cert_chain = lsquic_conn_get_server_cert_chain(x0 : ConnT) : StackStX509*
fun stream_id = lsquic_stream_id(s : StreamT) : StreamIdT fun stream_id = lsquic_stream_id(s : StreamT) : StreamIdT
alias StreamIdT = Uint64T alias StreamIdT = LibC::UInt64T
fun stream_get_ctx = lsquic_stream_get_ctx(s : StreamT) : StreamCtxT fun stream_get_ctx = lsquic_stream_get_ctx(s : StreamT) : Void*
fun stream_is_pushed = lsquic_stream_is_pushed(s : StreamT) : LibC::Int fun stream_is_pushed = lsquic_stream_is_pushed(s : StreamT) : LibC::Int
fun stream_is_rejected = lsquic_stream_is_rejected(s : StreamT) : LibC::Int fun stream_is_rejected = lsquic_stream_is_rejected(s : StreamT) : LibC::Int
fun stream_refuse_push = lsquic_stream_refuse_push(s : StreamT) : LibC::Int fun stream_refuse_push = lsquic_stream_refuse_push(s : StreamT) : LibC::Int
@ -312,16 +303,16 @@ lib LibLsquic
fun stream_priority = lsquic_stream_priority(s : StreamT) : LibC::UInt fun stream_priority = lsquic_stream_priority(s : StreamT) : LibC::UInt
fun stream_set_priority = lsquic_stream_set_priority(s : StreamT, priority : LibC::UInt) : LibC::Int fun stream_set_priority = lsquic_stream_set_priority(s : StreamT, priority : LibC::UInt) : LibC::Int
fun stream_conn = lsquic_stream_conn(s : StreamT) : ConnT fun stream_conn = lsquic_stream_conn(s : StreamT) : ConnT
fun conn_get_stream_by_id = lsquic_conn_get_stream_by_id(c : ConnT, stream_id : StreamIdT) : StreamT
fun conn_id = lsquic_conn_id(c : ConnT) : CidT* fun conn_id = lsquic_conn_id(c : ConnT) : CidT*
fun conn_get_engine = lsquic_conn_get_engine(c : ConnT) : EngineT fun conn_get_engine = lsquic_conn_get_engine(c : ConnT) : EngineT
fun conn_get_sockaddr = lsquic_conn_get_sockaddr(c : ConnT, local : LibC::Sockaddr**, peer : LibC::Sockaddr**) : LibC::Int fun conn_get_sockaddr = lsquic_conn_get_sockaddr(c : ConnT, local : Sockaddr**, peer : Sockaddr**) : LibC::Int
struct LoggerIf struct LoggerIf
log_buf : (Void*, LibC::Char*, LibC::SizeT -> LibC::Int) log_buf : (Void*, LibC::Char*, LibC::SizeT -> LibC::Int)
end end
fun logger_init = lsquic_logger_init(x0 : LoggerIf*, logger_ctx : Void*, x2 : LoggerTimestampStyle) fun logger_init = lsquic_logger_init(x0 : LoggerIf*, logger_ctx : Void*, x2 : LoggerTimestampStyle)
enum LoggerTimestampStyle enum LoggerTimestampStyle
LltsNone = 0 LltsNone = 0
LltsHhmmssms = 1 LltsHhmmssms = 1
@ -331,6 +322,7 @@ lib LibLsquic
LltsYyyymmddHhmmssus = 5 LltsYyyymmddHhmmssus = 5
NLlts = 6 NLlts = 6
end end
fun set_log_level = lsquic_set_log_level(log_level : LibC::Char*) : LibC::Int fun set_log_level = lsquic_set_log_level(log_level : LibC::Char*) : LibC::Int
fun logger_lopt = lsquic_logger_lopt(optarg : LibC::Char*) : LibC::Int fun logger_lopt = lsquic_logger_lopt(optarg : LibC::Char*) : LibC::Int
fun engine_quic_versions = lsquic_engine_quic_versions(x0 : EngineT) : LibC::UInt fun engine_quic_versions = lsquic_engine_quic_versions(x0 : EngineT) : LibC::UInt
@ -344,15 +336,14 @@ lib LibLsquic
LsqCryQuic = 0 LsqCryQuic = 0
LsqCryTlSv13 = 1 LsqCryTlSv13 = 1
end end
fun conn_crypto_cipher = lsquic_conn_crypto_cipher(c : ConnT) : LibC::Char* fun conn_crypto_cipher = lsquic_conn_crypto_cipher(c : ConnT) : LibC::Char*
fun str2ver = lsquic_str2ver(str : LibC::Char*, len : LibC::SizeT) : Version fun str2ver = lsquic_str2ver(str : LibC::Char*, len : LibC::SizeT) : Version
fun alpn2ver = lsquic_alpn2ver(alpn : LibC::Char*, len : LibC::SizeT) : Version fun alpn2ver = lsquic_alpn2ver(alpn : LibC::Char*, len : LibC::SizeT) : Version
fun engine_cooldown = lsquic_engine_cooldown(x0 : EngineT) fun engine_cooldown = lsquic_engine_cooldown(x0 : EngineT)
fun hsk_getssl = lsquic_hsk_getssl(conn : ConnT) : SslSt* fun conn_get_ctx = lsquic_conn_get_ctx(x0 : ConnT) : Void*
alias SslSt = Void fun conn_set_ctx = lsquic_conn_set_ctx(x0 : ConnT, x1 : Void*)
fun conn_get_ctx = lsquic_conn_get_ctx(x0 : ConnT) : ConnCtxT fun conn_get_peer_ctx = lsquic_conn_get_peer_ctx(x0 : ConnT, local_sa : Sockaddr*) : Void*
fun conn_set_ctx = lsquic_conn_set_ctx(x0 : ConnT, x1 : ConnCtxT)
fun conn_get_peer_ctx = lsquic_conn_get_peer_ctx(x0 : ConnT, local_sa : LibC::Sockaddr*) : Void*
fun conn_abort = lsquic_conn_abort(x0 : ConnT) fun conn_abort = lsquic_conn_abort(x0 : ConnT)
fun get_alt_svc_versions = lsquic_get_alt_svc_versions(versions : LibC::UInt) : LibC::Char* fun get_alt_svc_versions = lsquic_get_alt_svc_versions(versions : LibC::UInt) : LibC::Char*
fun get_h3_alpns = lsquic_get_h3_alpns(versions : LibC::UInt) : LibC::Char** fun get_h3_alpns = lsquic_get_h3_alpns(versions : LibC::UInt) : LibC::Char**
@ -361,6 +352,7 @@ lib LibLsquic
fun engine_earliest_adv_tick = lsquic_engine_earliest_adv_tick(engine : EngineT, diff : LibC::Int*) : LibC::Int fun engine_earliest_adv_tick = lsquic_engine_earliest_adv_tick(engine : EngineT, diff : LibC::Int*) : LibC::Int
fun engine_count_attq = lsquic_engine_count_attq(engine : EngineT, from_now : LibC::Int) : LibC::UInt fun engine_count_attq = lsquic_engine_count_attq(engine : EngineT, from_now : LibC::Int) : LibC::UInt
fun conn_status = lsquic_conn_status(x0 : ConnT, errbuf : LibC::Char*, bufsz : LibC::SizeT) : ConnStatus fun conn_status = lsquic_conn_status(x0 : ConnT, errbuf : LibC::Char*, bufsz : LibC::SizeT) : ConnStatus
enum ConnStatus enum ConnStatus
LsconnStHskInProgress = 0 LsconnStHskInProgress = 0
LsconnStConnected = 1 LsconnStConnected = 1
@ -373,5 +365,6 @@ lib LibLsquic
LsconnStClosed = 8 LsconnStClosed = 8
LsconnStPeerGoingAway = 9 LsconnStPeerGoingAway = 9
end end
$ver2str : LibC::Char*[6]
$lsquic_ver2str : LibC::Char*[7]
end end

View file

@ -1,45 +1,30 @@
require "openssl" require "openssl"
@[Link(ldflags: "#{__DIR__}/ext/liblsquic.a #{__DIR__}/ext/libcrypto.a")]
lib LibCrypto lib LibCrypto
fun evp_ripemd160 = EVP_sha1 : EVP_MD fun evp_ripemd160 = EVP_sha1 : EVP_MD
fun sk_free = sk_free(st : Void*) fun sk_free = sk_free(st : Void*)
fun sk_num = sk_num(x0 : Void*) : Int fun sk_num = sk_num(x0 : Void*) : Int
fun sk_pop_free = sk_pop_free(st : Void*, callback : (Void*) ->) fun sk_pop_free = sk_pop_free(st : Void*, callback : (Void*) ->)
fun sk_value = sk_value(x0 : Void*, x1 : Int) : Void* fun sk_value = sk_value(x0 : Void*, x1 : Int) : Void*
fun openssl_add_all_algorithms = rand : LibC::Int
fun err_load_crypto_strings = rand : LibC::Int
end end
@[Link(ldflags: "#{__DIR__}/ext/libssl.a")]
lib LibSSL lib LibSSL
{% ssl_version = "1.1.0" %}
fun ssl_set_tlsext_host_name = SSL_set_tlsext_host_name(handle : SSL, name : Char*) : Long fun ssl_set_tlsext_host_name = SSL_set_tlsext_host_name(handle : SSL, name : Char*) : Long
fun ssl_ctx_set_tmp_ecdh = SSL_CTX_set_tmp_ecdh(ctx : SSLContext, parg : Void*) : ULong fun ssl_ctx_set_tmp_ecdh = SSL_CTX_set_tmp_ecdh(ctx : SSLContext, parg : Void*) : ULong
fun ssl_ctx_get_mode = SSL_CTX_get_mode(ctx : SSLContext) : ULong fun ssl_ctx_get_mode = SSL_CTX_get_mode(ctx : SSLContext) : ULong
fun ssl_ctx_set_mode = SSL_CTX_set_mode(ctx : SSLContext, mode : ULong) : ULong fun ssl_ctx_set_mode = SSL_CTX_set_mode(ctx : SSLContext, mode : ULong) : ULong
fun ssl_ctx_clear_mode = SSL_CTX_clear_mode(ctx : SSLContext, mode : ULong) : ULong fun ssl_ctx_clear_mode = SSL_CTX_clear_mode(ctx : SSLContext, mode : ULong) : ULong
end fun ssl_ctx_get_options = SSL_CTX_get_options(ctx : SSLContext) : ULong
fun ssl_ctx_set_options = SSL_CTX_set_options(ctx : SSLContext, larg : ULong) : ULong
fun ssl_ctx_clear_options = SSL_CTX_clear_options(ctx : SSLContext, larg : ULong) : ULong
abstract class OpenSSL::SSL::Context fun ssl_library_init = rand : LibC::Int
def set_tmp_ecdh_key(curve = LibCrypto::NID_X9_62_prime256v1) fun ssl_load_error_strings = rand : LibC::Int
key = LibCrypto.ec_key_new_by_curve_name(curve)
raise OpenSSL::Error.new("ec_key_new_by_curve_name") if key.null?
LibSSL.ssl_ctx_set_tmp_ecdh(@handle, key)
LibCrypto.ec_key_free(key)
end
# Returns the current modes set on the TLS context.
def modes
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_get_mode(@handle)
end
# Adds modes to the TLS context.
def add_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_set_mode(@handle, mode)
end
# Removes modes from the TLS context.
def remove_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_clear_mode(@handle, mode)
end
end end
struct OpenSSL::BIO struct OpenSSL::BIO
@ -141,6 +126,43 @@ struct OpenSSL::BIO
end end
end end
abstract class OpenSSL::SSL::Context
def set_tmp_ecdh_key(curve = LibCrypto::NID_X9_62_prime256v1)
key = LibCrypto.ec_key_new_by_curve_name(curve)
raise OpenSSL::Error.new("ec_key_new_by_curve_name") if key.null?
LibSSL.ssl_ctx_set_tmp_ecdh(@handle, key)
LibCrypto.ec_key_free(key)
end
# Returns the current modes set on the TLS context.
def modes
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_get_mode(@handle)
end
# Adds modes to the TLS context.
def add_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_set_mode(@handle, mode)
end
# Removes modes from the TLS context.
def remove_modes(mode : OpenSSL::SSL::Modes)
OpenSSL::SSL::Modes.new LibSSL.ssl_ctx_clear_mode(@handle, mode)
end
# Returns the current options set on the TLS context.
def options
OpenSSL::SSL::Options.new LibSSL.ssl_ctx_get_options(@handle)
end
def add_options(options : OpenSSL::SSL::Options)
OpenSSL::SSL::Options.new LibSSL.ssl_ctx_set_options(@handle, options)
end
def remove_options(options : OpenSSL::SSL::Options)
OpenSSL::SSL::Options.new LibSSL.ssl_ctx_clear_options(@handle, options)
end
end
abstract class OpenSSL::SSL::Socket < IO abstract class OpenSSL::SSL::Socket < IO
class Client < Socket class Client < Socket
def initialize(io, context : Context::Client = Context::Client.new, sync_close : Bool = false, hostname : String? = nil) def initialize(io, context : Context::Client = Context::Client.new, sync_close : Bool = false, hostname : String? = nil)