itsi 0.2.26 → 0.2.27.rc1
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.
- checksums.yaml +4 -4
- data/Cargo.lock +7 -3
- data/Rakefile +24 -3
- data/crates/itsi_acme/Cargo.toml +2 -1
- data/crates/itsi_acme/src/acceptor.rs +1 -1
- data/crates/itsi_acme/src/acme.rs +31 -3
- data/crates/itsi_acme/src/http_challenge.rs +81 -0
- data/crates/itsi_acme/src/https_helper.rs +3 -1
- data/crates/itsi_acme/src/jose.rs +6 -2
- data/crates/itsi_acme/src/lib.rs +2 -0
- data/crates/itsi_acme/src/resolver.rs +27 -4
- data/crates/itsi_acme/src/state.rs +183 -22
- data/crates/itsi_scheduler/Cargo.toml +1 -1
- data/crates/itsi_scheduler/src/itsi_scheduler.rs +115 -64
- data/crates/itsi_scheduler/src/lib.rs +2 -1
- data/crates/itsi_server/Cargo.toml +2 -1
- data/crates/itsi_server/src/lib.rs +15 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +9 -0
- data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +95 -0
- data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +22 -1
- data/crates/itsi_server/src/ruby_types/itsi_server.rs +100 -0
- data/crates/itsi_server/src/server/binds/listener.rs +9 -24
- data/crates/itsi_server/src/server/binds/tls.rs +372 -67
- data/crates/itsi_server/src/services/itsi_http_service.rs +46 -2
- data/gems/scheduler/Cargo.lock +4011 -527
- data/gems/scheduler/Gemfile +8 -2
- data/gems/scheduler/Gemfile.lock +107 -0
- data/gems/scheduler/Rakefile +33 -9
- data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
- data/gems/scheduler/lib/itsi/scheduler.rb +121 -6
- data/gems/scheduler/test/helpers/test_helper.rb +2 -0
- data/gems/scheduler/test/test_address_resolve.rb +8 -2
- data/gems/scheduler/test/test_itsi_scheduler.rb +80 -0
- data/gems/scheduler/test/test_timeout_after.rb +102 -0
- data/gems/server/Cargo.lock +30 -1
- data/gems/server/Gemfile +2 -0
- data/gems/server/Gemfile.lock +123 -0
- data/gems/server/Rakefile +18 -5
- data/gems/server/lib/itsi/http_request.rb +10 -0
- data/gems/server/lib/itsi/server/rack_interface.rb +45 -2
- data/gems/server/lib/itsi/server/version.rb +1 -1
- data/gems/server/lib/itsi/server.rb +24 -0
- data/gems/server/test/acme/local_acme_challenges.rb +190 -0
- data/gems/server/test/helpers/local_acme.rb +218 -0
- data/gems/server/test/helpers/test_helper.rb +7 -9
- data/gems/server/test/middleware/endpoint.rb +9 -6
- data/gems/server/test/rack/test_rack_server.rb +79 -0
- data/lib/itsi/version.rb +1 -1
- metadata +12 -6
|
@@ -20,8 +20,9 @@ use x509_parser::parse_x509_certificate;
|
|
|
20
20
|
|
|
21
21
|
use crate::acceptor::AcmeAcceptor;
|
|
22
22
|
use crate::acme::{
|
|
23
|
-
Account, AcmeError, Auth, AuthStatus, Directory, Identifier, Order, OrderStatus,
|
|
23
|
+
Account, AcmeError, Auth, AuthStatus, Challenge, Directory, Identifier, Order, OrderStatus,
|
|
24
24
|
};
|
|
25
|
+
use crate::http_challenge::Http01Handler;
|
|
25
26
|
use crate::{AcmeConfig, Incoming, ResolvesServerCertAcme};
|
|
26
27
|
|
|
27
28
|
type Timer = std::pin::Pin<Box<Sleep>>;
|
|
@@ -36,6 +37,9 @@ pub struct AcmeState<EC: Debug = Infallible, EA: Debug = EC> {
|
|
|
36
37
|
config: Arc<AcmeConfig<EC, EA>>,
|
|
37
38
|
resolver: Arc<ResolvesServerCertAcme>,
|
|
38
39
|
account_key: Option<Vec<u8>>,
|
|
40
|
+
http01_handler: Arc<Http01Handler>,
|
|
41
|
+
http01_enabled: bool,
|
|
42
|
+
managed_domain: Option<String>,
|
|
39
43
|
|
|
40
44
|
early_action: Option<BoxFuture<Event<EC, EA>>>,
|
|
41
45
|
load_cert: Option<BoxFuture<Result<Option<Vec<u8>>, EC>>>,
|
|
@@ -128,12 +132,29 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
128
132
|
pub fn resolver(&self) -> Arc<ResolvesServerCertAcme> {
|
|
129
133
|
self.resolver.clone()
|
|
130
134
|
}
|
|
135
|
+
pub fn http01_handler(&self) -> Arc<Http01Handler> {
|
|
136
|
+
self.http01_handler.clone()
|
|
137
|
+
}
|
|
138
|
+
pub fn set_http01_enabled(&mut self, enabled: bool) {
|
|
139
|
+
self.http01_enabled = enabled;
|
|
140
|
+
}
|
|
131
141
|
pub fn new(config: AcmeConfig<EC, EA>) -> Self {
|
|
142
|
+
Self::new_with_resolver(config, ResolvesServerCertAcme::new(), Arc::new(Http01Handler::new()), None)
|
|
143
|
+
}
|
|
144
|
+
pub fn new_with_resolver(
|
|
145
|
+
config: AcmeConfig<EC, EA>,
|
|
146
|
+
resolver: Arc<ResolvesServerCertAcme>,
|
|
147
|
+
http01_handler: Arc<Http01Handler>,
|
|
148
|
+
managed_domain: Option<String>,
|
|
149
|
+
) -> Self {
|
|
132
150
|
let config = Arc::new(config);
|
|
133
151
|
Self {
|
|
134
152
|
config: config.clone(),
|
|
135
|
-
resolver
|
|
153
|
+
resolver,
|
|
136
154
|
account_key: None,
|
|
155
|
+
http01_handler,
|
|
156
|
+
http01_enabled: false,
|
|
157
|
+
managed_domain,
|
|
137
158
|
early_action: None,
|
|
138
159
|
load_cert: Some(Box::pin({
|
|
139
160
|
let config = config.clone();
|
|
@@ -195,7 +216,11 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
218
|
};
|
|
198
|
-
|
|
219
|
+
let cert = Arc::new(cert);
|
|
220
|
+
match self.managed_domain.as_ref() {
|
|
221
|
+
Some(domain) => self.resolver.set_cert_for_domain(domain.clone(), cert),
|
|
222
|
+
None => self.resolver.set_cert(cert),
|
|
223
|
+
}
|
|
199
224
|
let wait_duration = (validity[1] - (validity[1] - validity[0]) / 3 - Utc::now())
|
|
200
225
|
.max(chrono::Duration::zero())
|
|
201
226
|
.to_std()
|
|
@@ -220,6 +245,8 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
220
245
|
async fn order(
|
|
221
246
|
config: Arc<AcmeConfig<EC, EA>>,
|
|
222
247
|
resolver: Arc<ResolvesServerCertAcme>,
|
|
248
|
+
http01_handler: Arc<Http01Handler>,
|
|
249
|
+
http01_enabled: bool,
|
|
223
250
|
key_pair: Vec<u8>,
|
|
224
251
|
) -> Result<Vec<u8>, OrderError> {
|
|
225
252
|
let directory = Directory::discover(&config.client_config, &config.directory_url).await?;
|
|
@@ -242,10 +269,16 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
242
269
|
loop {
|
|
243
270
|
match order.status {
|
|
244
271
|
OrderStatus::Pending => {
|
|
245
|
-
let auth_futures = order
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
272
|
+
let auth_futures = order.authorizations.iter().map(|url| {
|
|
273
|
+
Self::authorize(
|
|
274
|
+
&config,
|
|
275
|
+
&resolver,
|
|
276
|
+
&http01_handler,
|
|
277
|
+
http01_enabled,
|
|
278
|
+
&account,
|
|
279
|
+
url,
|
|
280
|
+
)
|
|
281
|
+
});
|
|
249
282
|
try_join_all(auth_futures).await?;
|
|
250
283
|
log::info!("completed all authorizations");
|
|
251
284
|
order = account.order(&config.client_config, &order_url).await?;
|
|
@@ -289,40 +322,147 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
289
322
|
async fn authorize(
|
|
290
323
|
config: &AcmeConfig<EC, EA>,
|
|
291
324
|
resolver: &ResolvesServerCertAcme,
|
|
325
|
+
http01_handler: &Http01Handler,
|
|
326
|
+
http01_enabled: bool,
|
|
292
327
|
account: &Account,
|
|
293
328
|
url: &String,
|
|
294
329
|
) -> Result<(), OrderError> {
|
|
295
330
|
let auth = account.auth(&config.client_config, url).await?;
|
|
296
|
-
let (domain,
|
|
331
|
+
let (domain, challenges) = match auth.status {
|
|
297
332
|
AuthStatus::Pending => {
|
|
298
333
|
let Identifier::Dns(domain) = auth.identifier;
|
|
299
|
-
|
|
334
|
+
(domain, auth.challenges)
|
|
335
|
+
}
|
|
336
|
+
AuthStatus::Valid => return Ok(()),
|
|
337
|
+
_ => return Err(OrderError::BadAuth(auth)),
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
log::info!("trigger challenge for {}", &domain);
|
|
341
|
+
|
|
342
|
+
let primary = if http01_enabled {
|
|
343
|
+
ChallengeKind::Http01
|
|
344
|
+
} else {
|
|
345
|
+
ChallengeKind::TlsAlpn01
|
|
346
|
+
};
|
|
347
|
+
let secondary = match primary {
|
|
348
|
+
ChallengeKind::Http01 => ChallengeKind::TlsAlpn01,
|
|
349
|
+
ChallengeKind::TlsAlpn01 => ChallengeKind::Http01,
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
match Self::attempt_authorization(
|
|
353
|
+
config,
|
|
354
|
+
resolver,
|
|
355
|
+
http01_handler,
|
|
356
|
+
account,
|
|
357
|
+
&domain,
|
|
358
|
+
url,
|
|
359
|
+
&challenges,
|
|
360
|
+
primary,
|
|
361
|
+
)
|
|
362
|
+
.await?
|
|
363
|
+
{
|
|
364
|
+
AttemptOutcome::Validated => return Ok(()),
|
|
365
|
+
AttemptOutcome::Unavailable | AttemptOutcome::RetryableFailure => {}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
match Self::attempt_authorization(
|
|
369
|
+
config,
|
|
370
|
+
resolver,
|
|
371
|
+
http01_handler,
|
|
372
|
+
account,
|
|
373
|
+
&domain,
|
|
374
|
+
url,
|
|
375
|
+
&challenges,
|
|
376
|
+
secondary,
|
|
377
|
+
)
|
|
378
|
+
.await?
|
|
379
|
+
{
|
|
380
|
+
AttemptOutcome::Validated => Ok(()),
|
|
381
|
+
AttemptOutcome::Unavailable | AttemptOutcome::RetryableFailure => {
|
|
382
|
+
Err(OrderError::TooManyAttemptsAuth(domain))
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async fn attempt_authorization(
|
|
388
|
+
config: &AcmeConfig<EC, EA>,
|
|
389
|
+
resolver: &ResolvesServerCertAcme,
|
|
390
|
+
http01_handler: &Http01Handler,
|
|
391
|
+
account: &Account,
|
|
392
|
+
domain: &str,
|
|
393
|
+
auth_url: &str,
|
|
394
|
+
challenges: &[Challenge],
|
|
395
|
+
kind: ChallengeKind,
|
|
396
|
+
) -> Result<AttemptOutcome, OrderError> {
|
|
397
|
+
match kind {
|
|
398
|
+
ChallengeKind::TlsAlpn01 => {
|
|
300
399
|
let (challenge, auth_key) =
|
|
301
|
-
account.tls_alpn_01(
|
|
302
|
-
|
|
400
|
+
match account.tls_alpn_01(challenges, domain.to_string()) {
|
|
401
|
+
Ok(value) => value,
|
|
402
|
+
Err(AcmeError::NoTlsAlpn01Challenge) => {
|
|
403
|
+
return Ok(AttemptOutcome::Unavailable);
|
|
404
|
+
}
|
|
405
|
+
Err(error) => return Err(OrderError::Acme(error)),
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
resolver.set_auth_key(domain.to_string(), Arc::new(auth_key));
|
|
303
409
|
account
|
|
304
410
|
.challenge(&config.client_config, &challenge.url)
|
|
305
411
|
.await?;
|
|
306
|
-
|
|
412
|
+
let outcome =
|
|
413
|
+
Self::poll_authorization(config, account, domain, auth_url, &challenge.url)
|
|
414
|
+
.await?;
|
|
415
|
+
resolver.remove_auth_key(domain);
|
|
416
|
+
Ok(outcome)
|
|
307
417
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
418
|
+
ChallengeKind::Http01 => {
|
|
419
|
+
let (challenge, key_authorization) = match account.http_01(challenges) {
|
|
420
|
+
Ok(value) => value,
|
|
421
|
+
Err(AcmeError::NoHttp01Challenge) => return Ok(AttemptOutcome::Unavailable),
|
|
422
|
+
Err(error) => return Err(OrderError::Acme(error)),
|
|
423
|
+
};
|
|
424
|
+
let token = challenge
|
|
425
|
+
.token
|
|
426
|
+
.clone()
|
|
427
|
+
.ok_or(OrderError::Acme(AcmeError::MissingChallengeToken))?;
|
|
428
|
+
|
|
429
|
+
http01_handler.add_challenge(token.clone(), key_authorization);
|
|
430
|
+
account
|
|
431
|
+
.challenge(&config.client_config, &challenge.url)
|
|
432
|
+
.await?;
|
|
433
|
+
let outcome =
|
|
434
|
+
Self::poll_authorization(config, account, domain, auth_url, &challenge.url)
|
|
435
|
+
.await?;
|
|
436
|
+
http01_handler.remove_challenge(&token);
|
|
437
|
+
Ok(outcome)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async fn poll_authorization(
|
|
443
|
+
config: &AcmeConfig<EC, EA>,
|
|
444
|
+
account: &Account,
|
|
445
|
+
domain: &str,
|
|
446
|
+
auth_url: &str,
|
|
447
|
+
challenge_url: &str,
|
|
448
|
+
) -> Result<AttemptOutcome, OrderError> {
|
|
311
449
|
for i in 0u64..5 {
|
|
312
450
|
after(Duration::from_secs(1u64 << i)).await;
|
|
313
|
-
let auth = account.auth(&config.client_config,
|
|
451
|
+
let auth = account.auth(&config.client_config, auth_url).await?;
|
|
314
452
|
match auth.status {
|
|
315
453
|
AuthStatus::Pending => {
|
|
316
|
-
log::info!("authorization for {} still pending",
|
|
454
|
+
log::info!("authorization for {} still pending", domain);
|
|
317
455
|
account
|
|
318
|
-
.challenge(&config.client_config,
|
|
319
|
-
.await
|
|
456
|
+
.challenge(&config.client_config, challenge_url)
|
|
457
|
+
.await?;
|
|
320
458
|
}
|
|
321
|
-
AuthStatus::Valid => return Ok(
|
|
459
|
+
AuthStatus::Valid => return Ok(AttemptOutcome::Validated),
|
|
460
|
+
AuthStatus::Invalid => return Ok(AttemptOutcome::RetryableFailure),
|
|
322
461
|
_ => return Err(OrderError::BadAuth(auth)),
|
|
323
462
|
}
|
|
324
463
|
}
|
|
325
|
-
|
|
464
|
+
|
|
465
|
+
Ok(AttemptOutcome::RetryableFailure)
|
|
326
466
|
}
|
|
327
467
|
fn poll_next_infinite(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Event<EC, EA>> {
|
|
328
468
|
loop {
|
|
@@ -408,13 +548,34 @@ impl<EC: 'static + Debug, EA: 'static + Debug> AcmeState<EC, EA> {
|
|
|
408
548
|
};
|
|
409
549
|
let config = self.config.clone();
|
|
410
550
|
let resolver = self.resolver.clone();
|
|
551
|
+
let http01_handler = self.http01_handler.clone();
|
|
552
|
+
let http01_enabled = self.http01_enabled;
|
|
411
553
|
self.order = Some(Box::pin({
|
|
412
|
-
Self::order(
|
|
554
|
+
Self::order(
|
|
555
|
+
config.clone(),
|
|
556
|
+
resolver.clone(),
|
|
557
|
+
http01_handler,
|
|
558
|
+
http01_enabled,
|
|
559
|
+
account_key,
|
|
560
|
+
)
|
|
413
561
|
}));
|
|
414
562
|
}
|
|
415
563
|
}
|
|
416
564
|
}
|
|
417
565
|
|
|
566
|
+
#[derive(Clone, Copy, Debug)]
|
|
567
|
+
enum ChallengeKind {
|
|
568
|
+
Http01,
|
|
569
|
+
TlsAlpn01,
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
573
|
+
enum AttemptOutcome {
|
|
574
|
+
Validated,
|
|
575
|
+
RetryableFailure,
|
|
576
|
+
Unavailable,
|
|
577
|
+
}
|
|
578
|
+
|
|
418
579
|
impl<EC: 'static + Debug, EA: 'static + Debug> Stream for AcmeState<EC, EA> {
|
|
419
580
|
type Item = Event<EC, EA>;
|
|
420
581
|
|
|
@@ -4,22 +4,19 @@ mod timer;
|
|
|
4
4
|
use io_helpers::{build_interest, poll_readiness, set_nonblocking};
|
|
5
5
|
use io_waiter::IoWaiter;
|
|
6
6
|
use itsi_error::ItsiError;
|
|
7
|
-
use itsi_rb_helpers::
|
|
8
|
-
use magnus::{
|
|
9
|
-
error::Result as MagnusResult,
|
|
10
|
-
value::{InnerValue, Lazy, LazyId, Opaque, ReprValue},
|
|
11
|
-
Module, RClass, Ruby, Value,
|
|
12
|
-
};
|
|
7
|
+
use itsi_rb_helpers::call_without_gvl;
|
|
8
|
+
use magnus::{error::Result as MagnusResult, Ruby};
|
|
13
9
|
use mio::{Events, Poll, Token, Waker};
|
|
14
|
-
use parking_lot::
|
|
10
|
+
use parking_lot::Mutex;
|
|
15
11
|
use std::{
|
|
16
12
|
collections::{BinaryHeap, HashMap, VecDeque},
|
|
13
|
+
ffi::CString,
|
|
17
14
|
os::fd::RawFd,
|
|
18
|
-
|
|
15
|
+
ptr,
|
|
19
16
|
time::Duration,
|
|
20
17
|
};
|
|
21
18
|
use timer::Timer;
|
|
22
|
-
use tracing::{debug,
|
|
19
|
+
use tracing::{debug, info, warn};
|
|
23
20
|
|
|
24
21
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
25
22
|
pub(crate) struct Readiness(i16);
|
|
@@ -31,9 +28,6 @@ impl std::fmt::Debug for ItsiScheduler {
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
const WAKE_TOKEN: Token = Token(0);
|
|
34
|
-
static ID_CURRENT: LazyId = LazyId::new("current");
|
|
35
|
-
static CLASS_FIBER: Lazy<RClass> =
|
|
36
|
-
Lazy::new(|ruby| ruby.module_kernel().const_get("Fiber").unwrap());
|
|
37
31
|
|
|
38
32
|
#[magnus::wrap(class = "Itsi::Scheduler", free_immediately, size)]
|
|
39
33
|
pub(crate) struct ItsiScheduler {
|
|
@@ -123,6 +117,57 @@ impl ItsiScheduler {
|
|
|
123
117
|
self.timers.lock().retain(|timer| timer.token.0 != token);
|
|
124
118
|
}
|
|
125
119
|
|
|
120
|
+
pub fn cancel_wait(&self, token: usize) -> MagnusResult<()> {
|
|
121
|
+
let token = Token(token);
|
|
122
|
+
|
|
123
|
+
self.timers.lock().retain(|timer| timer.token != token);
|
|
124
|
+
|
|
125
|
+
let mut io_waiters = self.io_waiters.lock();
|
|
126
|
+
let Some(mut waiter) = io_waiters.remove(&token) else {
|
|
127
|
+
return Ok(());
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let mut registry = self.registry.lock();
|
|
131
|
+
let Some(queue) = registry.get_mut(&waiter.fd) else {
|
|
132
|
+
return Ok(());
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
let Some(position) = queue.iter().position(|entry| entry.token == token) else {
|
|
136
|
+
return Ok(());
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
if position == 0 {
|
|
140
|
+
self.poll
|
|
141
|
+
.lock()
|
|
142
|
+
.registry()
|
|
143
|
+
.deregister(&mut waiter)
|
|
144
|
+
.map_err(|_| {
|
|
145
|
+
ItsiError::ArgumentError("Failed to deregister".to_string())
|
|
146
|
+
})?;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
queue.remove(position);
|
|
150
|
+
|
|
151
|
+
if position == 0 {
|
|
152
|
+
if let Some(head) = queue.get_mut(0) {
|
|
153
|
+
let interest = build_interest(head.readiness)?;
|
|
154
|
+
self.poll
|
|
155
|
+
.lock()
|
|
156
|
+
.registry()
|
|
157
|
+
.register(head, head.token, interest)
|
|
158
|
+
.map_err(|_| {
|
|
159
|
+
ItsiError::ArgumentError("Failed to register".to_string())
|
|
160
|
+
})?;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if queue.is_empty() {
|
|
165
|
+
registry.remove(&waiter.fd);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
170
|
+
|
|
126
171
|
pub fn has_pending_io(&self) -> bool {
|
|
127
172
|
!self.timers.lock().is_empty() || !self.io_waiters.lock().is_empty()
|
|
128
173
|
}
|
|
@@ -220,63 +265,69 @@ impl ItsiScheduler {
|
|
|
220
265
|
})
|
|
221
266
|
}
|
|
222
267
|
|
|
223
|
-
pub fn run_blocking_in_thread<T, F>(&self, ruby: &Ruby, work: F) -> MagnusResult<Option<T>>
|
|
224
|
-
where
|
|
225
|
-
T: Send + Sync + std::fmt::Debug + 'static,
|
|
226
|
-
F: FnOnce() -> Option<T> + Send + 'static,
|
|
227
|
-
{
|
|
228
|
-
let result: Arc<RwLock<Option<T>>> = Arc::new(RwLock::new(None));
|
|
229
|
-
let result_clone = Arc::clone(&result);
|
|
230
|
-
|
|
231
|
-
let class_fiber = ruby.get_inner(&CLASS_FIBER);
|
|
232
|
-
let current_fiber = ruby
|
|
233
|
-
.get_inner(&CLASS_FIBER)
|
|
234
|
-
.funcall::<_, _, Value>(*ID_CURRENT, ());
|
|
235
|
-
|
|
236
|
-
if current_fiber.is_err() {
|
|
237
|
-
error!("Failed to get current fiber");
|
|
238
|
-
return Err(ItsiError::ArgumentError("Failed to get current fiber".to_string()).into());
|
|
239
|
-
}
|
|
240
|
-
let current_fiber = Opaque::from(current_fiber.unwrap());
|
|
241
|
-
let scheduler = Opaque::from(class_fiber.funcall::<_, _, Value>("scheduler", ()).unwrap());
|
|
242
|
-
|
|
243
|
-
create_ruby_thread(move || {
|
|
244
|
-
call_without_gvl(|| {
|
|
245
|
-
let outcome = work();
|
|
246
|
-
*result_clone.write() = outcome;
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
let ruby = Ruby::get().unwrap();
|
|
250
|
-
scheduler
|
|
251
|
-
.get_inner_with(&ruby)
|
|
252
|
-
.funcall::<_, _, Value>("unblock", (None::<String>, current_fiber))
|
|
253
|
-
.unwrap();
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
scheduler
|
|
257
|
-
.get_inner_with(ruby)
|
|
258
|
-
.funcall::<_, _, Value>("block", (None::<Value>, None::<u64>))?;
|
|
259
|
-
|
|
260
|
-
let result_opt = Arc::try_unwrap(result).unwrap().write().take();
|
|
261
|
-
Ok(result_opt)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
268
|
pub fn address_resolve(
|
|
265
|
-
|
|
266
|
-
|
|
269
|
+
_ruby: &Ruby,
|
|
270
|
+
_rself: &Self,
|
|
267
271
|
hostname: String,
|
|
268
272
|
) -> MagnusResult<Option<Vec<String>>> {
|
|
269
|
-
let result: Option<Vec<String>> =
|
|
270
|
-
|
|
271
|
-
let
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
let result: Option<Vec<String>> = call_without_gvl(move || {
|
|
274
|
+
let hostname = CString::new(hostname).ok()?;
|
|
275
|
+
let hints = nix::libc::addrinfo {
|
|
276
|
+
ai_flags: 0,
|
|
277
|
+
ai_family: nix::libc::AF_UNSPEC,
|
|
278
|
+
ai_socktype: nix::libc::SOCK_STREAM,
|
|
279
|
+
ai_protocol: 0,
|
|
280
|
+
ai_addrlen: 0,
|
|
281
|
+
ai_addr: ptr::null_mut(),
|
|
282
|
+
ai_canonname: ptr::null_mut(),
|
|
283
|
+
ai_next: ptr::null_mut(),
|
|
284
|
+
};
|
|
285
|
+
let mut res: *mut nix::libc::addrinfo = ptr::null_mut();
|
|
286
|
+
let rc = unsafe {
|
|
287
|
+
nix::libc::getaddrinfo(hostname.as_ptr(), ptr::null(), &hints, &mut res)
|
|
288
|
+
};
|
|
289
|
+
if rc != 0 || res.is_null() {
|
|
290
|
+
return None;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let mut ips = Vec::new();
|
|
294
|
+
let mut current = res;
|
|
295
|
+
while !current.is_null() {
|
|
296
|
+
let ai = unsafe { &*current };
|
|
297
|
+
if !ai.ai_addr.is_null() {
|
|
298
|
+
match ai.ai_family {
|
|
299
|
+
nix::libc::AF_INET => {
|
|
300
|
+
let addr = unsafe {
|
|
301
|
+
&*(ai.ai_addr as *const nix::libc::sockaddr_in)
|
|
302
|
+
};
|
|
303
|
+
let ip = std::net::Ipv4Addr::from(u32::from_be(addr.sin_addr.s_addr));
|
|
304
|
+
ips.push(ip.to_string());
|
|
305
|
+
}
|
|
306
|
+
nix::libc::AF_INET6 => {
|
|
307
|
+
let addr = unsafe {
|
|
308
|
+
&*(ai.ai_addr as *const nix::libc::sockaddr_in6)
|
|
309
|
+
};
|
|
310
|
+
let ip = std::net::Ipv6Addr::from(addr.sin6_addr.s6_addr);
|
|
311
|
+
ips.push(ip.to_string());
|
|
312
|
+
}
|
|
313
|
+
_ => {}
|
|
314
|
+
}
|
|
276
315
|
}
|
|
277
|
-
|
|
316
|
+
current = ai.ai_next;
|
|
278
317
|
}
|
|
279
|
-
|
|
318
|
+
|
|
319
|
+
unsafe {
|
|
320
|
+
nix::libc::freeaddrinfo(res);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if ips.is_empty() {
|
|
324
|
+
None
|
|
325
|
+
} else {
|
|
326
|
+
ips.sort();
|
|
327
|
+
ips.dedup();
|
|
328
|
+
Some(ips)
|
|
329
|
+
}
|
|
330
|
+
});
|
|
280
331
|
Ok(result)
|
|
281
332
|
}
|
|
282
333
|
|
|
@@ -20,8 +20,9 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
20
20
|
scheduler.define_method("warn", method!(ItsiScheduler::warn, 1))?;
|
|
21
21
|
scheduler.define_method("start_timer", method!(ItsiScheduler::start_timer, 2))?;
|
|
22
22
|
scheduler.define_method("clear_timer", method!(ItsiScheduler::clear_timer, 1))?;
|
|
23
|
+
scheduler.define_method("cancel_wait", method!(ItsiScheduler::cancel_wait, 1))?;
|
|
23
24
|
scheduler.define_method(
|
|
24
|
-
"
|
|
25
|
+
"native_address_resolve",
|
|
25
26
|
method!(ItsiScheduler::address_resolve, 1),
|
|
26
27
|
)?;
|
|
27
28
|
scheduler.define_method("has_pending_io?", method!(ItsiScheduler::has_pending_io, 0))?;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "itsi-server"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.27-rc1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Wouter Coppieters <wc@pico.net.nz>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -70,6 +70,7 @@ redis = { version = "0.29.2", features = [
|
|
|
70
70
|
] }
|
|
71
71
|
rustls = "0.23.23"
|
|
72
72
|
rustls-pemfile = "2.2.0"
|
|
73
|
+
webpki-roots = "0.26"
|
|
73
74
|
serde = "1.0.219"
|
|
74
75
|
serde_json = "1.0.140"
|
|
75
76
|
serde_magnus = "0.11.0"
|
|
@@ -41,6 +41,20 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
|
41
41
|
server.define_singleton_method("reset_signal_handlers", function!(reset_signal_handlers, 0))?;
|
|
42
42
|
server.define_method("start", method!(ItsiServer::start, 0))?;
|
|
43
43
|
server.define_method("stop", method!(ItsiServer::stop, 0))?;
|
|
44
|
+
server.define_method("tls_bindings", method!(ItsiServer::tls_bindings, 0))?;
|
|
45
|
+
server.define_method("tls_domains", method!(ItsiServer::tls_domains, 1))?;
|
|
46
|
+
server.define_method(
|
|
47
|
+
"tls_domain_statuses",
|
|
48
|
+
method!(ItsiServer::tls_domain_statuses, 1),
|
|
49
|
+
)?;
|
|
50
|
+
server.define_method(
|
|
51
|
+
"register_tls_domain",
|
|
52
|
+
method!(ItsiServer::register_tls_domain, 2),
|
|
53
|
+
)?;
|
|
54
|
+
server.define_method(
|
|
55
|
+
"unregister_tls_domain",
|
|
56
|
+
method!(ItsiServer::unregister_tls_domain, 2),
|
|
57
|
+
)?;
|
|
44
58
|
|
|
45
59
|
let request = ruby.get_inner(&ITSI_REQUEST);
|
|
46
60
|
request.define_method("path", method!(ItsiHttpRequest::path, 0))?;
|
|
@@ -94,6 +108,7 @@ fn init(ruby: &Ruby) -> Result<()> {
|
|
|
94
108
|
response.define_method("<<", method!(ItsiHttpResponse::send_frame, 1))?;
|
|
95
109
|
response.define_method("write", method!(ItsiHttpResponse::send_frame, 1))?;
|
|
96
110
|
response.define_method("read", method!(ItsiHttpResponse::recv_frame, 0))?;
|
|
111
|
+
response.define_method("partial_hijack", method!(ItsiHttpResponse::partial_hijack, 1))?;
|
|
97
112
|
response.define_method("closed?", method!(ItsiHttpResponse::is_closed, 0))?;
|
|
98
113
|
response.define_method(
|
|
99
114
|
"send_and_close",
|
|
@@ -211,6 +211,15 @@ impl ItsiHttpRequest {
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
+
Ok(ResponseFrame::PartialHijackedResponse(response)) => {
|
|
215
|
+
match response.process_partial_hijacked_response().await {
|
|
216
|
+
Ok(result) => Ok(result),
|
|
217
|
+
Err(e) => {
|
|
218
|
+
error!("Error processing partial hijacked response: {}", e);
|
|
219
|
+
Ok(Response::new(HttpBody::empty()))
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
214
223
|
Err(_) => {
|
|
215
224
|
error!("Failed to receive response from receiver");
|
|
216
225
|
Ok(INTERNAL_SERVER_ERROR_RESPONSE
|