Crate waitup

Crate waitup 

Source
Expand description

A robust library for waiting until TCP ports, HTTP endpoints, and services become available.

This library provides functionality for testing network connectivity and service availability, with support for DNS resolution, exponential backoff, and multiple connection strategies. Perfect for Docker, Kubernetes, CI/CD pipelines, and microservices orchestration.

§Features

  • Type Safety: NewType wrappers for ports and hostnames with validation
  • Multiple Protocols: TCP socket connections and HTTP/HTTPS requests
  • Flexible Configuration: Timeouts, retry limits, exponential backoff
  • Concurrency Strategies: Wait for all targets or any target
  • Graceful Cancellation: Cancellation token support for clean shutdown
  • Rich Error Context: Detailed error information with contextual messages
  • High Performance: Optimized for minimal allocations and fast execution
  • Comprehensive Testing: Property-based and parameterized test coverage

§Quick Start

Add to your Cargo.toml:

[dependencies]
waitup = "1.0"
tokio = { version = "1.0", features = ["full"] }

§Examples

§Basic TCP Connection Check

use waitup::{Target, WaitConfig, wait_for_connection};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), waitup::WaitForError> {
    let target = Target::tcp("localhost", 8080)?;
    let config = WaitConfig::builder()
        .timeout(Duration::from_secs(30))
        .interval(Duration::from_secs(1))
        .build();

    wait_for_connection(&[target], &config).await?;
    println!("Service is ready!");
    Ok(())
}

§HTTP Health Check with Custom Headers

use waitup::Target;
use url::Url;

#[tokio::main]
async fn main() -> Result<(), waitup::WaitForError> {
    let target = Target::http_builder(Url::parse("https://api.example.com/health")?)
        .status(200)
        .auth_bearer("your-api-token")
        .content_type("application/json")
        .build()?;

    let config = waitup::WaitConfig::builder()
        .timeout(std::time::Duration::from_secs(60))
        .build();

    waitup::wait_for_connection(&[target], &config).await?;
    println!("API is healthy!");
    Ok(())
}

§Multiple Services with Different Strategies

use waitup::{Target, WaitConfig, wait_for_connection};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), waitup::WaitForError> {
    let targets = vec![
        Target::tcp("database", 5432)?,
        Target::tcp("cache", 6379)?,
        Target::http_url("https://api.example.com/health", 200)?,
    ];

    // Wait for ALL services to be ready
    let config = WaitConfig::builder()
        .timeout(Duration::from_secs(120))
        .wait_for_any(false)
        .max_retries(Some(20))
        .build();

    wait_for_connection(&targets, &config).await?;
    println!("All services are ready!");
    Ok(())
}

§Advanced Configuration with Cancellation

use waitup::{Target, WaitConfig, wait_for_connection};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main]
async fn main() -> Result<(), waitup::WaitForError> {
    let target = Target::tcp("slow-service", 8080)?;

    let (builder, cancel_token) = WaitConfig::builder()
        .timeout(Duration::from_secs(60))
        .interval(Duration::from_millis(500))
        .with_cancellation();

    let config = builder.build();

    // Cancel after 10 seconds
    let cancel_handle = {
        let token = cancel_token.clone();
        tokio::spawn(async move {
            sleep(Duration::from_secs(10)).await;
            token.cancel();
        })
    };

    match wait_for_connection(&[target], &config).await {
        Ok(_) => println!("Service is ready!"),
        Err(waitup::WaitForError::Cancelled) => println!("Operation was cancelled"),
        Err(e) => println!("Error: {}", e),
    }

    cancel_handle.abort(); // Clean up the cancel task
    Ok(())
}

§Docker Compose Integration

use waitup::{Target, WaitConfig, wait_for_connection};
use std::time::Duration;

/// Wait for services defined in docker-compose.yml
#[tokio::main]
async fn main() -> Result<(), waitup::WaitForError> {
    let services = vec![
        Target::tcp("postgres", 5432)?,     // Database
        Target::tcp("redis", 6379)?,        // Cache
        Target::tcp("elasticsearch", 9200)?, // Search
        Target::http_url("http://web:8000/health", 200)?, // Web app
    ];

    let config = WaitConfig::builder()
        .timeout(Duration::from_secs(300))  // 5 minutes for Docker startup
        .interval(Duration::from_secs(2))   // Check every 2 seconds
        .max_interval(Duration::from_secs(10)) // Max 10 seconds between retries
        .connection_timeout(Duration::from_secs(5)) // 5 second connection timeout
        .wait_for_any(false)               // Wait for ALL services
        .build();

    println!("Waiting for services to be ready...");
    wait_for_connection(&services, &config).await?;
    println!("All services are ready! Starting application...");
    Ok(())
}

Re-exports§

pub use async_traits::AsyncConnectionStrategy;
pub use async_traits::AsyncRetryStrategy;
pub use async_traits::AsyncTargetChecker;
pub use async_traits::ConcurrentProgressStrategy;
pub use async_traits::DefaultTargetChecker;
pub use async_traits::ExponentialBackoffStrategy;
pub use async_traits::LinearBackoffStrategy;
pub use async_traits::WaitForAllStrategy;
pub use async_traits::WaitForAnyStrategy;
pub use config::WaitConfigBuilder;
pub use connection::wait_for_connection;
pub use connection::wait_for_single_target;
pub use error::Result;
pub use error::ResultExt;
pub use error::WaitForError;
pub use iterators::ResultSummary;
pub use iterators::TargetIterExt;
pub use iterators::TargetResultIterExt;
pub use security::RateLimiter;
pub use security::SecurityValidator;
pub use target::HttpTargetBuilder;
pub use target::TcpTargetBuilder;
pub use types::ConnectionError;
pub use types::Hostname;
pub use types::HttpError;
pub use types::Port;
pub use types::PortCategory;
pub use types::RetryCount;
pub use types::StatusCode;
pub use types::Target;
pub use types::TargetResult;
pub use types::WaitConfig;
pub use types::WaitResult;
pub use zero_cost::ConstRetryStrategy;
pub use zero_cost::DynamicPort;
pub use zero_cost::LazyFormat;
pub use zero_cost::RegisteredPort;
pub use zero_cost::SmallString;
pub use zero_cost::StringBuilder;
pub use zero_cost::TargetDisplay;
pub use zero_cost::ValidatedPort;
pub use zero_cost::WellKnownPort;

Modules§

async_traits
Async traits for extensible connection strategies and target checking.
config
Configuration builders and related functionality.
connection
Connection handling and network operations.
error
Error types and handling for waitup operations.
iterators
Iterator utilities and extensions for working with targets and results.
macros
Convenience macros for creating targets and configurations.
presets
Common presets and configurations for typical use cases.
security
Security and robustness enhancements for network operations.
target
Target implementation and builders.
types
Core type definitions for waitup library.
zero_cost
Zero-cost abstractions for eliminating allocations in hot paths.

Macros§

assert_ready
Assert that all targets in a collection are ready within a timeout (panics on failure).
check_ready
Check that all targets in a collection are ready within a timeout.
common_ports
Create common port configurations.
http_targets
Create HTTP targets from a compact syntax.
lazy_format
Macro for creating lazy format strings
tcp_targets
Create TCP targets from a compact syntax.
wait_config
Create a wait configuration with a compact syntax.
zero_alloc_error
Macro for creating zero-allocation error messages