mirror of https://github.com/smithy-lang/smithy-rs
Implement Python runtime crate and shared socket (#1399)
* Add the Python runtime crate `aws-smithy-http-server-python` * Implement SharedSocket support. * Implement logging to tracing support.
This commit is contained in:
parent
2cd82cb862
commit
ebabb98745
10
CODEOWNERS
10
CODEOWNERS
|
@ -1,5 +1,5 @@
|
|||
* @awslabs/rust-sdk-owners
|
||||
/codegen-server/ @awslabs/smithy-rs-server
|
||||
/codegen-server-test/ @awslabs/smithy-rs-server
|
||||
/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server
|
||||
/.github/workflows/server-benchmark.yml @awslabs/smithy-rs-server
|
||||
* @awslabs/rust-sdk-owners
|
||||
/codegen-server/ @awslabs/smithy-rs-server
|
||||
/codegen-server-test/ @awslabs/smithy-rs-server
|
||||
/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server
|
||||
/rust-runtime/aws-smithy-http-server-python/ @awslabs/smithy-rs-server
|
||||
|
|
|
@ -15,4 +15,5 @@ members = [
|
|||
"aws-smithy-types-convert",
|
||||
"aws-smithy-xml",
|
||||
"aws-smithy-http-server",
|
||||
"aws-smithy-http-server-python",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
name = "aws-smithy-http-server-python"
|
||||
version = "0.0.0-smithy-rs-head"
|
||||
authors = ["Smithy Rust Server <smithy-rs-server@amazon.com>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/awslabs/smithy-rs"
|
||||
keywords = ["smithy", "framework", "web", "api", "aws"]
|
||||
categories = ["asynchronous", "web-programming", "api-bindings"]
|
||||
description = """
|
||||
Python server runtime for Smithy Rust Server Framework.
|
||||
"""
|
||||
# until this is not stable, it is not publishable.
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.16" }
|
||||
pyo3-asyncio = { version = "0.16", features = ["attributes", "tokio-runtime"] }
|
||||
socket2 = { version = "0.4", features = ["all"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1.34"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
# End of docs.rs metadata
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,7 @@
|
|||
# aws-smithy-http-server-python
|
||||
|
||||
Server libraries for smithy-rs generated servers, targeting pure Python business logic.
|
||||
|
||||
<!-- anchor_start:footer -->
|
||||
This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly.
|
||||
<!-- anchor_end:footer -->
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Rust/Python bindings, runtime and utilities.
|
||||
//!
|
||||
//! This crates implements all the generic code needed to start and manage
|
||||
//! a Smithy Rust HTTP server where the business logic is implemented in Python,
|
||||
//! leveraging [PyO3].
|
||||
//!
|
||||
//! [PyO3]: https://pyo3.rs/
|
||||
|
||||
mod logging;
|
||||
mod socket;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use logging::{setup, LogLevel};
|
||||
#[doc(inline)]
|
||||
pub use socket::SharedSocket;
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Rust `tracing` and Python `logging` setup and utilities.
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
use tracing_subscriber::{prelude::*, EnvFilter};
|
||||
|
||||
/// Setup `tracing::subscriber` reading the log level from RUST_LOG environment variable
|
||||
/// and inject the custom Python `logger` into the interpreter.
|
||||
pub fn setup(py: Python, level: LogLevel) -> PyResult<()> {
|
||||
let format = tracing_subscriber::fmt::layer()
|
||||
.with_ansi(true)
|
||||
.with_level(true);
|
||||
match EnvFilter::try_from_default_env() {
|
||||
Ok(filter) => {
|
||||
let level: LogLevel = filter.to_string().into();
|
||||
tracing_subscriber::registry()
|
||||
.with(format)
|
||||
.with(filter)
|
||||
.init();
|
||||
setup_python_logging(py, level)?;
|
||||
}
|
||||
Err(_) => {
|
||||
tracing_subscriber::registry()
|
||||
.with(format)
|
||||
.with(LevelFilter::from_level(level.into()))
|
||||
.init();
|
||||
setup_python_logging(py, level)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This custom logger enum exported to Python can be used to configure the
|
||||
/// both the Rust `tracing` and Python `logging` levels.
|
||||
/// We cannot export directly `tracing::Level` to Python.
|
||||
#[pyclass]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
/// `From<LogLevel>` is used to convert `LogLevel` to the correct string
|
||||
/// needed by Python `logging` module.
|
||||
impl From<LogLevel> for String {
|
||||
fn from(other: LogLevel) -> String {
|
||||
match other {
|
||||
LogLevel::Error => "ERROR".into(),
|
||||
LogLevel::Warn => "WARN".into(),
|
||||
LogLevel::Info => "INFO".into(),
|
||||
_ => "DEBUG".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `From<String>` is used to covert `tracing::EnvFilter` into `LogLevel`.
|
||||
impl From<String> for LogLevel {
|
||||
fn from(other: String) -> LogLevel {
|
||||
match other.as_str() {
|
||||
"error" => LogLevel::Error,
|
||||
"warn" => LogLevel::Warn,
|
||||
"info" => LogLevel::Info,
|
||||
"debug" => LogLevel::Debug,
|
||||
_ => LogLevel::Trace,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `From<LogLevel>` is used to covert `LogLevel` into `tracing::EnvFilter`.
|
||||
impl From<LogLevel> for Level {
|
||||
fn from(other: LogLevel) -> Level {
|
||||
match other {
|
||||
LogLevel::Debug => Level::DEBUG,
|
||||
LogLevel::Info => Level::INFO,
|
||||
LogLevel::Warn => Level::WARN,
|
||||
LogLevel::Error => Level::ERROR,
|
||||
_ => Level::TRACE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifies the Python `logging` module to deliver its log messages using `tracing::Subscriber` events.
|
||||
///
|
||||
/// To achieve this goal, the following changes are made to the module:
|
||||
/// - A new builtin function `logging.python_tracing` transcodes `logging.LogRecord`s to `tracing::Event`s. This function
|
||||
/// is not exported in `logging.__all__`, as it is not intended to be called directly.
|
||||
/// - A new class `logging.RustTracing` provides a `logging.Handler` that delivers all records to `python_tracing`.
|
||||
/// - `logging.basicConfig` is changed to use `logging.HostHandler` by default.
|
||||
///
|
||||
/// Since any call like `logging.warn(...)` sets up logging via `logging.basicConfig`, all log messages are now
|
||||
/// delivered to `crate::logging`, which will send them to `tracing::event!`.
|
||||
fn setup_python_logging(py: Python, level: LogLevel) -> PyResult<()> {
|
||||
let logging = py.import("logging")?;
|
||||
logging.setattr("python_tracing", wrap_pyfunction!(python_tracing, logging)?)?;
|
||||
|
||||
let level: String = level.into();
|
||||
let pycode = format!(
|
||||
r#"
|
||||
class RustTracing(Handler):
|
||||
""" Python logging to Rust tracing handler. """
|
||||
def __init__(self, level=0):
|
||||
super().__init__(level=level)
|
||||
|
||||
def emit(self, record):
|
||||
python_tracing(record)
|
||||
|
||||
# Store the old basicConfig in the local namespace.
|
||||
oldBasicConfig = basicConfig
|
||||
|
||||
def basicConfig(*pargs, **kwargs):
|
||||
""" Reimplement basicConfig to hijack the root logger. """
|
||||
if "handlers" not in kwargs:
|
||||
kwargs["handlers"] = [RustTracing()]
|
||||
kwargs["level"] = {level}
|
||||
return oldBasicConfig(*pargs, **kwargs)
|
||||
"#,
|
||||
);
|
||||
|
||||
py.run(&pycode, Some(logging.dict()), None)?;
|
||||
let all = logging.index()?;
|
||||
all.append("RustTracing")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consumes a Python `logging.LogRecord` and emits a Rust `tracing::Event` instead.
|
||||
#[cfg(not(test))]
|
||||
#[pyfunction]
|
||||
fn python_tracing(record: &PyAny) -> PyResult<()> {
|
||||
let level = record.getattr("levelno")?;
|
||||
let message = record.getattr("getMessage")?.call0()?;
|
||||
let module = record.getattr("module")?;
|
||||
let filename = record.getattr("filename")?;
|
||||
let line = record.getattr("lineno")?;
|
||||
|
||||
match level.extract()? {
|
||||
40u8 => tracing::event!(Level::ERROR, %module, %filename, %line, "{message}"),
|
||||
30u8 => tracing::event!(Level::WARN, %module, %filename, %line, "{message}"),
|
||||
20u8 => tracing::event!(Level::INFO, %module, %filename, %line, "{message}"),
|
||||
10u8 => tracing::event!(Level::DEBUG, %module, %filename, %line, "{message}"),
|
||||
_ => tracing::event!(Level::TRACE, %module, %filename, %line, "{message}"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[pyfunction]
|
||||
fn python_tracing(record: &PyAny) -> PyResult<()> {
|
||||
let message = record.getattr("getMessage")?.call0()?;
|
||||
pretty_assertions::assert_eq!(message.to_string(), "a message");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::Once;
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
fn initialize() {
|
||||
INIT.call_once(|| {
|
||||
pyo3::prepare_freethreaded_python();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tracing_handler_is_injected_in_python() {
|
||||
initialize();
|
||||
Python::with_gil(|py| {
|
||||
setup_python_logging(py, LogLevel::Info).unwrap();
|
||||
let logging = py.import("logging").unwrap();
|
||||
logging.call_method1("info", ("a message",)).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
//! Socket implementation that can be shared between multiple Python processes.
|
||||
|
||||
use pyo3::prelude::*;
|
||||
|
||||
use socket2::{Domain, Protocol, Socket, Type};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
/// Socket implementation that can be shared between multiple Python processes.
|
||||
///
|
||||
/// Python cannot handle true multi-threaded applications due to the [GIL],
|
||||
/// often resulting in reduced performance and only one core used by the application.
|
||||
/// To work around this, Python web applications usually create a socket with
|
||||
/// SO_REUSEADDR and SO_REUSEPORT enabled that can be shared between multiple
|
||||
/// Python processes, allowing you to maximize performance and use all available
|
||||
/// computing capacity of the host.
|
||||
///
|
||||
/// [GIL]: https://wiki.python.org/moin/GlobalInterpreterLock
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct SharedSocket {
|
||||
pub(crate) inner: Socket,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl SharedSocket {
|
||||
/// Create a new UNIX `SharedSocket` from an address, port and backlog.
|
||||
/// If not specified, the backlog defaults to 1024 connections.
|
||||
#[new]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn new(address: String, port: i32, backlog: Option<i32>) -> PyResult<Self> {
|
||||
let address: SocketAddr = format!("{}:{}", address, port).parse()?;
|
||||
let domain = if address.is_ipv6() {
|
||||
Domain::IPV6
|
||||
} else {
|
||||
Domain::IPV4
|
||||
};
|
||||
tracing::info!("Shared socket listening on {address}, IP version: {domain:?}");
|
||||
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
|
||||
// Set value for the `SO_REUSEPORT` and `SO_REUSEADDR` options on this socket.
|
||||
// This indicates that further calls to `bind` may allow reuse of local
|
||||
// addresses. For IPv4 sockets this means that a socket may bind even when
|
||||
// there's a socket already listening on this port.
|
||||
socket.set_reuse_port(true)?;
|
||||
socket.set_reuse_address(true)?;
|
||||
socket.bind(&address.into())?;
|
||||
socket.listen(backlog.unwrap_or(1024))?;
|
||||
Ok(SharedSocket { inner: socket })
|
||||
}
|
||||
|
||||
/// Create a new Windows `SharedSocket` from an address, port and backlog.
|
||||
/// If not specified, the backlog defaults to 1024 connections.
|
||||
#[new]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn new(address: String, port: i32, backlog: Option<i32>) -> PyResult<Self> {
|
||||
let address: SocketAddr = format!("{}:{}", address, port).parse()?;
|
||||
let domain = if address.is_ipv6() {
|
||||
Domain::IPV6
|
||||
} else {
|
||||
Domain::IPV4
|
||||
};
|
||||
tracing::info!("Shared socket listening on {address}, IP version: {domain:?}");
|
||||
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
|
||||
// `SO_REUSEPORT` is not available on Windows.
|
||||
socket.set_reuse_address(true)?;
|
||||
socket.bind(&address.into())?;
|
||||
socket.listen(backlog.unwrap_or(1024))?;
|
||||
Ok(SharedSocket { inner: socket })
|
||||
}
|
||||
|
||||
/// Clone the inner socket allowing it to be shared between multiple
|
||||
/// Python processes.
|
||||
pub fn try_clone(&self) -> PyResult<SharedSocket> {
|
||||
let copied = self.inner.try_clone()?;
|
||||
Ok(SharedSocket { inner: copied })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn socket_can_bind_on_random_port() {
|
||||
let _socket = SharedSocket::new("127.0.0.1".to_owned(), 0, None).unwrap();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
assert!(_socket.inner.is_listener().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn socket_can_be_cloned() {
|
||||
let socket = SharedSocket::new("127.0.0.1".to_owned(), 0, None).unwrap();
|
||||
let _cloned_socket = socket.try_clone().unwrap();
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
assert!(_cloned_socket.inner.is_listener().is_ok());
|
||||
}
|
||||
}
|
|
@ -9,8 +9,6 @@ keywords = ["smithy", "framework", "web", "api", "aws"]
|
|||
categories = ["asynchronous", "web-programming", "api-bindings"]
|
||||
description = """
|
||||
Server runtime for Smithy Rust Server Framework.
|
||||
|
||||
NOTE: THIS IS HIGHLY EXPERIMENTAL AND SHOULD NOT BE USED YET.
|
||||
"""
|
||||
# until this is not stable, it is not publishable.
|
||||
publish = false
|
||||
|
@ -30,13 +28,13 @@ mime = "0.3"
|
|||
nom = "7"
|
||||
paste = "1"
|
||||
pin-project-lite = "0.2"
|
||||
regex = "1.0"
|
||||
regex = "1.5.5"
|
||||
serde_urlencoded = "0.7"
|
||||
strum_macros = "0.24"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
tower = { version = "0.4.11", features = ["util", "make"], default-features = false }
|
||||
tower-http = { version = "0.2.1", features = ["add-extension", "map-response-body"] }
|
||||
tower-http = { version = "0.3", features = ["add-extension", "map-response-body"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
|
|
|
@ -123,6 +123,7 @@ RUN set -eux; \
|
|||
openssl-devel \
|
||||
pkgconfig \
|
||||
python3 \
|
||||
python3-devel \
|
||||
shadow-utils; \
|
||||
yum clean all; \
|
||||
rm -rf /var/cache/yum; \
|
||||
|
|
|
@ -53,7 +53,7 @@ struct Metadata {
|
|||
|
||||
const RUST_SDK_TEAM: &str = "AWS Rust SDK Team <aws-sdk-rust@amazon.com>";
|
||||
const SERVER_TEAM: &str = "Smithy Rust Server <smithy-rs-server@amazon.com>";
|
||||
const SERVER_CRATES: &[&str] = &["aws-smithy-http-server"];
|
||||
const SERVER_CRATES: &[&str] = &["aws-smithy-http-server", "aws-smithy-http-server-python"];
|
||||
|
||||
/// Check crate licensing
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue