├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── LICENSE-THIRD-PARTY ├── README.md ├── example ├── Cargo.toml ├── README.md ├── certs │ ├── README.md │ ├── rootCA.crt │ ├── rootCA.key │ ├── rootCA.srl │ ├── server.crt │ ├── server.csr │ ├── server.key │ ├── server.pkcs8 │ └── v3.ext └── src │ ├── client-native.rs │ ├── client-rustls.rs │ ├── server-native.rs │ └── server-rustls.rs ├── monoio-io-wrapper ├── Cargo.toml ├── README.adoc └── src │ ├── lib.rs │ ├── safe_io.rs │ └── unsafe_io.rs ├── monoio-native-tls ├── Cargo.toml ├── README.md ├── examples │ └── connect.rs └── src │ ├── client.rs │ ├── error.rs │ ├── ffi.rs │ ├── lib.rs │ ├── server.rs │ ├── stream.rs │ └── utils.rs ├── monoio-rustls ├── Cargo.toml ├── README.md ├── examples │ └── connect.rs └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ ├── server.rs │ └── stream.rs ├── rust-toolchain └── rustfmt.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /.idea 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["monoio-rustls", "monoio-native-tls", "monoio-io-wrapper", "example"] 3 | resolver = "2" 4 | 5 | [workspace.dependencies] 6 | monoio = { version = "0.2.0" } 7 | bytes = { version = "1" } 8 | thiserror = { version = "1" } 9 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Monoio Contributors 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Monoio Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /LICENSE-THIRD-PARTY: -------------------------------------------------------------------------------- 1 | Third party project code used by this project: tokio-rustls. 2 | 3 | =============================================================================== 4 | 5 | tokio-rustls 6 | https://github.com/tokio-rs/tls/blob/master/tokio-rustls/LICENSE-MIT 7 | 8 | Copyright (c) 2017 quininer kel 9 | 10 | Permission is hereby granted, free of charge, to any 11 | person obtaining a copy of this software and associated 12 | documentation files (the "Software"), to deal in the 13 | Software without restriction, including without 14 | limitation the rights to use, copy, modify, merge, 15 | publish, distribute, sublicense, and/or sell copies of 16 | the Software, and to permit persons to whom the Software 17 | is furnished to do so, subject to the following 18 | conditions: 19 | 20 | The above copyright notice and this permission notice 21 | shall be included in all copies or substantial portions 22 | of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 25 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 26 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 27 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 28 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 29 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 31 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 32 | DEALINGS IN THE SOFTWARE. 33 | 34 | =============================================================================== 35 | 36 | tokio-rustls 37 | https://github.com/tokio-rs/tls/blob/master/tokio-rustls/LICENSE-APACHE 38 | 39 | Apache License 40 | Version 2.0, January 2004 41 | http://www.apache.org/licenses/ 42 | 43 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 44 | 45 | 1. Definitions. 46 | 47 | "License" shall mean the terms and conditions for use, reproduction, 48 | and distribution as defined by Sections 1 through 9 of this document. 49 | 50 | "Licensor" shall mean the copyright owner or entity authorized by 51 | the copyright owner that is granting the License. 52 | 53 | "Legal Entity" shall mean the union of the acting entity and all 54 | other entities that control, are controlled by, or are under common 55 | control with that entity. For the purposes of this definition, 56 | "control" means (i) the power, direct or indirect, to cause the 57 | direction or management of such entity, whether by contract or 58 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 59 | outstanding shares, or (iii) beneficial ownership of such entity. 60 | 61 | "You" (or "Your") shall mean an individual or Legal Entity 62 | exercising permissions granted by this License. 63 | 64 | "Source" form shall mean the preferred form for making modifications, 65 | including but not limited to software source code, documentation 66 | source, and configuration files. 67 | 68 | "Object" form shall mean any form resulting from mechanical 69 | transformation or translation of a Source form, including but 70 | not limited to compiled object code, generated documentation, 71 | and conversions to other media types. 72 | 73 | "Work" shall mean the work of authorship, whether in Source or 74 | Object form, made available under the License, as indicated by a 75 | copyright notice that is included in or attached to the work 76 | (an example is provided in the Appendix below). 77 | 78 | "Derivative Works" shall mean any work, whether in Source or Object 79 | form, that is based on (or derived from) the Work and for which the 80 | editorial revisions, annotations, elaborations, or other modifications 81 | represent, as a whole, an original work of authorship. For the purposes 82 | of this License, Derivative Works shall not include works that remain 83 | separable from, or merely link (or bind by name) to the interfaces of, 84 | the Work and Derivative Works thereof. 85 | 86 | "Contribution" shall mean any work of authorship, including 87 | the original version of the Work and any modifications or additions 88 | to that Work or Derivative Works thereof, that is intentionally 89 | submitted to Licensor for inclusion in the Work by the copyright owner 90 | or by an individual or Legal Entity authorized to submit on behalf of 91 | the copyright owner. For the purposes of this definition, "submitted" 92 | means any form of electronic, verbal, or written communication sent 93 | to the Licensor or its representatives, including but not limited to 94 | communication on electronic mailing lists, source code control systems, 95 | and issue tracking systems that are managed by, or on behalf of, the 96 | Licensor for the purpose of discussing and improving the Work, but 97 | excluding communication that is conspicuously marked or otherwise 98 | designated in writing by the copyright owner as "Not a Contribution." 99 | 100 | "Contributor" shall mean Licensor and any individual or Legal Entity 101 | on behalf of whom a Contribution has been received by Licensor and 102 | subsequently incorporated within the Work. 103 | 104 | 2. Grant of Copyright License. Subject to the terms and conditions of 105 | this License, each Contributor hereby grants to You a perpetual, 106 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 107 | copyright license to reproduce, prepare Derivative Works of, 108 | publicly display, publicly perform, sublicense, and distribute the 109 | Work and such Derivative Works in Source or Object form. 110 | 111 | 3. Grant of Patent License. Subject to the terms and conditions of 112 | this License, each Contributor hereby grants to You a perpetual, 113 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 114 | (except as stated in this section) patent license to make, have made, 115 | use, offer to sell, sell, import, and otherwise transfer the Work, 116 | where such license applies only to those patent claims licensable 117 | by such Contributor that are necessarily infringed by their 118 | Contribution(s) alone or by combination of their Contribution(s) 119 | with the Work to which such Contribution(s) was submitted. If You 120 | institute patent litigation against any entity (including a 121 | cross-claim or counterclaim in a lawsuit) alleging that the Work 122 | or a Contribution incorporated within the Work constitutes direct 123 | or contributory patent infringement, then any patent licenses 124 | granted to You under this License for that Work shall terminate 125 | as of the date such litigation is filed. 126 | 127 | 4. Redistribution. You may reproduce and distribute copies of the 128 | Work or Derivative Works thereof in any medium, with or without 129 | modifications, and in Source or Object form, provided that You 130 | meet the following conditions: 131 | 132 | (a) You must give any other recipients of the Work or 133 | Derivative Works a copy of this License; and 134 | 135 | (b) You must cause any modified files to carry prominent notices 136 | stating that You changed the files; and 137 | 138 | (c) You must retain, in the Source form of any Derivative Works 139 | that You distribute, all copyright, patent, trademark, and 140 | attribution notices from the Source form of the Work, 141 | excluding those notices that do not pertain to any part of 142 | the Derivative Works; and 143 | 144 | (d) If the Work includes a "NOTICE" text file as part of its 145 | distribution, then any Derivative Works that You distribute must 146 | include a readable copy of the attribution notices contained 147 | within such NOTICE file, excluding those notices that do not 148 | pertain to any part of the Derivative Works, in at least one 149 | of the following places: within a NOTICE text file distributed 150 | as part of the Derivative Works; within the Source form or 151 | documentation, if provided along with the Derivative Works; or, 152 | within a display generated by the Derivative Works, if and 153 | wherever such third-party notices normally appear. The contents 154 | of the NOTICE file are for informational purposes only and 155 | do not modify the License. You may add Your own attribution 156 | notices within Derivative Works that You distribute, alongside 157 | or as an addendum to the NOTICE text from the Work, provided 158 | that such additional attribution notices cannot be construed 159 | as modifying the License. 160 | 161 | You may add Your own copyright statement to Your modifications and 162 | may provide additional or different license terms and conditions 163 | for use, reproduction, or distribution of Your modifications, or 164 | for any such Derivative Works as a whole, provided Your use, 165 | reproduction, and distribution of the Work otherwise complies with 166 | the conditions stated in this License. 167 | 168 | 5. Submission of Contributions. Unless You explicitly state otherwise, 169 | any Contribution intentionally submitted for inclusion in the Work 170 | by You to the Licensor shall be under the terms and conditions of 171 | this License, without any additional terms or conditions. 172 | Notwithstanding the above, nothing herein shall supersede or modify 173 | the terms of any separate license agreement you may have executed 174 | with Licensor regarding such Contributions. 175 | 176 | 6. Trademarks. This License does not grant permission to use the trade 177 | names, trademarks, service marks, or product names of the Licensor, 178 | except as required for reasonable and customary use in describing the 179 | origin of the Work and reproducing the content of the NOTICE file. 180 | 181 | 7. Disclaimer of Warranty. Unless required by applicable law or 182 | agreed to in writing, Licensor provides the Work (and each 183 | Contributor provides its Contributions) on an "AS IS" BASIS, 184 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 185 | implied, including, without limitation, any warranties or conditions 186 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 187 | PARTICULAR PURPOSE. You are solely responsible for determining the 188 | appropriateness of using or redistributing the Work and assume any 189 | risks associated with Your exercise of permissions under this License. 190 | 191 | 8. Limitation of Liability. In no event and under no legal theory, 192 | whether in tort (including negligence), contract, or otherwise, 193 | unless required by applicable law (such as deliberate and grossly 194 | negligent acts) or agreed to in writing, shall any Contributor be 195 | liable to You for damages, including any direct, indirect, special, 196 | incidental, or consequential damages of any character arising as a 197 | result of this License or out of the use or inability to use the 198 | Work (including but not limited to damages for loss of goodwill, 199 | work stoppage, computer failure or malfunction, or any and all 200 | other commercial damages or losses), even if such Contributor 201 | has been advised of the possibility of such damages. 202 | 203 | 9. Accepting Warranty or Additional Liability. While redistributing 204 | the Work or Derivative Works thereof, You may choose to offer, 205 | and charge a fee for, acceptance of support, warranty, indemnity, 206 | or other liability obligations and/or rights consistent with this 207 | License. However, in accepting such obligations, You may act only 208 | on Your own behalf and on Your sole responsibility, not on behalf 209 | of any other Contributor, and only if You agree to indemnify, 210 | defend, and hold each Contributor harmless for any liability 211 | incurred by, or claims asserted against, such Contributor by reason 212 | of your accepting any such warranty or additional liability. 213 | 214 | END OF TERMS AND CONDITIONS 215 | 216 | APPENDIX: How to apply the Apache License to your work. 217 | 218 | To apply the Apache License to your work, attach the following 219 | boilerplate notice, with the fields enclosed by brackets "[]" 220 | replaced with your own identifying information. (Don't include 221 | the brackets!) The text should be enclosed in the appropriate 222 | comment syntax for the file format. We also recommend that a 223 | file or class name and description of purpose be included on the 224 | same "printed page" as the copyright notice for easier 225 | identification within third-party archives. 226 | 227 | Copyright 2017 quininer kel 228 | 229 | Licensed under the Apache License, Version 2.0 (the "License"); 230 | you may not use this file except in compliance with the License. 231 | You may obtain a copy of the License at 232 | 233 | http://www.apache.org/licenses/LICENSE-2.0 234 | 235 | Unless required by applicable law or agreed to in writing, software 236 | distributed under the License is distributed on an "AS IS" BASIS, 237 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 238 | See the License for the specific language governing permissions and 239 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monoio TLS 2 | TLS Stream Wrapper for Monoio. 3 | 4 | ## TLS with rustls 5 | `monoio-rustls` provides a TLS stream wrapper. It implements the `monoio::io::AsyncReadRent` and `monoio::io::AsyncWriteRent` trait. 6 | 7 | Read `example/src/client.rs` and `example/src/server.rs` for more details. 8 | 9 | ## TLS with native tls 10 | Maybe todo. 11 | 12 | ## Licenses 13 | Monoio-tls is licensed under the MIT license or Apache license. 14 | 15 | During developing we referenced a lot from tokio-rustls. We would like to thank the authors of the project. -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoio-tls-example" 3 | version = "0.3.0" 4 | 5 | authors = ["ChiHai "] 6 | edition = "2021" 7 | publish = false 8 | 9 | [dependencies] 10 | monoio = { workspace = true } 11 | 12 | # rustls 13 | rustls = { version = "~0.23.4", features = ["tls12"] } 14 | rustls-pemfile = "^2.1.2" 15 | monoio-rustls = { path = "../monoio-rustls", features = ["tls12"] } 16 | 17 | # native-tls 18 | native-tls = { version = "0.2" } 19 | monoio-native-tls = { path = "../monoio-native-tls" } 20 | 21 | [[bin]] 22 | name = "server-rustls" 23 | path = "src/server-rustls.rs" 24 | 25 | [[bin]] 26 | name = "client-rustls" 27 | path = "src/client-rustls.rs" 28 | 29 | [[bin]] 30 | name = "server-native" 31 | path = "src/server-native.rs" 32 | 33 | [[bin]] 34 | name = "client-native" 35 | path = "src/client-native.rs" 36 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # TLS Demo 2 | 3 | Use curl to verify tls handshake: `curl --resolve monoio.rs:50443:127.0.0.1 --cacert rootCA.crt -vvv https://monoio.rs:50443` -------------------------------------------------------------------------------- /example/certs/README.md: -------------------------------------------------------------------------------- 1 | # Certs 2 | Note: The certificates here are only for demo. 3 | 4 | ## Self Signed Cert Generation 5 | If you want to generate CA and server certificates by yourself, please make sure the certificates are in x509 v3 format(since webpki only support v3, and there's no way to convert v1 to v3). By default openssl generate server certificate in x509 v1. 6 | 7 | ```bash 8 | openssl genrsa -out rootCA.key 4096 9 | openssl req -x509 -new -nodes -sha512 -days 3650 \ 10 | -subj "/C=CN/ST=Shanghai/L=Shanghai/O=Monoio/OU=TLSDemo/CN=monoio-ca" \ 11 | -key rootCA.key \ 12 | -out rootCA.crt 13 | 14 | openssl genrsa -out server.key 4096 15 | openssl req -sha512 -new \ 16 | -subj "/C=CN/ST=Shanghai/L=Shanghai/O=Monoio/OU=TLSDemoServer/CN=monoio.rs" \ 17 | -key server.key \ 18 | -out server.csr 19 | 20 | cat > v3.ext <<-EOF 21 | authorityKeyIdentifier=keyid,issuer 22 | basicConstraints=CA:FALSE 23 | keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 24 | extendedKeyUsage=serverAuth 25 | subjectAltName=@alt_names 26 | 27 | [alt_names] 28 | DNS.1=monoio.rs 29 | EOF 30 | 31 | openssl x509 -req -sha512 -days 3650 \ 32 | -extfile v3.ext \ 33 | -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \ 34 | -in server.csr \ 35 | -out server.crt 36 | ``` 37 | 38 | ## Convert Private Key 39 | To use native-tls, you have to use key in PKCS#8 or PKCS#12. Convert key from PKCS#1 to PKCS#8: 40 | ```bash 41 | openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in server.key -out server.pkcs8 42 | ``` 43 | -------------------------------------------------------------------------------- /example/certs/rootCA.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 32 | 5FEh+FRqhfQ2V6UZ8Z/ny5c37yFi+Xe20A== 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /example/certs/rootCA.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /example/certs/rootCA.srl: -------------------------------------------------------------------------------- 1 | 6890BF801227B6406D46B41DB48334CC8EE5E7BB 2 | -------------------------------------------------------------------------------- /example/certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 33 | Yws= 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /example/certs/server.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIEtTCCAp0CAQAwcDELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFNoYW5naGFpMREw 3 | DwYDVQQHDAhTaGFuZ2hhaTEPMA0GA1UECgwGTW9ub2lvMRYwFAYDVQQLDA1UTFNE 4 | ZW1vU2VydmVyMRIwEAYDVQQDDAltb25vaW8ucnMwggIiMA0GCSqGSIb3DQEBAQUA 5 | A4ICDwAwggIKAoICAQDQg5NLGuO0TSW10C+FkykxmO99ixCNpbi89w5R37n2moe9 6 | gLGvCtF3lZNbBPix8qkdnEaxXhkDAi4Ry2xdA11gTDI7moWrNHcaxPk9nKtc/0dr 7 | BXnTtEFRrCbGtMCNbSHrie/WofOx77sVbe3NnKMKH9KqnhaxfutdOFaDNPgXuF3u 8 | cg3Biy7bNyQDd/7nq/gPEigUomE6Yi/MFh3onhTlV6Y89aQl3yLTTx4QxGSK69NU 9 | jPy9XDS4q0Kl8tYbFG7XeBt1lxLPVZzaCHwZt+wqnoTRnTt+heSCRH56sItuZ/yW 10 | yeeieceba9+dvKLrk5/ofIsHo5soltX+YQEL3dJJk2N1mOaLfy2Z0pIxtvmaByyy 11 | QsUcd0jfHnf8MUfsHhFoOba3iN/Vu3PV3ONeitlclV3oApegDybaNSm4UukYSqSI 12 | auz31V47SdTdU5LzBQmf8Zn++irfop4XfjBiQyZF/+m7sIVYkaN1V+yAEac2YSc7 13 | uDyRZ2A05mRcxCpMjV0ZStBQoDBTfSkc5s5RmTaOIvDE64zYBYoLZuGHbiHWGriv 14 | zpcebckGsChDOEKg7DfZhIx+bc40CMMP/eN7WJ3wApDZ1xHdMYv55ctWCTqfUd9P 15 | SBnk02kRjEDQHPgVMFV4TQG8lJ/6P++paJfQy87//yt+HBMdP88VAH0rI7oVpwID 16 | AQABoAAwDQYJKoZIhvcNAQENBQADggIBABygUqZSZ6zTHN2TxGJHq6spQfZtBh1y 17 | mfg1nhvpYdC3NqSm8Vw7IpTKr9iNCH+k9eR7/ThUC+1p7/l0blV/QzZNFNVf3ma5 18 | h0G3S/f94plmuFczKs09WCWPHrUNVjGncallnHkHihHgW7shu2G31dQ6df0UV4Ti 19 | CDcfb4P1CWAEdXH5c6z2EzuSI1S1az4NK2lVGwrFdPk6TmwIOjXS746PSWwLH4aF 20 | 1PKt3n8h4Q25vSu6f+ubnob8g5OVfkoUE2IuKaATqTqN1J0xCAS4IvVgTAivCTG2 21 | Lq5AFczJnxnZVI3tu7i/g5RExK/Adf3FeHWZCRKD3aHeDQBVgO5PqpRC28zlObN6 22 | Itf7xPJosu+H8VSDvfsBI/8oJBMkTCpjxdWkiTM8gF+OijA/gdk/1aJFpWFGWrWQ 23 | u6AZFoBbg4PBIOL11CXYzctql4RPfl1XS3A+XdmWM0kmOfkA+MzflCmStTxylE5F 24 | P96RTfoHeT7HZ+TSTSetA3BI0hkynhD8hnpv3gstsvmtV4ALg1UXytwwqmKmAZ1j 25 | nTkVHm7tdq+wbJjCur845Jvzh8Dcdz7oXy1ezKGk1n8VLoDqULYVbXXX8EEXl9nx 26 | 1VSUQM9H8oqazgy5QsSO5cGOSQt7HYKEF5C1sm4DwK1vVKpduBKRom8YIemwZoXR 27 | LKZ7Qdn1RxHH 28 | -----END CERTIFICATE REQUEST----- 29 | -------------------------------------------------------------------------------- /example/certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /example/certs/server.pkcs8: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | 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 51 | WFw/7CWMk64hipDn6DHIi+qIlj0bDo4= 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /example/certs/v3.ext: -------------------------------------------------------------------------------- 1 | authorityKeyIdentifier=keyid,issuer 2 | basicConstraints=CA:FALSE 3 | keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 4 | extendedKeyUsage=serverAuth 5 | subjectAltName=@alt_names 6 | 7 | [alt_names] 8 | DNS.1=monoio.rs 9 | -------------------------------------------------------------------------------- /example/src/client-native.rs: -------------------------------------------------------------------------------- 1 | use monoio::{ 2 | io::{AsyncReadRentExt, AsyncWriteRent, AsyncWriteRentExt}, 3 | net::TcpStream, 4 | }; 5 | use monoio_native_tls::TlsConnector; 6 | use native_tls::Certificate; 7 | 8 | #[monoio::main] 9 | async fn main() { 10 | let raw_connector = native_tls::TlsConnector::builder() 11 | .add_root_certificate(read_ca_certs()) 12 | .build() 13 | .unwrap(); 14 | let connector = TlsConnector::from(raw_connector); 15 | let stream = TcpStream::connect("127.0.0.1:50443").await.unwrap(); 16 | println!("127.0.0.1:50443 connected"); 17 | 18 | let mut stream = connector.connect("monoio.rs", stream).await.unwrap(); 19 | println!("handshake success"); 20 | 21 | let data = "hello world"; 22 | stream.write_all(data).await.0.expect("unable to send data"); 23 | println!("send data: {data}"); 24 | let buf = vec![0; data.len()]; 25 | let (res, buf) = stream.read_exact(buf).await; 26 | assert!(res.is_ok(), "unable to recv data"); 27 | println!( 28 | "recv data: {}", 29 | String::from_utf8(buf).expect("invalid data") 30 | ); 31 | let _ = stream.shutdown().await; 32 | } 33 | 34 | fn read_ca_certs() -> Certificate { 35 | Certificate::from_pem(include_bytes!("../certs/rootCA.crt")).unwrap() 36 | } 37 | -------------------------------------------------------------------------------- /example/src/client-rustls.rs: -------------------------------------------------------------------------------- 1 | use std::io::Cursor; 2 | 3 | use monoio::{ 4 | io::{AsyncReadRentExt, AsyncWriteRent, AsyncWriteRentExt}, 5 | net::TcpStream, 6 | }; 7 | use monoio_rustls::TlsConnector; 8 | use rustls::{RootCertStore}; 9 | use rustls::pki_types::CertificateDer; 10 | use rustls_pemfile::certs; 11 | 12 | #[monoio::main] 13 | async fn main() { 14 | let mut root_store = RootCertStore::empty(); 15 | // Uncomment if you want to use webpki_roots provided CA certs. 16 | // root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { 17 | // rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( 18 | // ta.subject, 19 | // ta.spki, 20 | // ta.name_constraints, 21 | // ) 22 | // })); 23 | root_store 24 | .add(read_ca_certs()) 25 | .expect("unable to trust self-signed CA"); 26 | let config = rustls::ClientConfig::builder() 27 | .with_root_certificates(root_store) 28 | .with_no_client_auth(); 29 | 30 | let connector = TlsConnector::from(config); 31 | let stream = TcpStream::connect("127.0.0.1:50443").await.unwrap(); 32 | println!("127.0.0.1:50443 connected"); 33 | 34 | let domain = rustls::pki_types::ServerName::try_from("monoio.rs").unwrap(); 35 | let mut stream = connector.connect(domain, stream).await.unwrap(); 36 | println!("handshake success"); 37 | 38 | let data = "hello world"; 39 | stream.write_all(data).await.0.expect("unable to send data"); 40 | println!("send data: {data}"); 41 | let buf = vec![0; data.len()]; 42 | let (res, buf) = stream.read_exact(buf).await; 43 | assert!(res.is_ok(), "unable to recv data"); 44 | println!( 45 | "recv data: {}", 46 | String::from_utf8(buf).expect("invalid data") 47 | ); 48 | let _ = stream.shutdown().await; 49 | } 50 | 51 | fn read_ca_certs() -> CertificateDer<'static> { 52 | let mut ca_cursor = Cursor::new(include_bytes!("../certs/rootCA.crt")); 53 | let cert = certs(&mut ca_cursor).into_iter().next().unwrap().unwrap(); 54 | cert 55 | } 56 | -------------------------------------------------------------------------------- /example/src/server-native.rs: -------------------------------------------------------------------------------- 1 | //! An echo server with tls. 2 | #![feature(concat_bytes)] 3 | 4 | use std::io; 5 | 6 | use monoio::{ 7 | io::{AsyncReadRent, AsyncWriteRentExt}, 8 | net::{TcpListener, TcpStream}, 9 | }; 10 | use monoio_native_tls::TlsAcceptor; 11 | use native_tls::Identity; 12 | 13 | #[monoio::main] 14 | async fn main() { 15 | let raw_acceptor = native_tls::TlsAcceptor::new(read_server_certs()).unwrap(); 16 | let acceptor = TlsAcceptor::from(raw_acceptor); 17 | 18 | let listener = TcpListener::bind("127.0.0.1:50443").expect("unable to listen 127.0.0.1:50443"); 19 | while let Ok((stream, addr)) = listener.accept().await { 20 | println!("Accepted from {addr}, will accept tls handshake"); 21 | let tls_acceptor = acceptor.clone(); 22 | monoio::spawn(async move { 23 | let e = process_raw_stream(stream, tls_acceptor).await; 24 | println!("Relay finished {e:?}"); 25 | }); 26 | } 27 | println!("Server exit"); 28 | } 29 | 30 | async fn process_raw_stream(stream: TcpStream, tls_acceptor: TlsAcceptor) -> io::Result<()> { 31 | let mut tls_stream = match tls_acceptor.accept(stream).await { 32 | Ok(s) => { 33 | println!("Handshake finished, will relay data"); 34 | s 35 | } 36 | Err(e) => { 37 | println!("Unable to do handshake: {e}"); 38 | return Err(e.into()); 39 | } 40 | }; 41 | 42 | let mut n = 0; 43 | let mut buf = Vec::with_capacity(8 * 1024); 44 | loop { 45 | // read 46 | let (res, _buf) = tls_stream.read(buf).await; 47 | buf = _buf; 48 | let res: usize = res?; 49 | if res == 0 { 50 | // eof 51 | break; 52 | } 53 | 54 | // write all 55 | let (res, _buf) = tls_stream.write_all(buf).await; 56 | buf = _buf; 57 | n += res?; 58 | } 59 | 60 | println!("Relay finished normally, {n} bytes processed"); 61 | Ok(()) 62 | } 63 | 64 | fn read_server_certs() -> Identity { 65 | let chain = concat_bytes!( 66 | include_bytes!("../certs/server.crt"), 67 | include_bytes!("../certs/rootCA.crt") 68 | ); 69 | Identity::from_pkcs8(&chain[..], include_bytes!("../certs/server.pkcs8")).unwrap() 70 | } 71 | -------------------------------------------------------------------------------- /example/src/server-rustls.rs: -------------------------------------------------------------------------------- 1 | //! An echo server with tls. 2 | 3 | use std::io::{self, Cursor}; 4 | 5 | use monoio::{ 6 | io::{AsyncReadRent, AsyncWriteRentExt}, 7 | net::{TcpListener, TcpStream}, 8 | }; 9 | use monoio_rustls::TlsAcceptor; 10 | use rustls::{ServerConfig}; 11 | use rustls::pki_types::{CertificateDer, PrivateKeyDer}; 12 | use rustls_pemfile::{certs, rsa_private_keys}; 13 | 14 | #[monoio::main] 15 | async fn main() { 16 | let (chain, key) = read_server_certs(); 17 | let config = ServerConfig::builder() 18 | .with_no_client_auth() 19 | .with_single_cert(chain, key) 20 | .expect("invalid cert chain or key"); 21 | let tls_acceptor = TlsAcceptor::from(config); 22 | 23 | let listener = TcpListener::bind("127.0.0.1:50443").expect("unable to listen 127.0.0.1:50443"); 24 | while let Ok((stream, addr)) = listener.accept().await { 25 | println!("Accepted from {addr}, will accept tls handshake"); 26 | let tls_acceptor = tls_acceptor.clone(); 27 | monoio::spawn(async move { 28 | let e = process_raw_stream(stream, tls_acceptor).await; 29 | println!("Relay finished {e:?}"); 30 | }); 31 | } 32 | println!("Server exit"); 33 | } 34 | 35 | async fn process_raw_stream(stream: TcpStream, tls_acceptor: TlsAcceptor) -> io::Result<()> { 36 | let mut tls_stream = match tls_acceptor.accept(stream).await { 37 | Ok(s) => { 38 | println!("Handshake finished, will relay data"); 39 | s 40 | } 41 | Err(e) => { 42 | println!("Unable to do handshake: {e}"); 43 | return Err(e.into()); 44 | } 45 | }; 46 | 47 | let mut n = 0; 48 | let mut buf = Vec::with_capacity(8 * 1024); 49 | loop { 50 | // read 51 | let (res, _buf) = tls_stream.read(buf).await; 52 | buf = _buf; 53 | let res: usize = res?; 54 | if res == 0 { 55 | // eof 56 | break; 57 | } 58 | 59 | // write all 60 | let (res, _buf) = tls_stream.write_all(buf).await; 61 | buf = _buf; 62 | n += res?; 63 | } 64 | 65 | println!("Relay finished normally, {n} bytes processed"); 66 | Ok(()) 67 | } 68 | 69 | fn read_server_certs() -> (Vec>, PrivateKeyDer<'static>) { 70 | let mut ca_cursor = Cursor::new(include_bytes!("../certs/rootCA.crt")); 71 | let ca_data = certs(&mut ca_cursor).into_iter().next().unwrap().unwrap(); 72 | let ca = CertificateDer::from(ca_data); 73 | 74 | let mut crt_cursor = Cursor::new(include_bytes!("../certs/server.crt")); 75 | let crt_data = certs(&mut crt_cursor).into_iter().next().unwrap().unwrap(); 76 | let crt = CertificateDer::from(crt_data); 77 | 78 | let mut key_cursor = Cursor::new(include_bytes!("../certs/server.key")); 79 | let key_data = rsa_private_keys(&mut key_cursor).into_iter().next().unwrap().unwrap(); 80 | let key = PrivateKeyDer::from(key_data); 81 | 82 | let chain = vec![crt, ca]; 83 | (chain, key) 84 | } 85 | -------------------------------------------------------------------------------- /monoio-io-wrapper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoio-io-wrapper" 3 | version = "0.1.1" 4 | 5 | authors = ["ChiHai ", "Rain Jiang "] 6 | categories = ["asynchronous", "network-programming"] 7 | description = "A read/write wrapper to bridge sync and async io" 8 | edition = "2021" 9 | homepage = "https://github.com/monoio-rs/monoio-tls" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.adoc" 12 | repository = "https://github.com/monoio-rs/monoio-tls" 13 | 14 | [dependencies] 15 | monoio = { workspace = true } 16 | 17 | [features] 18 | default = [] 19 | unsafe_io = [] 20 | -------------------------------------------------------------------------------- /monoio-io-wrapper/README.adoc: -------------------------------------------------------------------------------- 1 | = Monoio IO Wrapper 2 | An io wrapper to bind std io and monoio async io. 3 | 4 | |=== 5 | |Return |do_io |read / write 6 | 7 | .2+|SafeIO .2+| Current async r/w result | 1. WouldBlock: Empty(r) or Full(w) and need calling `do_io` 8 | |2. Other: success io or last `do_io` error 9 | 10 | .2+|UnsafeIO | 1. WouldBlock: Not capture mem block info, need calling `read` / `write` | 1. WouldBlock: mem block info is captured and need calling `do_io` 11 | |2. Other: current async r/w result | 2. Other: success io or last `do_io` error 12 | 13 | |=== -------------------------------------------------------------------------------- /monoio-io-wrapper/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unsafe_removed_from_name)] 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent}; 4 | 5 | mod safe_io; 6 | #[cfg(feature = "unsafe_io")] 7 | mod unsafe_io; 8 | 9 | #[derive(Debug)] 10 | pub enum ReadBuffer { 11 | Safe(safe_io::SafeRead), 12 | #[cfg(feature = "unsafe_io")] 13 | Unsafe(unsafe_io::UnsafeRead), 14 | } 15 | 16 | #[derive(Debug)] 17 | pub enum WriteBuffer { 18 | Safe(safe_io::SafeWrite), 19 | #[cfg(feature = "unsafe_io")] 20 | Unsafe(unsafe_io::UnsafeWrite), 21 | } 22 | 23 | impl ReadBuffer { 24 | /// Create a new ReadBuffer with given buffer size. 25 | #[inline] 26 | pub fn new(buffer_size: usize) -> Self { 27 | Self::Safe(safe_io::SafeRead::new(buffer_size)) 28 | } 29 | 30 | /// Create a new ReadBuffer that uses unsafe I/O. 31 | /// # Safety 32 | /// Users must make sure the buffer ptr and len is valid until io finished. 33 | /// So the Future cannot be dropped directly. Consider using CancellableIO. 34 | #[inline] 35 | #[cfg(feature = "unsafe_io")] 36 | pub const unsafe fn new_unsafe() -> Self { 37 | Self::Unsafe(unsafe_io::UnsafeRead::new()) 38 | } 39 | 40 | #[inline] 41 | pub async fn do_io(&mut self, mut io: IO) -> std::io::Result { 42 | match self { 43 | Self::Safe(b) => b.do_io(&mut io).await, 44 | #[cfg(feature = "unsafe_io")] 45 | Self::Unsafe(b) => unsafe { b.do_io(&mut io).await }, 46 | } 47 | } 48 | 49 | #[inline] 50 | #[cfg(feature = "unsafe_io")] 51 | pub fn is_safe(&self) -> bool { 52 | match self { 53 | Self::Safe(_) => true, 54 | #[cfg(feature = "unsafe_io")] 55 | Self::Unsafe(_) => false, 56 | } 57 | } 58 | 59 | #[inline] 60 | #[cfg(not(feature = "unsafe_io"))] 61 | pub const fn is_safe(&self) -> bool { 62 | true 63 | } 64 | } 65 | 66 | impl Default for ReadBuffer { 67 | #[inline] 68 | fn default() -> Self { 69 | Self::Safe(Default::default()) 70 | } 71 | } 72 | 73 | impl std::io::Read for ReadBuffer { 74 | #[inline] 75 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 76 | match self { 77 | Self::Safe(b) => b.read(buf), 78 | #[cfg(feature = "unsafe_io")] 79 | Self::Unsafe(b) => b.read(buf), 80 | } 81 | } 82 | } 83 | 84 | impl WriteBuffer { 85 | /// Create a new WriteBuffer with given buffer size. 86 | #[inline] 87 | pub fn new(buffer_size: usize) -> Self { 88 | Self::Safe(safe_io::SafeWrite::new(buffer_size)) 89 | } 90 | 91 | /// Create a new WriteBuffer that uses unsafe I/O. 92 | /// # Safety 93 | /// Users must make sure the buffer ptr and len is valid until io finished. 94 | /// So the Future cannot be dropped directly. Consider using CancellableIO. 95 | #[inline] 96 | #[cfg(feature = "unsafe_io")] 97 | pub const unsafe fn new_unsafe() -> Self { 98 | Self::Unsafe(unsafe_io::UnsafeWrite::new()) 99 | } 100 | 101 | #[inline] 102 | pub async fn do_io(&mut self, mut io: IO) -> std::io::Result { 103 | match self { 104 | Self::Safe(buf) => buf.do_io(&mut io).await, 105 | #[cfg(feature = "unsafe_io")] 106 | Self::Unsafe(buf) => unsafe { buf.do_io(&mut io).await }, 107 | } 108 | } 109 | 110 | #[inline] 111 | #[cfg(feature = "unsafe_io")] 112 | pub fn is_safe(&self) -> bool { 113 | match self { 114 | Self::Safe(_) => true, 115 | #[cfg(feature = "unsafe_io")] 116 | Self::Unsafe(_) => false, 117 | } 118 | } 119 | 120 | #[inline] 121 | #[cfg(not(feature = "unsafe_io"))] 122 | pub const fn is_safe(&self) -> bool { 123 | true 124 | } 125 | } 126 | 127 | impl Default for WriteBuffer { 128 | #[inline] 129 | fn default() -> Self { 130 | Self::Safe(Default::default()) 131 | } 132 | } 133 | 134 | impl std::io::Write for WriteBuffer { 135 | #[inline] 136 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 137 | match self { 138 | Self::Safe(b) => b.write(buf), 139 | #[cfg(feature = "unsafe_io")] 140 | Self::Unsafe(b) => b.write(buf), 141 | } 142 | } 143 | 144 | #[inline] 145 | fn flush(&mut self) -> std::io::Result<()> { 146 | match self { 147 | Self::Safe(b) => b.flush(), 148 | #[cfg(feature = "unsafe_io")] 149 | Self::Unsafe(b) => b.flush(), 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /monoio-io-wrapper/src/safe_io.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, io, mem}; 2 | 3 | use monoio::{ 4 | buf::{IoBuf, IoBufMut}, 5 | io::{AsyncReadRent, AsyncWriteRent, AsyncWriteRentExt}, 6 | }; 7 | 8 | const BUFFER_SIZE: usize = 16 * 1024; 9 | 10 | struct Buffer { 11 | read: usize, 12 | write: usize, 13 | buf: Box<[u8]>, 14 | } 15 | 16 | impl Default for Buffer { 17 | fn default() -> Self { 18 | Self::new(BUFFER_SIZE) 19 | } 20 | } 21 | 22 | impl Buffer { 23 | fn new(size: usize) -> Self { 24 | Self { 25 | read: 0, 26 | write: 0, 27 | buf: vec![0; size].into_boxed_slice(), 28 | } 29 | } 30 | 31 | fn len(&self) -> usize { 32 | self.write - self.read 33 | } 34 | 35 | fn is_empty(&self) -> bool { 36 | self.len() == 0 37 | } 38 | 39 | fn available(&self) -> usize { 40 | self.buf.len() - self.write 41 | } 42 | 43 | fn is_full(&self) -> bool { 44 | self.available() == 0 45 | } 46 | 47 | fn advance(&mut self, n: usize) { 48 | assert!(self.read + n <= self.write); 49 | self.read += n; 50 | if self.read == self.write { 51 | self.read = 0; 52 | self.write = 0; 53 | } 54 | } 55 | } 56 | 57 | unsafe impl monoio::buf::IoBuf for Buffer { 58 | fn read_ptr(&self) -> *const u8 { 59 | unsafe { self.buf.as_ptr().add(self.read) } 60 | } 61 | 62 | fn bytes_init(&self) -> usize { 63 | self.write - self.read 64 | } 65 | } 66 | 67 | unsafe impl monoio::buf::IoBufMut for Buffer { 68 | fn write_ptr(&mut self) -> *mut u8 { 69 | unsafe { self.buf.as_mut_ptr().add(self.write) } 70 | } 71 | 72 | fn bytes_total(&mut self) -> usize { 73 | self.buf.len() - self.write 74 | } 75 | 76 | unsafe fn set_init(&mut self, pos: usize) { 77 | self.write += pos; 78 | } 79 | } 80 | 81 | pub struct SafeRead { 82 | // the option is only meant for temporary take, it always should be some 83 | buffer: Option, 84 | status: ReadStatus, 85 | } 86 | 87 | impl Debug for SafeRead { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | f.debug_struct("SafeRead") 90 | .field("status", &self.status) 91 | .finish() 92 | } 93 | } 94 | 95 | #[derive(Debug)] 96 | enum ReadStatus { 97 | Eof, 98 | Err(io::Error), 99 | Ok, 100 | } 101 | 102 | impl Default for SafeRead { 103 | fn default() -> Self { 104 | Self { 105 | buffer: Some(Buffer::default()), 106 | status: ReadStatus::Ok, 107 | } 108 | } 109 | } 110 | 111 | impl SafeRead { 112 | /// Create a new SafeRead with given buffer size. 113 | pub fn new(buffer_size: usize) -> Self { 114 | Self { 115 | buffer: Some(Buffer::new(buffer_size)), 116 | status: ReadStatus::Ok, 117 | } 118 | } 119 | 120 | /// `do_io` do async read from io to inner buffer. 121 | /// # Handle return value 122 | /// _: the read result. 123 | pub async fn do_io(&mut self, mut io: IO) -> io::Result { 124 | // if there are some data inside the buffer, just return. 125 | let buffer = self.buffer.as_ref().expect("buffer ref expected"); 126 | if !buffer.is_empty() { 127 | return Ok(buffer.len()); 128 | } 129 | 130 | // read from raw io 131 | // # Safety 132 | // We have already checked it is not None. 133 | let buffer = unsafe { self.buffer.take().unwrap_unchecked() }; 134 | let (result, buf) = io.read(buffer).await; 135 | self.buffer = Some(buf); 136 | match result { 137 | Ok(0) => { 138 | self.status = ReadStatus::Eof; 139 | result 140 | } 141 | Ok(_) => { 142 | self.status = ReadStatus::Ok; 143 | result 144 | } 145 | Err(e) => { 146 | let rerr = e.kind().into(); 147 | self.status = ReadStatus::Err(e); 148 | Err(rerr) 149 | } 150 | } 151 | } 152 | } 153 | 154 | impl io::Read for SafeRead { 155 | /// `read` from buffer. 156 | /// # Handle return value 157 | /// 1. Err(WouldBlock): the buffer is empty, call do_io to fetch more. 158 | /// 2. _: handle it. 159 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 160 | // if buffer is empty, return WoundBlock. 161 | let buffer = self.buffer.as_mut().expect("buffer mut expected"); 162 | if buffer.is_empty() { 163 | return match mem::replace(&mut self.status, ReadStatus::Ok) { 164 | ReadStatus::Eof => Ok(0), 165 | ReadStatus::Err(e) => Err(e), 166 | ReadStatus::Ok => Err(io::ErrorKind::WouldBlock.into()), 167 | }; 168 | } 169 | 170 | // now buffer is not empty. copy it. 171 | let to_copy = buffer.len().min(buf.len()); 172 | unsafe { std::ptr::copy_nonoverlapping(buffer.read_ptr(), buf.as_mut_ptr(), to_copy) }; 173 | buffer.advance(to_copy); 174 | 175 | Ok(to_copy) 176 | } 177 | } 178 | 179 | pub struct SafeWrite { 180 | // the option is only meant for temporary take, it always should be some 181 | buffer: Option, 182 | status: WriteStatus, 183 | } 184 | 185 | impl Debug for SafeWrite { 186 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 187 | f.debug_struct("SafeWrite") 188 | .field("status", &self.status) 189 | .finish() 190 | } 191 | } 192 | 193 | #[derive(Debug)] 194 | enum WriteStatus { 195 | Err(io::Error), 196 | Ok, 197 | } 198 | 199 | impl Default for SafeWrite { 200 | fn default() -> Self { 201 | Self { 202 | buffer: Some(Buffer::default()), 203 | status: WriteStatus::Ok, 204 | } 205 | } 206 | } 207 | 208 | impl SafeWrite { 209 | /// Create a new SafeWrite with given buffer size. 210 | pub fn new(buffer_size: usize) -> Self { 211 | Self { 212 | buffer: Some(Buffer::new(buffer_size)), 213 | status: WriteStatus::Ok, 214 | } 215 | } 216 | 217 | /// `do_io` do async write from inner buffer to io. 218 | /// # Handle return value 219 | /// _: the write_all result(note: the data may have been written even when error). 220 | pub async fn do_io(&mut self, mut io: IO) -> io::Result { 221 | // if the buffer is empty, just return. 222 | let buffer = self.buffer.as_ref().expect("buffer ref expected"); 223 | if buffer.is_empty() { 224 | return Ok(0); 225 | } 226 | 227 | // buffer is not empty now. write it. 228 | // # Safety 229 | // We have already checked it is not None. 230 | let buffer = unsafe { self.buffer.take().unwrap_unchecked() }; 231 | let (result, buffer) = io.write_all(buffer).await; 232 | self.buffer = Some(buffer); 233 | match result { 234 | Ok(written_len) => { 235 | unsafe { self.buffer.as_mut().unwrap_unchecked().advance(written_len) }; 236 | Ok(written_len) 237 | } 238 | Err(e) => { 239 | let rerr = e.kind().into(); 240 | self.status = WriteStatus::Err(e); 241 | Err(rerr) 242 | } 243 | } 244 | } 245 | } 246 | 247 | impl io::Write for SafeWrite { 248 | /// `write` to buffer. 249 | /// # Handle return value 250 | /// 1. Err(WouldBlock): the buffer is full, call do_io to flush it. 251 | /// 2. _: handle it. 252 | fn write(&mut self, buf: &[u8]) -> io::Result { 253 | // if there is too much data inside the buffer, return WoundBlock 254 | let buffer = self.buffer.as_mut().expect("buffer mut expected"); 255 | match mem::replace(&mut self.status, WriteStatus::Ok) { 256 | WriteStatus::Err(e) => return Err(e), 257 | WriteStatus::Ok if buffer.is_full() => return Err(io::ErrorKind::WouldBlock.into()), 258 | _ => (), 259 | } 260 | 261 | // there is space inside the buffer, copy to it. 262 | let to_copy = buf.len().min(buffer.available()); 263 | unsafe { std::ptr::copy_nonoverlapping(buf.as_ptr(), buffer.write_ptr(), to_copy) }; 264 | unsafe { buffer.set_init(to_copy) }; 265 | Ok(to_copy) 266 | } 267 | 268 | /// `flush` to buffer. 269 | /// # Handle return value 270 | /// 1. Err(WouldBlock): the buffer is full, call do_io to flush it. 271 | /// 2. _: handle it. 272 | fn flush(&mut self) -> io::Result<()> { 273 | let buffer = self.buffer.as_mut().expect("buffer mut expected"); 274 | match mem::replace(&mut self.status, WriteStatus::Ok) { 275 | WriteStatus::Err(e) => Err(e), 276 | WriteStatus::Ok if !buffer.is_empty() => Err(io::ErrorKind::WouldBlock.into()), 277 | _ => Ok(()), 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /monoio-io-wrapper/src/unsafe_io.rs: -------------------------------------------------------------------------------- 1 | use std::{io, mem}; 2 | 3 | use monoio::{ 4 | buf::{IoBuf, IoBufMut}, 5 | io::{AsyncReadRent, AsyncWriteRent}, 6 | }; 7 | 8 | /// Used by both UnsafeRead and UnsafeWrite. 9 | #[derive(Debug)] 10 | enum Status { 11 | /// We haven't do real io, and maybe the dest is recorded. 12 | WaitFill(Option<(*const u8, usize)>), 13 | /// We have already do real io. The length maybe zero or non-zero. 14 | Filled(Result), 15 | } 16 | 17 | impl Default for Status { 18 | fn default() -> Self { 19 | Status::WaitFill(None) 20 | } 21 | } 22 | 23 | /// UnsafeRead is a wrapper of some meta data. 24 | /// It implements std::io::Read trait. But it do real io in an async way. 25 | /// On the first read, it may returns WouldBlock error, which means the 26 | /// `fullfill` should be called to do real io. 27 | /// The data is read directly into the dest that last std read passes. 28 | /// Note that this action is an unsafe hack to avoid data copy. 29 | /// You can only use this wrapper when you make sure the read dest is always 30 | /// a valid buffer. 31 | #[derive(Default, Debug)] 32 | pub struct UnsafeRead { 33 | status: Status, 34 | } 35 | 36 | impl UnsafeRead { 37 | pub const fn new() -> Self { 38 | Self { 39 | status: Status::WaitFill(None), 40 | } 41 | } 42 | 43 | /// `do_io` must be called after calling to io::Read::read. 44 | /// # Handle return value 45 | /// 1. Ok(n): previous read data length. 46 | /// 2. Err(e): previous error. 47 | /// 3. Err(WouldBlock): need calling read to capture ptr and len. 48 | /// # Safety 49 | /// User must make sure the former buffer is still valid until io finished. 50 | pub async unsafe fn do_io(&mut self, mut io: IO) -> io::Result { 51 | match self.status { 52 | Status::WaitFill(Some((ptr, len))) => { 53 | let buf = RawBuf { ptr, len }; 54 | let read_ret = io.read(buf).await.0; 55 | let rret: io::Result = match &read_ret { 56 | Ok(n) => Ok(*n), 57 | Err(e) => Err(e.kind().into()), 58 | }; 59 | self.status = Status::Filled(read_ret); 60 | rret 61 | } 62 | Status::WaitFill(None) => Err(io::ErrorKind::WouldBlock.into()), 63 | Status::Filled(ref prev_ret) => match prev_ret { 64 | Ok(n) => Ok(*n), 65 | Err(e) => Err(e.kind().into()), 66 | }, 67 | } 68 | } 69 | 70 | /// Clear status. 71 | pub fn reset(&mut self) { 72 | self.status = Status::WaitFill(None); 73 | } 74 | } 75 | 76 | impl io::Read for UnsafeRead { 77 | /// `read` from buffer(not really a buffer). 78 | /// # Handle return value 79 | /// 1. Err(WouldBlock): ptr and len captured, need call do_io and then retry read. 80 | /// 2. _: handle it. 81 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 82 | match mem::replace(&mut self.status, Status::WaitFill(None)) { 83 | Status::WaitFill(_) => { 84 | let ptr = buf.as_ptr(); 85 | let len = buf.len(); 86 | self.status = Status::WaitFill(Some((ptr, len))); 87 | Err(io::ErrorKind::WouldBlock.into()) 88 | } 89 | Status::Filled(ret) => ret, 90 | } 91 | } 92 | } 93 | 94 | /// UnsafeWrite behaves like `UnsafeRead`. 95 | #[derive(Default, Debug)] 96 | pub struct UnsafeWrite { 97 | status: Status, 98 | } 99 | 100 | impl UnsafeWrite { 101 | pub const fn new() -> Self { 102 | Self { 103 | status: Status::WaitFill(None), 104 | } 105 | } 106 | 107 | /// `do_io` must be called after calling to io::Write::write. 108 | /// # Handle return value 109 | /// 1. Ok(n): previous written data length. 110 | /// 2. Err(e): previous error. 111 | /// 3. Err(WouldBlock): need calling write to capture ptr and len. 112 | /// # Safety 113 | /// User must make sure the former buffer is still valid until io finished. 114 | pub async unsafe fn do_io(&mut self, mut io: IO) -> io::Result { 115 | match self.status { 116 | Status::WaitFill(Some((ptr, len))) => { 117 | let buf = RawBuf { ptr, len }; 118 | let write_ret = io.write(buf).await.0; 119 | let rret: io::Result = match &write_ret { 120 | Ok(n) => Ok(*n), 121 | Err(e) => Err(e.kind().into()), 122 | }; 123 | self.status = Status::Filled(write_ret); 124 | rret 125 | } 126 | Status::WaitFill(None) => Err(io::ErrorKind::WouldBlock.into()), 127 | Status::Filled(ref prev_ret) => match prev_ret { 128 | Ok(n) => Ok(*n), 129 | Err(e) => Err(e.kind().into()), 130 | }, 131 | } 132 | } 133 | 134 | /// Clear status. 135 | pub fn reset(&mut self) { 136 | self.status = Status::WaitFill(None); 137 | } 138 | } 139 | 140 | impl io::Write for UnsafeWrite { 141 | /// `read` to buffer. 142 | /// # Handle return value 143 | /// 1. Err(WouldBlock): ptr and len captured, need call do_io and then retry write. 144 | /// 2. _: handle it. 145 | fn write(&mut self, buf: &[u8]) -> io::Result { 146 | match mem::replace(&mut self.status, Status::WaitFill(None)) { 147 | Status::WaitFill(_) => { 148 | let ptr = buf.as_ptr(); 149 | let len = buf.len(); 150 | self.status = Status::WaitFill(Some((ptr, len))); 151 | Err(io::ErrorKind::WouldBlock.into()) 152 | } 153 | Status::Filled(ret) => ret, 154 | } 155 | } 156 | 157 | fn flush(&mut self) -> io::Result<()> { 158 | Ok(()) 159 | } 160 | } 161 | 162 | /// RawBuf is a wrapper of buffer ptr and len. 163 | /// It seems that it hold the ownership of the buffer, which infact not. 164 | /// Use this wrapper only when you can make sure the buffer ptr lives 165 | /// longer than the wrapper. 166 | struct RawBuf { 167 | ptr: *const u8, 168 | len: usize, 169 | } 170 | 171 | unsafe impl IoBuf for RawBuf { 172 | fn read_ptr(&self) -> *const u8 { 173 | self.ptr 174 | } 175 | 176 | fn bytes_init(&self) -> usize { 177 | self.len 178 | } 179 | } 180 | 181 | unsafe impl IoBufMut for RawBuf { 182 | fn write_ptr(&mut self) -> *mut u8 { 183 | self.ptr as *mut u8 184 | } 185 | 186 | fn bytes_total(&mut self) -> usize { 187 | self.len 188 | } 189 | 190 | unsafe fn set_init(&mut self, _pos: usize) {} 191 | } 192 | -------------------------------------------------------------------------------- /monoio-native-tls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoio-native-tls" 3 | version = "0.4.0" 4 | 5 | authors = ["ChiHai ", "Rain Jiang "] 6 | categories = ["asynchronous", "cryptography", "network-programming"] 7 | description = "Asynchronous TLS streams wrapper for Monoio based on NativeTLS." 8 | edition = "2021" 9 | homepage = "https://github.com/monoio-rs/monoio-tls" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.md" 12 | repository = "https://github.com/monoio-rs/monoio-tls" 13 | 14 | [dependencies] 15 | monoio = { workspace = true } 16 | bytes = { workspace = true } 17 | thiserror = { workspace = true } 18 | 19 | monoio-io-wrapper = { version = "0.1.1", path = "../monoio-io-wrapper" } 20 | native-tls = { version = "0.2" } 21 | 22 | openssl-sys = { version = "0.9", optional = true } 23 | tracing = { version = "0.1", optional = true } 24 | 25 | [features] 26 | default = [] 27 | alpn = ["native-tls/alpn"] 28 | vendored = ["native-tls/vendored"] 29 | qat = ["openssl-sys", "tracing"] 30 | -------------------------------------------------------------------------------- /monoio-native-tls/README.md: -------------------------------------------------------------------------------- 1 | # Monoio-native-tls 2 | -------------------------------------------------------------------------------- /monoio-native-tls/examples/connect.rs: -------------------------------------------------------------------------------- 1 | use monoio::{ 2 | io::{AsyncReadRent, AsyncWriteRentExt}, 3 | net::TcpStream, 4 | }; 5 | use monoio_native_tls::TlsConnector; 6 | use native_tls::TlsConnector as NativeTlsConnector; 7 | 8 | #[monoio::main] 9 | async fn main() { 10 | let connector = NativeTlsConnector::builder().build().unwrap(); 11 | let connector = TlsConnector::from(connector); 12 | 13 | let stream = TcpStream::connect("rsproxy.cn:443").await.unwrap(); 14 | println!("rsproxy.cn:443 connected"); 15 | 16 | let mut stream = connector.connect("rsproxy.cn", stream).await.unwrap(); 17 | println!("handshake success"); 18 | 19 | let content = b"GET / HTTP/1.0\r\nHost: rsproxy.cn\r\n\r\n"; 20 | let (r, _) = stream.write_all(content).await; 21 | r.expect("unable to write http request"); 22 | println!("http request sent"); 23 | 24 | let buf = vec![0_u8; 64]; 25 | let (r, buf) = stream.read(buf).await; 26 | r.expect("unable to read http response"); 27 | let resp = String::from_utf8(buf).unwrap(); 28 | println!("http response recv: \n\n{}", resp); 29 | } 30 | -------------------------------------------------------------------------------- /monoio-native-tls/src/client.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent}; 4 | 5 | use crate::{ 6 | utils::{handshake, IOWrapper}, 7 | TlsError, TlsStream, 8 | }; 9 | 10 | /// A wrapper around a `native_tls::TlsConnector`, providing an async `connect` 11 | /// method. 12 | #[derive(Clone)] 13 | pub struct TlsConnector { 14 | inner: native_tls::TlsConnector, 15 | read_buffer: Option, 16 | write_buffer: Option, 17 | } 18 | 19 | impl TlsConnector { 20 | /// Connects the provided stream with this connector, assuming the provided 21 | /// domain. 22 | /// 23 | /// This function will internally call `TlsConnector::connect` to connect 24 | /// the stream and returns a future representing the resolution of the 25 | /// connection operation. The returned future will resolve to either 26 | /// `TlsStream` or `Error` depending if it's successful or not. 27 | /// 28 | /// This is typically used for clients who have already established, for 29 | /// example, a TCP connection to a remote server. That stream is then 30 | /// provided here to perform the client half of a connection to a 31 | /// TLS-powered server. 32 | pub async fn connect(&self, domain: &str, stream: S) -> Result, TlsError> 33 | where 34 | S: AsyncReadRent + AsyncWriteRent, 35 | { 36 | let io = IOWrapper::new_with_buffer_size(stream, self.read_buffer, self.write_buffer); 37 | handshake(move |s_wrap| self.inner.connect(domain, s_wrap), io).await 38 | } 39 | 40 | pub fn read_buffer(mut self, size: Option) -> Self { 41 | self.read_buffer = size; 42 | self 43 | } 44 | 45 | pub fn write_buffer(mut self, size: Option) -> Self { 46 | self.write_buffer = size; 47 | self 48 | } 49 | } 50 | 51 | impl fmt::Debug for TlsConnector { 52 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 | f.debug_struct("TlsConnector").finish() 54 | } 55 | } 56 | 57 | impl From for TlsConnector { 58 | fn from(inner: native_tls::TlsConnector) -> TlsConnector { 59 | TlsConnector { 60 | inner, 61 | read_buffer: None, 62 | write_buffer: None, 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /monoio-native-tls/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum TlsError { 7 | #[error("io error")] 8 | Io(#[from] std::io::Error), 9 | #[error("native-tls error")] 10 | NativeTls(#[from] native_tls::Error), 11 | } 12 | 13 | impl From for io::Error { 14 | fn from(e: TlsError) -> Self { 15 | match e { 16 | TlsError::Io(e) => e, 17 | TlsError::NativeTls(e) => io::Error::new(io::ErrorKind::Other, e), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /monoio-native-tls/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use openssl_sys::{c_char, c_int, ENGINE}; 2 | 3 | extern "C" { 4 | pub fn ENGINE_init(engine: *mut ENGINE) -> c_int; 5 | pub fn ENGINE_by_id(id: *const c_char) -> *mut ENGINE; 6 | pub fn ENGINE_set_default(engine: *mut ENGINE, flags: c_int) -> c_int; 7 | } 8 | 9 | pub fn init_openssl_engine(name: &std::ffi::CStr) { 10 | openssl_sys::init(); 11 | 12 | unsafe { 13 | let engine = ENGINE_by_id(name.as_ptr()); 14 | if engine as usize == 0 { 15 | tracing::info!("engine: unknown"); 16 | return; 17 | } 18 | let rc = ENGINE_init(engine); 19 | if rc == 0 { 20 | tracing::error!("engine: initialize failed"); 21 | return; 22 | } 23 | tracing::info!("engine: {}", engine as usize); 24 | let rc = ENGINE_set_default(engine, 0xffff); 25 | if rc == 0 { 26 | tracing::error!("engine: initialize failed"); 27 | } 28 | tracing::info!("engine: register successfully"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /monoio-native-tls/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod client; 2 | mod error; 3 | mod server; 4 | mod stream; 5 | mod utils; 6 | 7 | pub use client::TlsConnector; 8 | pub use error::TlsError; 9 | pub use server::TlsAcceptor; 10 | pub use stream::TlsStream; 11 | 12 | #[cfg(feature = "qat")] 13 | mod ffi; 14 | 15 | pub fn init() { 16 | #[cfg(feature = "qat")] 17 | static INIT_ONCE: std::sync::Once = std::sync::Once::new(); 18 | #[cfg(feature = "qat")] 19 | const LKCF_ENGINE: &[u8] = b"lkcf-engine\0"; 20 | 21 | #[cfg(feature = "qat")] 22 | INIT_ONCE.call_once(|| { 23 | ffi::init_openssl_engine(unsafe { 24 | std::ffi::CStr::from_bytes_with_nul_unchecked(LKCF_ENGINE) 25 | }) 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /monoio-native-tls/src/server.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent}; 4 | 5 | use crate::{ 6 | utils::{handshake, IOWrapper}, 7 | TlsError, TlsStream, 8 | }; 9 | 10 | /// A wrapper around a `native_tls::TlsAcceptor`, providing an async `accept` 11 | /// method. 12 | #[derive(Clone)] 13 | pub struct TlsAcceptor { 14 | inner: native_tls::TlsAcceptor, 15 | read_buffer: Option, 16 | write_buffer: Option, 17 | } 18 | 19 | impl TlsAcceptor { 20 | /// Accepts a new client connection with the provided stream. 21 | /// 22 | /// This function will internally call `TlsAcceptor::accept` to connect 23 | /// the stream and returns a future representing the resolution of the 24 | /// connection operation. The returned future will resolve to either 25 | /// `TlsStream` or `Error` depending if it's successful or not. 26 | /// 27 | /// This is typically used after a new socket has been accepted from a 28 | /// `TcpListener`. That socket is then passed to this function to perform 29 | /// the server half of accepting a client connection. 30 | pub async fn accept(&self, stream: S) -> Result, TlsError> 31 | where 32 | S: AsyncReadRent + AsyncWriteRent, 33 | { 34 | let io = IOWrapper::new_with_buffer_size(stream, self.read_buffer, self.write_buffer); 35 | handshake(move |s_wrap| self.inner.accept(s_wrap), io).await 36 | } 37 | 38 | pub fn read_buffer(mut self, size: Option) -> Self { 39 | self.read_buffer = size; 40 | self 41 | } 42 | 43 | pub fn write_buffer(mut self, size: Option) -> Self { 44 | self.write_buffer = size; 45 | self 46 | } 47 | } 48 | 49 | impl fmt::Debug for TlsAcceptor { 50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 51 | f.debug_struct("TlsAcceptor").finish() 52 | } 53 | } 54 | 55 | impl From for TlsAcceptor { 56 | fn from(inner: native_tls::TlsAcceptor) -> TlsAcceptor { 57 | TlsAcceptor { 58 | inner, 59 | read_buffer: None, 60 | write_buffer: None, 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /monoio-native-tls/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write}; 2 | 3 | use monoio::{ 4 | buf::{IoBuf, IoBufMut, IoVecBuf, IoVecBufMut, RawBuf}, 5 | io::{AsyncReadRent, AsyncWriteRent, Split}, 6 | BufResult, 7 | }; 8 | 9 | use crate::utils::{Buffers, IOWrapper}; 10 | 11 | /// A wrapper around an underlying raw stream which implements the TLS or SSL 12 | /// protocol. 13 | /// 14 | /// A `TlsStream` represents a handshake that has been completed successfully 15 | /// and both the server and the client are ready for receiving and sending 16 | /// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written 17 | /// to a `TlsStream` are encrypted when passing through to `S`. 18 | #[derive(Debug)] 19 | pub struct TlsStream { 20 | tls: native_tls::TlsStream, 21 | io: IOWrapper, 22 | } 23 | 24 | impl TlsStream { 25 | pub(crate) fn new(tls_stream: native_tls::TlsStream, io: IOWrapper) -> Self { 26 | Self { 27 | tls: tls_stream, 28 | io, 29 | } 30 | } 31 | 32 | pub fn into_inner(self) -> S { 33 | self.io.into_parts().0 34 | } 35 | 36 | #[cfg(feature = "alpn")] 37 | pub fn alpn_protocol(&self) -> Option> { 38 | self.tls.negotiated_alpn().ok().flatten() 39 | } 40 | } 41 | 42 | unsafe impl Split for TlsStream {} 43 | 44 | impl AsyncReadRent for TlsStream { 45 | #[allow(clippy::await_holding_refcell_ref)] 46 | async fn read(&mut self, mut buf: T) -> BufResult { 47 | let slice = unsafe { std::slice::from_raw_parts_mut(buf.write_ptr(), buf.bytes_total()) }; 48 | 49 | loop { 50 | // read from native-tls to buffer 51 | match self.tls.read(slice) { 52 | Ok(n) => { 53 | unsafe { buf.set_init(n) }; 54 | return (Ok(n), buf); 55 | } 56 | // we need more data, read something. 57 | Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (), 58 | Err(e) => { 59 | return (Err(e), buf); 60 | } 61 | } 62 | 63 | // now we need data, read something into native-tls 64 | match unsafe { self.io.do_read_io() }.await { 65 | Ok(0) => { 66 | return (Ok(0), buf); 67 | } 68 | Ok(_) => (), 69 | Err(e) => { 70 | return (Err(e), buf); 71 | } 72 | }; 73 | } 74 | } 75 | 76 | async fn readv(&mut self, mut buf: T) -> BufResult { 77 | let n = match unsafe { RawBuf::new_from_iovec_mut(&mut buf) } { 78 | Some(raw_buf) => self.read(raw_buf).await.0, 79 | None => Ok(0), 80 | }; 81 | if let Ok(n) = n { 82 | unsafe { buf.set_init(n) }; 83 | } 84 | (n, buf) 85 | } 86 | } 87 | 88 | impl AsyncWriteRent for TlsStream { 89 | #[allow(clippy::await_holding_refcell_ref)] 90 | async fn write(&mut self, buf: T) -> BufResult { 91 | // construct slice 92 | let slice = unsafe { std::slice::from_raw_parts(buf.read_ptr(), buf.bytes_init()) }; 93 | 94 | loop { 95 | // write slice to native-tls and buffer 96 | let maybe_n = match self.tls.write(slice) { 97 | Ok(n) => Some(n), 98 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => None, 99 | Err(e) => return (Err(e), buf), 100 | }; 101 | 102 | // write from buffer to connection 103 | if let Err(e) = unsafe { self.io.do_write_io() }.await { 104 | return (Err(e), buf); 105 | } 106 | 107 | if let Some(n) = maybe_n { 108 | return (Ok(n), buf); 109 | } 110 | } 111 | } 112 | 113 | // TODO: use real writev 114 | async fn writev(&mut self, buf_vec: T) -> BufResult { 115 | let n = match unsafe { RawBuf::new_from_iovec(&buf_vec) } { 116 | Some(raw_buf) => self.write(raw_buf).await.0, 117 | None => Ok(0), 118 | }; 119 | (n, buf_vec) 120 | } 121 | 122 | #[allow(clippy::await_holding_refcell_ref)] 123 | async fn flush(&mut self) -> io::Result<()> { 124 | loop { 125 | match self.tls.flush() { 126 | Ok(_) => { 127 | unsafe { self.io.do_write_io() }.await?; 128 | return Ok(()); 129 | } 130 | Err(e) if e.kind() == io::ErrorKind::WouldBlock => { 131 | unsafe { self.io.do_write_io() }.await?; 132 | } 133 | Err(e) => { 134 | return Err(e); 135 | } 136 | } 137 | } 138 | } 139 | 140 | async fn shutdown(&mut self) -> io::Result<()> { 141 | self.tls.shutdown()?; 142 | unsafe { self.io.do_write_io() }.await?; 143 | Ok(()) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /monoio-native-tls/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::UnsafeCell, io, rc::Rc}; 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent}; 4 | use monoio_io_wrapper::{ReadBuffer, WriteBuffer}; 5 | use native_tls::HandshakeError as NativeHandshakeError; 6 | 7 | use crate::{TlsError, TlsStream}; 8 | 9 | #[derive(Debug, Clone)] 10 | pub(crate) struct Buffers { 11 | r_buffer: Rc>, 12 | w_buffer: Rc>, 13 | } 14 | 15 | #[derive(Debug)] 16 | pub(crate) struct IOWrapper { 17 | io: IO, 18 | r_buffer: Rc>, 19 | w_buffer: Rc>, 20 | } 21 | 22 | impl IOWrapper { 23 | pub(crate) fn new(io: IO, r_buffer: ReadBuffer, w_buffer: WriteBuffer) -> Self { 24 | Self { 25 | io, 26 | r_buffer: Rc::new(UnsafeCell::new(r_buffer)), 27 | w_buffer: Rc::new(UnsafeCell::new(w_buffer)), 28 | } 29 | } 30 | 31 | pub(crate) fn new_with_buffer_size(io: IO, r: Option, w: Option) -> Self { 32 | let r_buffer = match r { 33 | Some(rb) => ReadBuffer::new(rb), 34 | None => ReadBuffer::default(), 35 | }; 36 | let w_buffer = match w { 37 | Some(rb) => WriteBuffer::new(rb), 38 | None => WriteBuffer::default(), 39 | }; 40 | 41 | Self::new(io, r_buffer, w_buffer) 42 | } 43 | 44 | pub(crate) fn buffers(&self) -> Buffers { 45 | Buffers { 46 | r_buffer: self.r_buffer.clone(), 47 | w_buffer: self.w_buffer.clone(), 48 | } 49 | } 50 | 51 | pub(crate) fn into_parts( 52 | self, 53 | ) -> (IO, Rc>, Rc>) { 54 | (self.io, self.r_buffer, self.w_buffer) 55 | } 56 | 57 | pub(crate) async unsafe fn do_read_io(&mut self) -> std::io::Result 58 | where 59 | IO: AsyncReadRent, 60 | { 61 | (*self.r_buffer.get()).do_io(&mut self.io).await 62 | } 63 | 64 | pub(crate) async unsafe fn do_write_io(&mut self) -> std::io::Result 65 | where 66 | IO: AsyncWriteRent, 67 | { 68 | (*self.w_buffer.get()).do_io(&mut self.io).await 69 | } 70 | } 71 | 72 | impl IOWrapper { 73 | #[inline] 74 | #[allow(clippy::await_holding_refcell_ref)] 75 | pub(crate) async fn read_io(&mut self) -> io::Result { 76 | unsafe { &mut *self.r_buffer.get() } 77 | .do_io(&mut self.io) 78 | .await 79 | } 80 | } 81 | 82 | impl IOWrapper { 83 | #[inline] 84 | #[allow(clippy::await_holding_refcell_ref)] 85 | pub(crate) async fn write_io(&mut self) -> io::Result { 86 | unsafe { &mut *self.w_buffer.get() } 87 | .do_io(&mut self.io) 88 | .await 89 | } 90 | } 91 | 92 | impl io::Read for Buffers { 93 | #[inline] 94 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 95 | unsafe { &mut *self.r_buffer.get() }.read(buf) 96 | } 97 | } 98 | 99 | impl io::Write for Buffers { 100 | #[inline] 101 | fn write(&mut self, buf: &[u8]) -> io::Result { 102 | unsafe { &mut *self.w_buffer.get() }.write(buf) 103 | } 104 | 105 | #[inline] 106 | fn flush(&mut self) -> io::Result<()> { 107 | // Due to openssl and rust-openssl issue, in 108 | // flush we cannot return WouldBlock. 109 | // Related PRs: 110 | // https://github.com/openssl/openssl/pull/20919 111 | // https://github.com/sfackler/rust-openssl/pull/1922 112 | // After these PRs are merged, we should use: 113 | // unsafe { &mut *self.w_buffer.get() }.flush() 114 | Ok(()) 115 | } 116 | } 117 | 118 | pub(crate) async fn handshake(f: F, mut io: IOWrapper) -> Result, TlsError> 119 | where 120 | F: FnOnce(Buffers) -> Result, NativeHandshakeError>, 121 | S: AsyncReadRent + AsyncWriteRent, 122 | { 123 | let mut mid = match f(io.buffers()) { 124 | Ok(tls) => { 125 | io.write_io().await?; 126 | return Ok(TlsStream::new(tls, io)); 127 | } 128 | Err(NativeHandshakeError::WouldBlock(s)) => s, 129 | Err(NativeHandshakeError::Failure(e)) => return Err(e.into()), 130 | }; 131 | 132 | loop { 133 | if io.write_io().await? == 0 { 134 | io.read_io().await?; 135 | } 136 | 137 | match mid.handshake() { 138 | Ok(tls) => { 139 | io.write_io().await?; 140 | return Ok(TlsStream::new(tls, io)); 141 | } 142 | Err(NativeHandshakeError::WouldBlock(s)) => mid = s, 143 | Err(NativeHandshakeError::Failure(e)) => return Err(e.into()), 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /monoio-rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monoio-rustls" 3 | version = "0.4.0" 4 | 5 | authors = ["ChiHai ", "Rain Jiang "] 6 | categories = ["asynchronous", "cryptography", "network-programming"] 7 | description = "Asynchronous TLS streams wrapper for Monoio based on Rustls." 8 | edition = "2021" 9 | homepage = "https://github.com/monoio-rs/monoio-tls" 10 | license = "MIT/Apache-2.0" 11 | readme = "README.md" 12 | repository = "https://github.com/monoio-rs/monoio-tls" 13 | 14 | [dependencies] 15 | monoio = { workspace = true } 16 | bytes = { workspace = true } 17 | thiserror = { workspace = true } 18 | 19 | monoio-io-wrapper = { version = "0.1.1", path = "../monoio-io-wrapper" } 20 | rustls = { version = "~0.23.4", default-features = false, features = ["std"] } 21 | 22 | [features] 23 | default = ["logging", "tls12"] 24 | logging = ["rustls/logging"] 25 | tls12 = ["rustls/tls12"] 26 | # Once unsafe_io is enabled, you may not drop the future before it returns ready. 27 | # It saves one buffer copy than disabled. 28 | unsafe_io = ["monoio-io-wrapper/unsafe_io"] 29 | 30 | [dev-dependencies] 31 | monoio = { workspace = true } 32 | webpki-roots = "~0.26.1" 33 | -------------------------------------------------------------------------------- /monoio-rustls/README.md: -------------------------------------------------------------------------------- 1 | # Monoio-rustls 2 | -------------------------------------------------------------------------------- /monoio-rustls/examples/connect.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use monoio::{ 4 | io::{AsyncReadRent, AsyncWriteRentExt}, 5 | net::TcpStream, 6 | }; 7 | use monoio_rustls::TlsConnector; 8 | use rustls::{RootCertStore}; 9 | 10 | #[monoio::main] 11 | async fn main() { 12 | let mut root_store = RootCertStore::empty(); 13 | root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); 14 | let config = rustls::ClientConfig::builder() 15 | .with_root_certificates(root_store) 16 | .with_no_client_auth(); 17 | 18 | let connector = TlsConnector::from(Arc::new(config)); 19 | let stream = TcpStream::connect("rsproxy.cn:443").await.unwrap(); 20 | println!("rsproxy.cn:443 connected"); 21 | 22 | let domain = rustls::pki_types::ServerName::try_from("rsproxy.cn").unwrap(); 23 | let mut stream = connector.connect(domain, stream).await.unwrap(); 24 | println!("handshake success"); 25 | 26 | let content = b"GET / HTTP/1.0\r\nHost: rsproxy.cn\r\n\r\n"; 27 | let (r, _) = stream.write_all(content).await; 28 | r.expect("unable to write http request"); 29 | println!("http request sent"); 30 | 31 | let buf = vec![0_u8; 64]; 32 | let (r, buf) = stream.read(buf).await; 33 | r.expect("unable to read http response"); 34 | let resp = String::from_utf8(buf).unwrap(); 35 | println!("http response recv: \n\n{}", resp); 36 | } 37 | -------------------------------------------------------------------------------- /monoio-rustls/src/client.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent, OwnedReadHalf, OwnedWriteHalf}; 4 | use rustls::{pki_types::ServerName, ClientConfig, ClientConnection}; 5 | 6 | use crate::{stream::Stream, TlsError}; 7 | 8 | /// A wrapper around an underlying raw stream which implements the TLS protocol. 9 | pub type TlsStream = Stream; 10 | /// TlsStream for read only. 11 | pub type TlsStreamReadHalf = OwnedReadHalf>; 12 | /// TlsStream for write only. 13 | pub type TlsStreamWriteHalf = OwnedWriteHalf>; 14 | 15 | /// A wrapper around a `rustls::ClientConfig`, providing an async `connect` method. 16 | #[derive(Clone)] 17 | pub struct TlsConnector { 18 | inner: Arc, 19 | #[cfg(feature = "unsafe_io")] 20 | unsafe_io: bool, 21 | } 22 | 23 | impl From> for TlsConnector { 24 | fn from(inner: Arc) -> TlsConnector { 25 | TlsConnector { 26 | inner, 27 | #[cfg(feature = "unsafe_io")] 28 | unsafe_io: false, 29 | } 30 | } 31 | } 32 | 33 | impl From for TlsConnector { 34 | fn from(inner: ClientConfig) -> TlsConnector { 35 | TlsConnector { 36 | inner: Arc::new(inner), 37 | #[cfg(feature = "unsafe_io")] 38 | unsafe_io: false, 39 | } 40 | } 41 | } 42 | 43 | impl TlsConnector { 44 | /// Enable unsafe-io. 45 | /// # Safety 46 | /// Users must make sure the buffer ptr and len is valid until io finished. 47 | /// So the Future cannot be dropped directly. Consider using CancellableIO. 48 | #[cfg(feature = "unsafe_io")] 49 | pub unsafe fn unsafe_io(self, enabled: bool) -> Self { 50 | Self { 51 | unsafe_io: enabled, 52 | ..self 53 | } 54 | } 55 | 56 | pub async fn connect( 57 | &self, 58 | domain: ServerName<'static>, 59 | stream: IO, 60 | ) -> Result, TlsError> 61 | where 62 | IO: AsyncReadRent + AsyncWriteRent, 63 | { 64 | let session = ClientConnection::new(self.inner.clone(), domain)?; 65 | #[cfg(feature = "unsafe_io")] 66 | let mut stream = if self.unsafe_io { 67 | // # Safety 68 | // Users already maked unsafe io. 69 | unsafe { Stream::new_unsafe(stream, session) } 70 | } else { 71 | Stream::new(stream, session) 72 | }; 73 | #[cfg(not(feature = "unsafe_io"))] 74 | let mut stream = Stream::new(stream, session); 75 | stream.handshake().await?; 76 | Ok(stream) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /monoio-rustls/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum TlsError { 7 | #[error("io error")] 8 | Io(#[from] std::io::Error), 9 | #[error("rustls error")] 10 | Rustls(#[from] rustls::Error), 11 | } 12 | 13 | impl From for io::Error { 14 | fn from(e: TlsError) -> Self { 15 | match e { 16 | TlsError::Io(e) => e, 17 | TlsError::Rustls(e) => io::Error::new(io::ErrorKind::Other, e), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /monoio-rustls/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(stable_features)] 2 | 3 | mod client; 4 | mod error; 5 | mod server; 6 | mod stream; 7 | 8 | pub use client::{ 9 | TlsConnector, TlsStream as ClientTlsStream, TlsStreamReadHalf as ClientTlsStreamReadHalf, 10 | TlsStreamWriteHalf as ClientTlsStreamWriteHalf, 11 | }; 12 | pub use error::TlsError; 13 | pub use server::{ 14 | TlsAcceptor, TlsStream as ServerTlsStream, TlsStreamReadHalf as ServerTlsStreamReadHalf, 15 | TlsStreamWriteHalf as ServerTlsStreamWriteHalf, 16 | }; 17 | 18 | /// A wrapper around an underlying raw stream which implements the TLS protocol. 19 | pub type TlsStream = stream::Stream; 20 | 21 | impl From> for TlsStream { 22 | fn from(value: ClientTlsStream) -> Self { 23 | value.map_conn(|c| c.into()) 24 | } 25 | } 26 | 27 | impl From> for TlsStream { 28 | fn from(value: ServerTlsStream) -> Self { 29 | value.map_conn(|c| c.into()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /monoio-rustls/src/server.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use monoio::io::{AsyncReadRent, AsyncWriteRent, OwnedReadHalf, OwnedWriteHalf}; 4 | use rustls::{ServerConfig, ServerConnection}; 5 | 6 | use crate::{stream::Stream, TlsError}; 7 | 8 | /// A wrapper around an underlying raw stream which implements the TLS protocol. 9 | pub type TlsStream = Stream; 10 | /// TlsStream for read only. 11 | pub type TlsStreamReadHalf = OwnedReadHalf>; 12 | /// TlsStream for write only. 13 | pub type TlsStreamWriteHalf = OwnedWriteHalf>; 14 | 15 | /// A wrapper around a `rustls::ServerConfig`, providing an async `accept` method. 16 | #[derive(Clone)] 17 | pub struct TlsAcceptor { 18 | inner: Arc, 19 | #[cfg(feature = "unsafe_io")] 20 | unsafe_io: bool, 21 | } 22 | 23 | impl From> for TlsAcceptor { 24 | fn from(inner: Arc) -> TlsAcceptor { 25 | TlsAcceptor { 26 | inner, 27 | #[cfg(feature = "unsafe_io")] 28 | unsafe_io: false, 29 | } 30 | } 31 | } 32 | 33 | impl From for TlsAcceptor { 34 | fn from(inner: ServerConfig) -> TlsAcceptor { 35 | TlsAcceptor { 36 | inner: Arc::new(inner), 37 | #[cfg(feature = "unsafe_io")] 38 | unsafe_io: false, 39 | } 40 | } 41 | } 42 | 43 | impl TlsAcceptor { 44 | /// Enable unsafe-io. 45 | /// # Safety 46 | /// Users must make sure the buffer ptr and len is valid until io finished. 47 | /// So the Future cannot be dropped directly. Consider using CancellableIO. 48 | #[cfg(feature = "unsafe_io")] 49 | pub unsafe fn unsafe_io(self, enabled: bool) -> Self { 50 | Self { 51 | unsafe_io: enabled, 52 | ..self 53 | } 54 | } 55 | 56 | pub async fn accept(&self, stream: IO) -> Result, TlsError> 57 | where 58 | IO: AsyncReadRent + AsyncWriteRent, 59 | { 60 | let session = ServerConnection::new(self.inner.clone())?; 61 | #[cfg(feature = "unsafe_io")] 62 | let mut stream = if self.unsafe_io { 63 | // # Safety 64 | // Users already maked unsafe io. 65 | unsafe { Stream::new_unsafe(stream, session) } 66 | } else { 67 | Stream::new(stream, session) 68 | }; 69 | #[cfg(not(feature = "unsafe_io"))] 70 | let mut stream = Stream::new(stream, session); 71 | stream.handshake().await?; 72 | Ok(stream) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /monoio-rustls/src/stream.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{self, Read, Write}, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | use monoio::{ 7 | buf::{IoBuf, IoBufMut, IoVecBuf, IoVecBufMut, RawBuf}, 8 | io::{AsyncReadRent, AsyncWriteRent, Split}, 9 | BufResult, 10 | }; 11 | use monoio_io_wrapper::{ReadBuffer, WriteBuffer}; 12 | use rustls::{ClientConnection, ConnectionCommon, ServerConnection, SideData}; 13 | 14 | #[derive(Debug)] 15 | pub struct Stream { 16 | pub(crate) io: IO, 17 | pub(crate) session: C, 18 | r_buffer: ReadBuffer, 19 | w_buffer: WriteBuffer, 20 | } 21 | 22 | impl Stream { 23 | #[inline] 24 | pub fn alpn_protocol(&self) -> Option> { 25 | self.session.alpn_protocol().map(|s| s.to_vec()) 26 | } 27 | } 28 | 29 | impl Stream { 30 | #[inline] 31 | pub fn alpn_protocol(&self) -> Option> { 32 | self.session.alpn_protocol().map(|s| s.to_vec()) 33 | } 34 | } 35 | 36 | unsafe impl Split for Stream {} 37 | 38 | impl Stream { 39 | pub fn new(io: IO, session: C) -> Self { 40 | Self { 41 | io, 42 | session, 43 | r_buffer: Default::default(), 44 | w_buffer: Default::default(), 45 | } 46 | } 47 | 48 | /// Enable unsafe-io. 49 | /// # Safety 50 | /// Users must make sure the buffer ptr and len is valid until io finished. 51 | /// So the Future cannot be dropped directly. Consider using CancellableIO. 52 | #[cfg(feature = "unsafe_io")] 53 | pub unsafe fn new_unsafe(io: IO, session: C) -> Self { 54 | Self { 55 | io, 56 | session, 57 | r_buffer: ReadBuffer::new_unsafe(), 58 | w_buffer: WriteBuffer::new_unsafe(), 59 | } 60 | } 61 | 62 | pub fn into_parts(self) -> (IO, C) { 63 | (self.io, self.session) 64 | } 65 | 66 | pub(crate) fn map_conn C2>(self, f: F) -> Stream { 67 | Stream { 68 | io: self.io, 69 | session: f(self.session), 70 | r_buffer: self.r_buffer, 71 | w_buffer: self.w_buffer, 72 | } 73 | } 74 | } 75 | 76 | impl Stream 77 | where 78 | C: DerefMut + Deref>, 79 | { 80 | pub(crate) async fn read_io(&mut self, splitted: bool) -> io::Result { 81 | let n = loop { 82 | match self.session.read_tls(&mut self.r_buffer) { 83 | Ok(n) => { 84 | break n; 85 | } 86 | Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { 87 | #[allow(unused_unsafe)] 88 | unsafe { 89 | self.r_buffer.do_io(&mut self.io).await? 90 | }; 91 | continue; 92 | } 93 | Err(err) => return Err(err), 94 | } 95 | }; 96 | 97 | let state = match self.session.process_new_packets() { 98 | Ok(state) => state, 99 | Err(err) => { 100 | // When to write_io? If we do this in read call, the UnsafeWrite may crash 101 | // when we impl split in an UnsafeCell way. 102 | // Here we choose not to do write when read. 103 | // User should manually shutdown it on error. 104 | if !splitted { 105 | let _ = self.write_io().await; 106 | } 107 | return Err(io::Error::new(io::ErrorKind::InvalidData, err)); 108 | } 109 | }; 110 | 111 | if state.peer_has_closed() && self.session.is_handshaking() { 112 | return Err(io::Error::new( 113 | io::ErrorKind::UnexpectedEof, 114 | "tls handshake alert", 115 | )); 116 | } 117 | 118 | Ok(n) 119 | } 120 | 121 | pub(crate) async fn write_io(&mut self) -> io::Result { 122 | let n = loop { 123 | match self.session.write_tls(&mut self.w_buffer) { 124 | Ok(n) => { 125 | if self.w_buffer.is_safe() { 126 | self.w_buffer.do_io(&mut self.io).await?; 127 | } 128 | break n; 129 | } 130 | Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { 131 | // here we don't have to check WouldBlock since we already captured the 132 | // mem block info under unsafe-io. 133 | #[allow(unused_unsafe)] 134 | unsafe { 135 | self.w_buffer.do_io(&mut self.io).await? 136 | }; 137 | continue; 138 | } 139 | Err(err) => return Err(err), 140 | } 141 | }; 142 | 143 | Ok(n) 144 | } 145 | 146 | pub(crate) async fn handshake(&mut self) -> io::Result<(usize, usize)> { 147 | let mut wrlen = 0; 148 | let mut rdlen = 0; 149 | let mut eof = false; 150 | 151 | loop { 152 | while self.session.wants_write() && self.session.is_handshaking() { 153 | wrlen += self.write_io().await?; 154 | } 155 | while !eof && self.session.wants_read() && self.session.is_handshaking() { 156 | let n = self.read_io(false).await?; 157 | rdlen += n; 158 | if n == 0 { 159 | eof = true; 160 | } 161 | } 162 | 163 | match (eof, self.session.is_handshaking()) { 164 | (true, true) => { 165 | let err = io::Error::new(io::ErrorKind::UnexpectedEof, "tls handshake eof"); 166 | return Err(err); 167 | } 168 | (false, true) => (), 169 | (_, false) => { 170 | break; 171 | } 172 | }; 173 | } 174 | 175 | // flush buffer 176 | while self.session.wants_write() { 177 | wrlen += self.write_io().await?; 178 | } 179 | 180 | Ok((rdlen, wrlen)) 181 | } 182 | 183 | pub(crate) async fn read_inner( 184 | &mut self, 185 | mut buf: T, 186 | splitted: bool, 187 | ) -> BufResult { 188 | let slice = unsafe { std::slice::from_raw_parts_mut(buf.write_ptr(), buf.bytes_total()) }; 189 | loop { 190 | // read from rustls to buffer 191 | match self.session.reader().read(slice) { 192 | Ok(n) => { 193 | unsafe { buf.set_init(n) }; 194 | return (Ok(n), buf); 195 | } 196 | // we need more data, read something. 197 | Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (), 198 | Err(e) => { 199 | return (Err(e), buf); 200 | } 201 | } 202 | 203 | // now we need data, read something into rustls 204 | if let Err(e) = self.read_io(splitted).await { 205 | return (Err(e), buf); 206 | } 207 | } 208 | } 209 | } 210 | 211 | impl AsyncReadRent for Stream 212 | where 213 | C: DerefMut + Deref>, 214 | { 215 | async fn read(&mut self, buf: T) -> BufResult { 216 | self.read_inner(buf, false).await 217 | } 218 | 219 | async fn readv(&mut self, mut buf: T) -> BufResult { 220 | let n = match unsafe { RawBuf::new_from_iovec_mut(&mut buf) } { 221 | Some(raw_buf) => self.read(raw_buf).await.0, 222 | None => Ok(0), 223 | }; 224 | if let Ok(n) = n { 225 | unsafe { buf.set_init(n) }; 226 | } 227 | (n, buf) 228 | } 229 | } 230 | 231 | impl AsyncWriteRent for Stream 232 | where 233 | C: DerefMut + Deref>, 234 | { 235 | async fn write(&mut self, buf: T) -> BufResult { 236 | // construct slice 237 | let slice = unsafe { std::slice::from_raw_parts(buf.read_ptr(), buf.bytes_init()) }; 238 | 239 | // flush rustls inner write buffer to make sure there is space for new data 240 | if self.session.wants_write() { 241 | if let Err(e) = self.write_io().await { 242 | return (Err(e), buf); 243 | } 244 | } 245 | 246 | // write slice to rustls 247 | let n = match self.session.writer().write(slice) { 248 | Ok(n) => n, 249 | Err(e) => return (Err(e), buf), 250 | }; 251 | 252 | // write from rustls to connection 253 | while self.session.wants_write() { 254 | match self.write_io().await { 255 | Ok(0) => { 256 | break; 257 | } 258 | Ok(_) => (), 259 | Err(e) => return (Err(e), buf), 260 | } 261 | } 262 | (Ok(n), buf) 263 | } 264 | 265 | // TODO: use real writev 266 | async fn writev(&mut self, buf_vec: T) -> BufResult { 267 | let n = match unsafe { RawBuf::new_from_iovec(&buf_vec) } { 268 | Some(raw_buf) => self.write(raw_buf).await.0, 269 | None => Ok(0), 270 | }; 271 | (n, buf_vec) 272 | } 273 | 274 | async fn flush(&mut self) -> io::Result<()> { 275 | self.session.writer().flush()?; 276 | while self.session.wants_write() { 277 | self.write_io().await?; 278 | } 279 | self.io.flush().await 280 | } 281 | 282 | async fn shutdown(&mut self) -> io::Result<()> { 283 | self.session.send_close_notify(); 284 | 285 | while self.session.wants_write() { 286 | self.write_io().await?; 287 | } 288 | self.io.shutdown().await 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | comment_width = 100 2 | edition = "2021" 3 | format_code_in_doc_comments = true 4 | format_strings = true 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Crate" 7 | normalize_comments = true 8 | normalize_doc_attributes = true 9 | wrap_comments = true 10 | --------------------------------------------------------------------------------