gatewayd: mm, Mattermost's events and REST calls, typed
Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
//! A scripted HTTP server for tests: it records each request and answers with what the test's
|
||||
//! function returns for it. Built on `tls_server`. Do not edit.
|
||||
|
||||
#![allow(dead_code)] // each test file uses a different part of this module
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex, mpsc};
|
||||
|
||||
use rustls::ServerConfig;
|
||||
|
||||
use crate::tls_server::serve;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Request {
|
||||
pub method: String,
|
||||
pub path: String,
|
||||
/// The whole head, for tests that look for a header.
|
||||
pub head: String,
|
||||
pub body: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn json(&self) -> serde_json::Value {
|
||||
serde_json::from_slice(&self.body).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A response with a JSON body.
|
||||
pub fn reply(status: u16, extra_headers: &str, body: &str) -> String {
|
||||
format!(
|
||||
"HTTP/1.1 {status} X\r\nContent-Type: application/json\r\n{extra_headers}Content-Length: {}\r\n\r\n{body}",
|
||||
body.len()
|
||||
)
|
||||
}
|
||||
|
||||
fn read_request(conn: &mut dyn Read) -> Option<Request> {
|
||||
let mut head = Vec::new();
|
||||
let mut byte = [0u8; 1];
|
||||
while !head.ends_with(b"\r\n\r\n") {
|
||||
if conn.read(&mut byte).ok()? == 0 {
|
||||
return None;
|
||||
}
|
||||
head.push(byte[0]);
|
||||
}
|
||||
let head = String::from_utf8(head).ok()?;
|
||||
let mut first = head.split_whitespace();
|
||||
let method = first.next()?.to_string();
|
||||
let path = first.next()?.to_string();
|
||||
let length = head
|
||||
.lines()
|
||||
.find_map(|l| {
|
||||
l.to_ascii_lowercase()
|
||||
.strip_prefix("content-length:")
|
||||
.map(|v| v.trim().parse().ok())
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or(0);
|
||||
let mut body = vec![0u8; length];
|
||||
conn.read_exact(&mut body).ok()?;
|
||||
Some(Request {
|
||||
method,
|
||||
path,
|
||||
head,
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
/// Serve requests: `answer(n, &request)` gives the response to the n-th request (from 0). Every
|
||||
/// request is sent on the returned channel.
|
||||
pub fn serve_http<F>(
|
||||
tls: Option<Arc<ServerConfig>>,
|
||||
answer: F,
|
||||
) -> (SocketAddr, mpsc::Receiver<Request>)
|
||||
where
|
||||
F: Fn(usize, &Request) -> String + Send + Sync + 'static,
|
||||
{
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let tx = Mutex::new(tx);
|
||||
let count = AtomicUsize::new(0);
|
||||
let addr = serve(tls, move |mut conn| {
|
||||
let Some(request) = read_request(&mut conn) else {
|
||||
return;
|
||||
};
|
||||
let n = count.fetch_add(1, Ordering::SeqCst);
|
||||
let response = answer(n, &request);
|
||||
let _ = tx.lock().unwrap().send(request);
|
||||
let _ = conn.write_all(response.as_bytes());
|
||||
let _ = conn.flush();
|
||||
});
|
||||
(addr, rx)
|
||||
}
|
||||
Reference in New Issue
Block a user