Skip to content

Commit

Permalink
expose serve fn
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Bond <[email protected]>
  • Loading branch information
loshz committed Jun 5, 2022
1 parent 1a386d7 commit d91f3c5
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- main

env:
RUST_VERSION: 1.60
RUST_VERSION: 1.61

jobs:
lint:
Expand Down
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "metrics_server"
version = "0.6.1"
version = "0.7.0"
authors = ["Dan Bond <[email protected]>"]
edition = "2021"
rust-version = "1.58"
Expand All @@ -14,10 +14,13 @@ keywords = ["http", "prometheus", "metrics"]
categories = ["web-programming::http-server"]
include = ["src/**/*", "tests", "examples", "Cargo.toml", "LICENSE", "README.md"]

[lib]
doctest = false

[dependencies]
tiny_http = { version = "0.11", features = ["ssl-rustls"] }
log = "0.4"

[dev-dependencies]
prometheus-client = "0.15"
prometheus-client = "0.16"
reqwest = { version = "0.11", features = ["blocking"] }
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This crate provides a thread safe, minimalstic HTTP/S server used to buffer metr
Include the lib in your `Cargo.toml` dependencies:
```toml
[dependencies]
metrics_server = "0.6"
metrics_server = "0.7"
```

### HTTP
Expand Down
19 changes: 19 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//!
//! # Examples
//!
//! Start a HTTP server:
//!
//! ```rust
//! use metrics_server::MetricsServer;
//!
Expand All @@ -19,6 +21,23 @@
//! let bytes = server.update(Vec::from([1, 2, 3, 4]));
//! assert_eq!(4, bytes);
//! ```
//!
//! Start a HTTPS server:
//!
//! ```rust
//! use metrics_server::MetricsServer;
//!
//! // Load TLS config.
//! let cert = include_bytes!("/path/to/cert.pem").to_vec();
//! let key = include_bytes!("/path/to/key.pem").to_vec();
//!
//! // Create a new HTTPS server and start listening for requests in the background.
//! let server = MetricsServer::https("localhost:8443", cert, key);
//!
//! // Publish your application metrics.
//! let bytes = server.update(Vec::from([1, 2, 3, 4]));
//! assert_eq!(4, bytes);
//! ```
mod server;

pub use server::MetricsServer;
78 changes: 43 additions & 35 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ struct MetricsServerShared {
}

impl MetricsServer {
/// Creates an empty `MetricsServer` and starts a HTTP/S server on a new thread at the given address.
///
/// This server will only respond synchronously as it blocks until receiving new requests.
/// Creates an empty `MetricsServer` with a configured HTTP/S server.
///
/// # Panics
///
Expand All @@ -30,15 +28,35 @@ impl MetricsServer {
where
A: ToSocketAddrs,
{
match (certificate, private_key) {
(Some(cert), Some(key)) => MetricsServer::https(addr, cert, key),
_ => MetricsServer::http(addr),
// Parse TLS config.
let config = match (certificate, private_key) {
(Some(certificate), Some(private_key)) => tiny_http::ServerConfig {
addr,
ssl: Some(tiny_http::SslConfig {
certificate,
private_key,
}),
},
// Default to no TLS.
_ => tiny_http::ServerConfig { addr, ssl: None },
};

// Create an Arc of the shared data.
let shared = Arc::new(MetricsServerShared {
data: Mutex::new(Vec::new()),
server: Server::new(config).unwrap(),
stop: AtomicBool::new(false),
});

MetricsServer {
shared,
thread: None,
}
}

/// Shortcut for creating an empty `MetricsServer` and starting a HTTP server on a new thread at the given address.
///
/// This server will only respond synchronously as it blocks until receiving new requests.
/// The server will only respond synchronously as it blocks until receiving new requests.
///
/// # Panics
///
Expand All @@ -47,14 +65,12 @@ impl MetricsServer {
where
A: ToSocketAddrs,
{
let config = tiny_http::ServerConfig { addr, ssl: None };

MetricsServer::serve(config)
MetricsServer::new(addr, None, None).serve()
}

/// Shortcut for creating an empty `MetricsServer` and starting a HTTPS server on a new thread at the given address.
///
/// This server will only respond synchronously as it blocks until receiving new requests.
/// The server will only respond synchronously as it blocks until receiving new requests.
///
/// Note: there is currently no option to skip TLS cert verification.
///
Expand All @@ -65,44 +81,36 @@ impl MetricsServer {
where
A: ToSocketAddrs,
{
let config = tiny_http::ServerConfig {
addr,
ssl: Some(tiny_http::SslConfig {
certificate,
private_key,
}),
};

MetricsServer::serve(config)
MetricsServer::new(addr, Some(certificate), Some(private_key)).serve()
}

/// Safely updates the data in a `MetricsServer` and returns the number of bytes written.
///
/// This method is protected by a mutex making it safe
/// to call concurrently from multiple threads.
/// This method is protected by a mutex making it safe to call concurrently from multiple threads.
pub fn update(&self, data: Vec<u8>) -> usize {
let mut buf = self.shared.data.lock().unwrap();
*buf = data;
buf.as_slice().len()
}

fn serve<A>(config: tiny_http::ServerConfig<A>) -> Self
where
A: ToSocketAddrs,
{
// Create an Arc of the shared data.
let shared = Arc::new(MetricsServerShared {
data: Mutex::new(Vec::new()),
server: Server::new(config).unwrap(),
stop: AtomicBool::new(false),
});
/// Start serving requests on the underlying server.
///
/// The server will only respond synchronously as it blocks until receiving new requests.
///
/// # Panics
///
/// Panics if called on a server that has already been started.
pub fn serve(mut self) -> Self {
if self.thread.is_some() {
panic!("metrics_server already started")
}

// Invoking clone on Arc produces a new Arc instance, which points to the
// same allocation on the heap as the source Arc, while increasing a reference count.
let s = Arc::clone(&shared);
let s = Arc::clone(&self.shared);

// Handle requests in a new thread so we can process in the background.
let thread = Some(thread::spawn({
self.thread = Some(thread::spawn({
move || {
// Blocks until the next request is received.
for req in s.server.incoming_requests() {
Expand Down Expand Up @@ -146,7 +154,7 @@ impl MetricsServer {
}
}));

MetricsServer { shared, thread }
self
}
}

Expand Down
24 changes: 22 additions & 2 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,32 @@ fn test_new_server_invalid_address() {
}

#[test]
fn test_new_http_server_serve() {
fn test_new_http_server() {
let _ = MetricsServer::new("localhost:8001", None, None);
}

#[test]
fn test_new_https_server_serve() {
#[should_panic]
fn test_new_server_invalid_cert() {
// Load TLS config.
let cert = Vec::new();
let key = include_bytes!("./certs/private_key.pem").to_vec();

let _ = MetricsServer::new("localhost:8441", Some(cert), Some(key));
}

#[test]
#[should_panic]
fn test_new_server_invalid_key() {
// Load TLS config.
let cert = include_bytes!("./certs/certificate.pem").to_vec();
let key = Vec::new();

let _ = MetricsServer::new("localhost:8442", Some(cert), Some(key));
}

#[test]
fn test_new_https_server() {
// Load TLS config.
let cert = include_bytes!("./certs/certificate.pem").to_vec();
let key = include_bytes!("./certs/private_key.pem").to_vec();
Expand Down

0 comments on commit d91f3c5

Please sign in to comment.