itsi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +1541 -0
  3. data/Cargo.toml +13 -0
  4. data/README.md +82 -0
  5. data/Rakefile +87 -0
  6. data/crates/itsi_error/Cargo.lock +368 -0
  7. data/crates/itsi_error/Cargo.toml +9 -0
  8. data/crates/itsi_error/src/lib.rs +49 -0
  9. data/crates/itsi_rb_helpers/Cargo.lock +355 -0
  10. data/crates/itsi_rb_helpers/Cargo.toml +8 -0
  11. data/crates/itsi_rb_helpers/src/lib.rs +98 -0
  12. data/crates/itsi_scheduler/Cargo.toml +16 -0
  13. data/crates/itsi_scheduler/extconf.rb +6 -0
  14. data/crates/itsi_scheduler/src/lib.rs +17 -0
  15. data/crates/itsi_server/Cargo.toml +29 -0
  16. data/crates/itsi_server/extconf.rb +6 -0
  17. data/crates/itsi_server/src/lib.rs +52 -0
  18. data/crates/itsi_server/src/request/itsi_request.rs +143 -0
  19. data/crates/itsi_server/src/request/mod.rs +1 -0
  20. data/crates/itsi_server/src/server/bind.rs +138 -0
  21. data/crates/itsi_server/src/server/itsi_ca/itsi_ca.crt +32 -0
  22. data/crates/itsi_server/src/server/itsi_ca/itsi_ca.key +52 -0
  23. data/crates/itsi_server/src/server/itsi_server.rs +182 -0
  24. data/crates/itsi_server/src/server/listener.rs +218 -0
  25. data/crates/itsi_server/src/server/mod.rs +5 -0
  26. data/crates/itsi_server/src/server/tls.rs +138 -0
  27. data/crates/itsi_server/src/server/transfer_protocol.rs +23 -0
  28. data/crates/itsi_server/src/stream_writer/mod.rs +21 -0
  29. data/crates/itsi_tracing/Cargo.lock +274 -0
  30. data/crates/itsi_tracing/Cargo.toml +12 -0
  31. data/crates/itsi_tracing/src/lib.rs +11 -0
  32. data/gems/scheduler/.gitignore +14 -0
  33. data/gems/scheduler/.rubocop.yml +8 -0
  34. data/gems/scheduler/CHANGELOG.md +5 -0
  35. data/gems/scheduler/CODE_OF_CONDUCT.md +132 -0
  36. data/gems/scheduler/Cargo.lock +745 -0
  37. data/gems/scheduler/Cargo.toml +3 -0
  38. data/gems/scheduler/LICENSE.txt +21 -0
  39. data/gems/scheduler/README.md +43 -0
  40. data/gems/scheduler/Rakefile +22 -0
  41. data/gems/scheduler/bin/console +11 -0
  42. data/gems/scheduler/bin/setup +8 -0
  43. data/gems/scheduler/ext/itsi_error/Cargo.lock +368 -0
  44. data/gems/scheduler/ext/itsi_error/Cargo.toml +9 -0
  45. data/gems/scheduler/ext/itsi_error/src/lib.rs +49 -0
  46. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.lock +355 -0
  47. data/gems/scheduler/ext/itsi_rb_helpers/Cargo.toml +8 -0
  48. data/gems/scheduler/ext/itsi_rb_helpers/src/lib.rs +98 -0
  49. data/gems/scheduler/ext/itsi_scheduler/Cargo.toml +16 -0
  50. data/gems/scheduler/ext/itsi_scheduler/extconf.rb +6 -0
  51. data/gems/scheduler/ext/itsi_scheduler/src/lib.rs +17 -0
  52. data/gems/scheduler/ext/itsi_tracing/Cargo.lock +274 -0
  53. data/gems/scheduler/ext/itsi_tracing/Cargo.toml +12 -0
  54. data/gems/scheduler/ext/itsi_tracing/src/lib.rs +11 -0
  55. data/gems/scheduler/itsi-scheduler.gemspec +43 -0
  56. data/gems/scheduler/lib/itsi/scheduler/version.rb +7 -0
  57. data/gems/scheduler/lib/itsi/scheduler.rb +11 -0
  58. data/gems/scheduler/sig/itsi_scheduler.rbs +4 -0
  59. data/gems/scheduler/test/test_helper.rb +6 -0
  60. data/gems/scheduler/test/test_itsi_scheduler.rb +9 -0
  61. data/gems/server/.gitignore +14 -0
  62. data/gems/server/.rubocop.yml +8 -0
  63. data/gems/server/CHANGELOG.md +5 -0
  64. data/gems/server/CODE_OF_CONDUCT.md +132 -0
  65. data/gems/server/LICENSE.txt +21 -0
  66. data/gems/server/README.md +43 -0
  67. data/gems/server/Rakefile +22 -0
  68. data/gems/server/bin/console +11 -0
  69. data/gems/server/bin/setup +8 -0
  70. data/gems/server/exe/itsi +84 -0
  71. data/gems/server/ext/itsi_error/Cargo.lock +368 -0
  72. data/gems/server/ext/itsi_error/Cargo.toml +9 -0
  73. data/gems/server/ext/itsi_error/src/lib.rs +49 -0
  74. data/gems/server/ext/itsi_rb_helpers/Cargo.lock +355 -0
  75. data/gems/server/ext/itsi_rb_helpers/Cargo.toml +8 -0
  76. data/gems/server/ext/itsi_rb_helpers/src/lib.rs +98 -0
  77. data/gems/server/ext/itsi_server/Cargo.toml +29 -0
  78. data/gems/server/ext/itsi_server/extconf.rb +6 -0
  79. data/gems/server/ext/itsi_server/src/lib.rs +52 -0
  80. data/gems/server/ext/itsi_server/src/request/itsi_request.rs +143 -0
  81. data/gems/server/ext/itsi_server/src/request/mod.rs +1 -0
  82. data/gems/server/ext/itsi_server/src/server/bind.rs +138 -0
  83. data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +32 -0
  84. data/gems/server/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +52 -0
  85. data/gems/server/ext/itsi_server/src/server/itsi_server.rs +182 -0
  86. data/gems/server/ext/itsi_server/src/server/listener.rs +218 -0
  87. data/gems/server/ext/itsi_server/src/server/mod.rs +5 -0
  88. data/gems/server/ext/itsi_server/src/server/tls.rs +138 -0
  89. data/gems/server/ext/itsi_server/src/server/transfer_protocol.rs +23 -0
  90. data/gems/server/ext/itsi_server/src/stream_writer/mod.rs +21 -0
  91. data/gems/server/ext/itsi_tracing/Cargo.lock +274 -0
  92. data/gems/server/ext/itsi_tracing/Cargo.toml +12 -0
  93. data/gems/server/ext/itsi_tracing/src/lib.rs +11 -0
  94. data/gems/server/itsi-server.gemspec +45 -0
  95. data/gems/server/lib/itsi/request.rb +39 -0
  96. data/gems/server/lib/itsi/server/version.rb +7 -0
  97. data/gems/server/lib/itsi/server.rb +21 -0
  98. data/gems/server/sig/itsi_server.rbs +4 -0
  99. data/gems/server/test/test_helper.rb +5 -0
  100. data/gems/server/test/test_itsi_server.rb +9 -0
  101. data/lib/itsi/version.rb +3 -0
  102. data/lib/itsi.rb +6 -0
  103. metadata +172 -0
@@ -0,0 +1,218 @@
1
+ use super::bind::{Bind, BindAddress};
2
+ use super::transfer_protocol::TransferProtocol;
3
+ use hyper_util::rt::TokioIo;
4
+ use itsi_error::Result;
5
+ use itsi_tracing::info;
6
+ use socket2::{Domain, Protocol, Socket, Type};
7
+ use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
8
+ use std::pin::Pin;
9
+ use std::sync::Arc;
10
+ use std::{os::unix::net::UnixListener as StdUnixListener, path::PathBuf};
11
+ use tokio::net::{unix, TcpListener, TcpStream, UnixListener, UnixStream};
12
+ use tokio_rustls::TlsAcceptor;
13
+
14
+ pub(crate) trait IoStream:
15
+ tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin
16
+ {
17
+ }
18
+ impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + Unpin> IoStream for T {}
19
+
20
+ pub(crate) enum Listener {
21
+ Tcp(TcpListener),
22
+ TcpTls((TcpListener, TlsAcceptor)),
23
+ Unix(UnixListener),
24
+ UnixTls((UnixListener, TlsAcceptor)),
25
+ }
26
+
27
+ enum Stream {
28
+ TcpStream((TcpStream, SocketAddr)),
29
+ UnixStream((UnixStream, unix::SocketAddr)),
30
+ }
31
+
32
+ #[derive(Clone, Debug)]
33
+ pub enum SockAddr {
34
+ Tcp(Arc<SocketAddr>),
35
+ Unix(Arc<unix::SocketAddr>),
36
+ }
37
+ impl std::fmt::Display for SockAddr {
38
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39
+ match self {
40
+ SockAddr::Tcp(socket_addr) => write!(f, "{}", socket_addr.ip().to_canonical()),
41
+ SockAddr::Unix(socket_addr) => match socket_addr.as_pathname() {
42
+ Some(path) => write!(f, "{:?}", path),
43
+ None => write!(f, ""),
44
+ },
45
+ }
46
+ }
47
+ }
48
+
49
+ impl Listener {
50
+ pub(crate) async fn accept(&self) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
51
+ match self {
52
+ Listener::Tcp(listener) => Listener::accept_tcp(listener).await,
53
+ Listener::TcpTls((listener, acceptor)) => {
54
+ Listener::accept_tls(listener, acceptor).await
55
+ }
56
+ Listener::Unix(stream) => Listener::accept_unix(stream).await,
57
+ Listener::UnixTls((listener, acceptor)) => {
58
+ Listener::accept_unix_tls(listener, acceptor).await
59
+ }
60
+ }
61
+ }
62
+
63
+ async fn to_tokio_io(
64
+ input_stream: Stream,
65
+ tls_acceptor: Option<&TlsAcceptor>,
66
+ ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
67
+ match tls_acceptor {
68
+ Some(acceptor) => match input_stream {
69
+ Stream::TcpStream((tcp_stream, socket_address)) => {
70
+ match acceptor.accept(tcp_stream).await {
71
+ Ok(tls_stream) => Ok((
72
+ TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
73
+ SockAddr::Tcp(Arc::new(socket_address)),
74
+ )),
75
+ Err(err) => Err(err.into()),
76
+ }
77
+ }
78
+ Stream::UnixStream((unix_stream, socket_address)) => {
79
+ match acceptor.accept(unix_stream).await {
80
+ Ok(tls_stream) => Ok((
81
+ TokioIo::new(Box::pin(tls_stream) as Pin<Box<dyn IoStream>>),
82
+ SockAddr::Unix(Arc::new(socket_address)),
83
+ )),
84
+ Err(err) => Err(err.into()),
85
+ }
86
+ }
87
+ },
88
+ None => match input_stream {
89
+ Stream::TcpStream((tcp_stream, socket_address)) => Ok((
90
+ TokioIo::new(Box::pin(tcp_stream) as Pin<Box<dyn IoStream>>),
91
+ SockAddr::Tcp(Arc::new(socket_address)),
92
+ )),
93
+ Stream::UnixStream((unix_stream, socket_address)) => Ok((
94
+ TokioIo::new(Box::pin(unix_stream) as Pin<Box<dyn IoStream>>),
95
+ SockAddr::Unix(Arc::new(socket_address)),
96
+ )),
97
+ },
98
+ }
99
+ }
100
+
101
+ async fn accept_tcp(
102
+ listener: &TcpListener,
103
+ ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
104
+ let tcp_stream = listener.accept().await?;
105
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), None).await
106
+ }
107
+
108
+ async fn accept_tls(
109
+ listener: &TcpListener,
110
+ acceptor: &TlsAcceptor,
111
+ ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
112
+ let tcp_stream = listener.accept().await?;
113
+ Self::to_tokio_io(Stream::TcpStream(tcp_stream), Some(acceptor)).await
114
+ }
115
+
116
+ async fn accept_unix(
117
+ listener: &UnixListener,
118
+ ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
119
+ let unix_stream = listener.accept().await?;
120
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), None).await
121
+ }
122
+
123
+ async fn accept_unix_tls(
124
+ listener: &UnixListener,
125
+ acceptor: &TlsAcceptor,
126
+ ) -> Result<(TokioIo<Pin<Box<dyn IoStream>>>, SockAddr)> {
127
+ let unix_stream = listener.accept().await?;
128
+ Self::to_tokio_io(Stream::UnixStream(unix_stream), Some(acceptor)).await
129
+ }
130
+
131
+ pub(crate) fn scheme(&self) -> String {
132
+ match self {
133
+ Listener::Tcp(_) => "http".to_string(),
134
+ Listener::TcpTls(_) => "https".to_string(),
135
+ Listener::Unix(_) => "http".to_string(),
136
+ Listener::UnixTls(_) => "https".to_string(),
137
+ }
138
+ }
139
+
140
+ pub(crate) fn port(&self) -> u16 {
141
+ match self {
142
+ Listener::Tcp(listener) => listener.local_addr().unwrap().port(),
143
+ Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().port(),
144
+ Listener::Unix(_) => 0,
145
+ Listener::UnixTls(_) => 0,
146
+ }
147
+ }
148
+
149
+ pub(crate) fn host(&self) -> String {
150
+ match self {
151
+ Listener::Tcp(listener) => listener.local_addr().unwrap().ip().to_string(),
152
+ Listener::TcpTls((listener, _)) => listener.local_addr().unwrap().ip().to_string(),
153
+ Listener::Unix(_) => "unix".to_string(),
154
+ Listener::UnixTls(_) => "unix".to_string(),
155
+ }
156
+ }
157
+ }
158
+
159
+ impl From<Bind> for Listener {
160
+ fn from(bind: Bind) -> Self {
161
+ match bind.address {
162
+ BindAddress::Ip(addr) => match bind.protocol {
163
+ TransferProtocol::Http => Listener::Tcp(
164
+ TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap())).unwrap(),
165
+ ),
166
+ TransferProtocol::Https => {
167
+ let tcp_listener =
168
+ TcpListener::from_std(connect_tcp_socket(addr, bind.port.unwrap()))
169
+ .unwrap();
170
+ let tls_acceptor = TlsAcceptor::from(Arc::new(bind.tls_config.unwrap()));
171
+ Listener::TcpTls((tcp_listener, tls_acceptor))
172
+ }
173
+ _ => unreachable!(),
174
+ },
175
+ BindAddress::UnixSocket(path) => match bind.tls_config {
176
+ Some(tls_config) => {
177
+ let tls_acceptor = TlsAcceptor::from(Arc::new(tls_config));
178
+ Listener::UnixTls((
179
+ UnixListener::from_std(connect_unix_socket(&path)).unwrap(),
180
+ tls_acceptor,
181
+ ))
182
+ }
183
+ None => Listener::Unix(UnixListener::from_std(connect_unix_socket(&path)).unwrap()),
184
+ },
185
+ }
186
+ }
187
+ }
188
+
189
+ fn connect_tcp_socket(addr: IpAddr, port: u16) -> StdTcpListener {
190
+ let domain = match addr {
191
+ IpAddr::V4(_) => Domain::IPV4,
192
+ IpAddr::V6(_) => Domain::IPV6,
193
+ };
194
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP)).unwrap();
195
+ let socket_address: SocketAddr = SocketAddr::new(addr, port);
196
+ socket.set_reuse_address(true).ok();
197
+ socket.set_reuse_port(true).ok();
198
+ socket.set_nonblocking(true).ok();
199
+ socket.set_nodelay(true).ok();
200
+ socket.set_recv_buffer_size(1_048_576).ok();
201
+ info!("Binding to {}", socket_address);
202
+ socket.bind(&socket_address.into()).unwrap();
203
+ socket.listen(1024).unwrap();
204
+ socket.into()
205
+ }
206
+
207
+ fn connect_unix_socket(path: &PathBuf) -> StdUnixListener {
208
+ let _ = std::fs::remove_file(path);
209
+ let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap();
210
+ socket.set_nonblocking(true).ok();
211
+ let socket_address = socket2::SockAddr::unix(path).unwrap();
212
+
213
+ info!("Binding to {:?}", path);
214
+ socket.bind(&socket_address).unwrap();
215
+ socket.listen(1024).unwrap();
216
+
217
+ socket.into()
218
+ }
@@ -0,0 +1,5 @@
1
+ pub mod bind;
2
+ pub mod itsi_server;
3
+ pub mod listener;
4
+ pub mod tls;
5
+ pub mod transfer_protocol;
@@ -0,0 +1,138 @@
1
+ use base64::{engine::general_purpose, Engine as _};
2
+ use itsi_error::Result;
3
+ use itsi_tracing::{info, warn};
4
+ use rcgen::{CertificateParams, DnType, KeyPair, SanType};
5
+ use rustls_pemfile::{certs, pkcs8_private_keys};
6
+ use std::{collections::HashMap, fs, io::BufReader};
7
+ use tokio_rustls::rustls::{Certificate, PrivateKey, ServerConfig};
8
+
9
+ const ITS_CA_CERT: &str = include_str!("./itsi_ca/itsi_ca.crt");
10
+ const ITS_CA_KEY: &str = include_str!("./itsi_ca/itsi_ca.key");
11
+
12
+ // Generates a TLS configuration based on either :
13
+ // * Input "cert" and "key" options (either paths or Base64-encoded strings) or
14
+ // * Performs automatic certificate generation/retrieval. Generated certs use an internal self-signed Isti CA.
15
+ // If a non-local host or optional domain parameter is provided,
16
+ // an automated certificate will attempt to be fetched using let's encrypt.
17
+ pub fn configure_tls(host: &str, query_params: &HashMap<String, String>) -> Result<ServerConfig> {
18
+ let (certs, key) = if let (Some(cert_path), Some(key_path)) =
19
+ (query_params.get("cert"), query_params.get("key"))
20
+ {
21
+ // Load from file or Base64
22
+ let certs = load_certs(cert_path);
23
+ let key = load_private_key(key_path);
24
+ (certs, key)
25
+ } else if query_params
26
+ .get("cert")
27
+ .map(|v| v == "auto")
28
+ .unwrap_or(false)
29
+ {
30
+ let domain_param = query_params.get("domain");
31
+ let host_string = host.to_string();
32
+ let domain = domain_param.or_else(|| {
33
+ if host_string != "localhost" {
34
+ Some(&host_string)
35
+ } else {
36
+ None
37
+ }
38
+ });
39
+
40
+ if let Some(domain) = domain {
41
+ retrieve_acme_cert(domain)?
42
+ } else {
43
+ generate_ca_signed_cert(host)?
44
+ }
45
+ } else {
46
+ generate_ca_signed_cert(host)?
47
+ };
48
+
49
+ let mut config = ServerConfig::builder()
50
+ .with_safe_defaults()
51
+ .with_no_client_auth()
52
+ .with_single_cert(certs, key)
53
+ .expect("Failed to build TLS config");
54
+
55
+ config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
56
+ Ok(config)
57
+ }
58
+
59
+ pub fn load_certs(path: &str) -> Vec<Certificate> {
60
+ let data = if let Some(stripped) = path.strip_prefix("base64:") {
61
+ general_purpose::STANDARD
62
+ .decode(stripped)
63
+ .expect("Invalid base64 certificate")
64
+ } else {
65
+ fs::read(path).expect("Failed to read certificate file")
66
+ };
67
+
68
+ if data.starts_with(b"-----BEGIN ") {
69
+ let mut reader = BufReader::new(&data[..]);
70
+ let certs_der: Vec<Vec<u8>> = certs(&mut reader)
71
+ .map(|r| {
72
+ r.map(|der| der.as_ref().to_vec())
73
+ .map_err(itsi_error::ItsiError::from)
74
+ })
75
+ .collect::<Result<_>>()
76
+ .expect("Failed to parse certificate file");
77
+ certs_der.into_iter().map(Certificate).collect()
78
+ } else {
79
+ vec![Certificate(data)]
80
+ }
81
+ }
82
+
83
+ /// Loads a private key from a file or Base64.
84
+ pub fn load_private_key(path: &str) -> PrivateKey {
85
+ let key_data = if let Some(stripped) = path.strip_prefix("base64:") {
86
+ general_purpose::STANDARD
87
+ .decode(stripped)
88
+ .expect("Invalid base64 private key")
89
+ } else {
90
+ fs::read(path).expect("Failed to read private key file")
91
+ };
92
+
93
+ if key_data.starts_with(b"-----BEGIN ") {
94
+ let mut reader = BufReader::new(&key_data[..]);
95
+ let keys: Vec<Vec<u8>> = pkcs8_private_keys(&mut reader)
96
+ .map(|r| {
97
+ r.map(|key| key.secret_pkcs8_der().to_vec())
98
+ .map_err(itsi_error::ItsiError::from)
99
+ })
100
+ .collect::<Result<_>>()
101
+ .expect("Failed to parse private key");
102
+ if !keys.is_empty() {
103
+ return PrivateKey(keys[0].clone());
104
+ }
105
+ }
106
+ PrivateKey(key_data)
107
+ }
108
+
109
+ pub fn generate_ca_signed_cert(domain: &str) -> Result<(Vec<Certificate>, PrivateKey)> {
110
+ info!("Generating New Itsi CA - Self signed Certificate. Use `itsi ca export` to export the CA certificate for import into your local trust store.");
111
+
112
+ let ca_kp = KeyPair::from_pem(ITS_CA_KEY).unwrap();
113
+ let params = CertificateParams::from_ca_cert_pem(ITS_CA_CERT).unwrap();
114
+
115
+ let ca_cert = params.self_signed(&ca_kp).unwrap();
116
+ let ee_key = KeyPair::generate().unwrap();
117
+ let mut ee_params = CertificateParams::default();
118
+
119
+ // Set the domain in the subject alternative names (SAN)
120
+ ee_params.subject_alt_names = vec![SanType::DnsName(domain.try_into()?)];
121
+ // Optionally, set the Common Name (CN) in the distinguished name:
122
+ ee_params
123
+ .distinguished_name
124
+ .push(DnType::CommonName, domain);
125
+
126
+ ee_params.use_authority_key_identifier_extension = true;
127
+
128
+ let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ee_key).unwrap();
129
+ let ee_cert_der = ee_cert.der().to_vec();
130
+ let ee_cert = Certificate(ee_cert_der);
131
+ Ok((vec![ee_cert], PrivateKey(ee_key.serialize_der())))
132
+ }
133
+
134
+ /// Retrieves an ACME certificate for a given domain.
135
+ pub fn retrieve_acme_cert(domain: &str) -> Result<(Vec<Certificate>, PrivateKey)> {
136
+ warn!("Retrieving ACME cert for {}", domain);
137
+ generate_ca_signed_cert(domain)
138
+ }
@@ -0,0 +1,23 @@
1
+ use itsi_error::ItsiError;
2
+ use std::str::FromStr;
3
+
4
+ #[derive(Debug, Default, Clone)]
5
+ pub enum TransferProtocol {
6
+ #[default]
7
+ Https,
8
+ Http,
9
+ Unix,
10
+ }
11
+
12
+ impl FromStr for TransferProtocol {
13
+ type Err = ItsiError;
14
+
15
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
16
+ match s {
17
+ "http" => Ok(TransferProtocol::Http),
18
+ "https" => Ok(TransferProtocol::Https),
19
+ "unix" => Ok(TransferProtocol::Unix),
20
+ _ => Err(ItsiError::UnsupportedProtocol(s.to_string())),
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,21 @@
1
+ use bytes::Bytes;
2
+ use magnus::{error::Result as MagnusResult, exception::io_error, Error};
3
+ use tokio::sync::mpsc::Sender;
4
+
5
+ #[magnus::wrap(class = "Itsi::StreamWriter", free_immediately, size)]
6
+ pub struct StreamWriter {
7
+ sender: Sender<Bytes>,
8
+ }
9
+
10
+ impl StreamWriter {
11
+ pub fn new(sender: Sender<Bytes>) -> Self {
12
+ StreamWriter { sender }
13
+ }
14
+
15
+ pub fn write(&self, bytes: Bytes) -> MagnusResult<()> {
16
+ self.sender
17
+ .blocking_send(bytes)
18
+ .map_err(|e| Error::new(io_error(), format!("{:?}", e)))?;
19
+ Ok(())
20
+ }
21
+ }
@@ -0,0 +1,274 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.1.3"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "cfg-if"
16
+ version = "1.0.0"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
19
+
20
+ [[package]]
21
+ name = "itsi_tracing"
22
+ version = "0.1.0"
23
+ dependencies = [
24
+ "tracing",
25
+ "tracing-subscriber",
26
+ ]
27
+
28
+ [[package]]
29
+ name = "lazy_static"
30
+ version = "1.5.0"
31
+ source = "registry+https://github.com/rust-lang/crates.io-index"
32
+ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
33
+
34
+ [[package]]
35
+ name = "log"
36
+ version = "0.4.26"
37
+ source = "registry+https://github.com/rust-lang/crates.io-index"
38
+ checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
39
+
40
+ [[package]]
41
+ name = "matchers"
42
+ version = "0.1.0"
43
+ source = "registry+https://github.com/rust-lang/crates.io-index"
44
+ checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
45
+ dependencies = [
46
+ "regex-automata 0.1.10",
47
+ ]
48
+
49
+ [[package]]
50
+ name = "memchr"
51
+ version = "2.7.4"
52
+ source = "registry+https://github.com/rust-lang/crates.io-index"
53
+ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
54
+
55
+ [[package]]
56
+ name = "nu-ansi-term"
57
+ version = "0.46.0"
58
+ source = "registry+https://github.com/rust-lang/crates.io-index"
59
+ checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
60
+ dependencies = [
61
+ "overload",
62
+ "winapi",
63
+ ]
64
+
65
+ [[package]]
66
+ name = "once_cell"
67
+ version = "1.20.3"
68
+ source = "registry+https://github.com/rust-lang/crates.io-index"
69
+ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
70
+
71
+ [[package]]
72
+ name = "overload"
73
+ version = "0.1.1"
74
+ source = "registry+https://github.com/rust-lang/crates.io-index"
75
+ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
76
+
77
+ [[package]]
78
+ name = "pin-project-lite"
79
+ version = "0.2.16"
80
+ source = "registry+https://github.com/rust-lang/crates.io-index"
81
+ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
82
+
83
+ [[package]]
84
+ name = "proc-macro2"
85
+ version = "1.0.93"
86
+ source = "registry+https://github.com/rust-lang/crates.io-index"
87
+ checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
88
+ dependencies = [
89
+ "unicode-ident",
90
+ ]
91
+
92
+ [[package]]
93
+ name = "quote"
94
+ version = "1.0.38"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
97
+ dependencies = [
98
+ "proc-macro2",
99
+ ]
100
+
101
+ [[package]]
102
+ name = "regex"
103
+ version = "1.11.1"
104
+ source = "registry+https://github.com/rust-lang/crates.io-index"
105
+ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
106
+ dependencies = [
107
+ "aho-corasick",
108
+ "memchr",
109
+ "regex-automata 0.4.9",
110
+ "regex-syntax 0.8.5",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "regex-automata"
115
+ version = "0.1.10"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
118
+ dependencies = [
119
+ "regex-syntax 0.6.29",
120
+ ]
121
+
122
+ [[package]]
123
+ name = "regex-automata"
124
+ version = "0.4.9"
125
+ source = "registry+https://github.com/rust-lang/crates.io-index"
126
+ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
127
+ dependencies = [
128
+ "aho-corasick",
129
+ "memchr",
130
+ "regex-syntax 0.8.5",
131
+ ]
132
+
133
+ [[package]]
134
+ name = "regex-syntax"
135
+ version = "0.6.29"
136
+ source = "registry+https://github.com/rust-lang/crates.io-index"
137
+ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
138
+
139
+ [[package]]
140
+ name = "regex-syntax"
141
+ version = "0.8.5"
142
+ source = "registry+https://github.com/rust-lang/crates.io-index"
143
+ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
144
+
145
+ [[package]]
146
+ name = "sharded-slab"
147
+ version = "0.1.7"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+ checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
150
+ dependencies = [
151
+ "lazy_static",
152
+ ]
153
+
154
+ [[package]]
155
+ name = "smallvec"
156
+ version = "1.14.0"
157
+ source = "registry+https://github.com/rust-lang/crates.io-index"
158
+ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
159
+
160
+ [[package]]
161
+ name = "syn"
162
+ version = "2.0.98"
163
+ source = "registry+https://github.com/rust-lang/crates.io-index"
164
+ checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
165
+ dependencies = [
166
+ "proc-macro2",
167
+ "quote",
168
+ "unicode-ident",
169
+ ]
170
+
171
+ [[package]]
172
+ name = "thread_local"
173
+ version = "1.1.8"
174
+ source = "registry+https://github.com/rust-lang/crates.io-index"
175
+ checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
176
+ dependencies = [
177
+ "cfg-if",
178
+ "once_cell",
179
+ ]
180
+
181
+ [[package]]
182
+ name = "tracing"
183
+ version = "0.1.41"
184
+ source = "registry+https://github.com/rust-lang/crates.io-index"
185
+ checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
186
+ dependencies = [
187
+ "pin-project-lite",
188
+ "tracing-attributes",
189
+ "tracing-core",
190
+ ]
191
+
192
+ [[package]]
193
+ name = "tracing-attributes"
194
+ version = "0.1.28"
195
+ source = "registry+https://github.com/rust-lang/crates.io-index"
196
+ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
197
+ dependencies = [
198
+ "proc-macro2",
199
+ "quote",
200
+ "syn",
201
+ ]
202
+
203
+ [[package]]
204
+ name = "tracing-core"
205
+ version = "0.1.33"
206
+ source = "registry+https://github.com/rust-lang/crates.io-index"
207
+ checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
208
+ dependencies = [
209
+ "once_cell",
210
+ "valuable",
211
+ ]
212
+
213
+ [[package]]
214
+ name = "tracing-log"
215
+ version = "0.2.0"
216
+ source = "registry+https://github.com/rust-lang/crates.io-index"
217
+ checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
218
+ dependencies = [
219
+ "log",
220
+ "once_cell",
221
+ "tracing-core",
222
+ ]
223
+
224
+ [[package]]
225
+ name = "tracing-subscriber"
226
+ version = "0.3.19"
227
+ source = "registry+https://github.com/rust-lang/crates.io-index"
228
+ checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
229
+ dependencies = [
230
+ "matchers",
231
+ "nu-ansi-term",
232
+ "once_cell",
233
+ "regex",
234
+ "sharded-slab",
235
+ "smallvec",
236
+ "thread_local",
237
+ "tracing",
238
+ "tracing-core",
239
+ "tracing-log",
240
+ ]
241
+
242
+ [[package]]
243
+ name = "unicode-ident"
244
+ version = "1.0.17"
245
+ source = "registry+https://github.com/rust-lang/crates.io-index"
246
+ checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
247
+
248
+ [[package]]
249
+ name = "valuable"
250
+ version = "0.1.1"
251
+ source = "registry+https://github.com/rust-lang/crates.io-index"
252
+ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
253
+
254
+ [[package]]
255
+ name = "winapi"
256
+ version = "0.3.9"
257
+ source = "registry+https://github.com/rust-lang/crates.io-index"
258
+ checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
259
+ dependencies = [
260
+ "winapi-i686-pc-windows-gnu",
261
+ "winapi-x86_64-pc-windows-gnu",
262
+ ]
263
+
264
+ [[package]]
265
+ name = "winapi-i686-pc-windows-gnu"
266
+ version = "0.4.0"
267
+ source = "registry+https://github.com/rust-lang/crates.io-index"
268
+ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
269
+
270
+ [[package]]
271
+ name = "winapi-x86_64-pc-windows-gnu"
272
+ version = "0.4.0"
273
+ source = "registry+https://github.com/rust-lang/crates.io-index"
274
+ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
@@ -0,0 +1,12 @@
1
+ [package]
2
+ name = "itsi_tracing"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+
6
+ [dependencies]
7
+ tracing = { version = "0.1.41", features = ["attributes"] }
8
+ tracing-subscriber = { version = "0.3.19", features = [
9
+ "env-filter",
10
+ "std",
11
+ "fmt",
12
+ ] }