Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions rs/moq-relay/src/landing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>moq-relay</title>
<style>
:root {
color-scheme: light dark;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Ubuntu,
sans-serif;
}
body {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
box-sizing: border-box;
background: #0b0d10;
color: #e5e7eb;
}
main {
max-width: 40rem;
text-align: center;
}
h1 {
font-size: 1.5rem;
margin: 0 0 1rem;
}
p {
margin: 0 0 1rem;
line-height: 1.5;
}
a {
color: #60a5fa;
}
code {
background: rgba(255, 255, 255, 0.08);
padding: 0.1rem 0.35rem;
border-radius: 0.25rem;
font-size: 0.95em;
}
</style>
</head>
<body>
<main>
<h1>moq-relay</h1>
<p>
This is a <code>moq-relay</code> instance, and you're not a MoQ client.
</p>
<p>
See <a href="https://moq.dev">https://moq.dev</a> for more info.
</p>
Comment on lines +62 to +64
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add the spec link called out in the PR objective.

The page only points users at moq.dev right now, so the “spec + site” guidance described for this feature is still incomplete.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rs/moq-relay/src/landing.html` around lines 62 - 64, The landing page
currently only links to https://moq.dev; update the paragraph that renders the
link (the <p> element containing "See https://moq.dev for more info.") to also
include the specification URL referenced in the PR objective by adding an
additional anchor (or appending the spec link) alongside the moq.dev link so
users can access both the site and the spec; modify the content of that <p> to
include the spec link text and URL.

</main>
</body>
</html>
20 changes: 19 additions & 1 deletion rs/moq-relay/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use axum::{
body::Body,
extract::{Path, Query, State},
http::{Method, StatusCode},
response::{IntoResponse, Response},
response::{Html, IntoResponse, Response},
routing::get,
};
use bytes::Bytes;
Expand Down Expand Up @@ -109,6 +109,7 @@ impl Web {
};

let app = app
.fallback(serve_landing)
.layer(CorsLayer::new().allow_origin(Any).allow_methods([Method::GET]))
.with_state(Arc::new(self.state))
.into_make_service();
Expand Down Expand Up @@ -160,6 +161,23 @@ async fn reload_certs(config: axum_server::tls_rustls::RustlsConfig, cert: PathB
}
}

/// HTML landing page served when a plain browser hits the relay directly.
///
/// MoQ clients speak WebTransport or WebSocket, so a GET request from a
/// regular browser isn't something we can service. Rather than exposing an
/// internal error (e.g. the "Request method must be `CONNECT`" rejection
/// from axum's WebSocket extractor), we render a short informational page.
pub(crate) const LANDING_PAGE: &str = include_str!("landing.html");

pub(crate) fn landing_response() -> Response {
(StatusCode::NOT_FOUND, Html(LANDING_PAGE)).into_response()
}

/// Axum fallback handler for any unmatched route.
async fn serve_landing() -> Response {
landing_response()
}

async fn serve_fingerprint(State(state): State<Arc<WebState>>) -> String {
// Get the first certificate's fingerprint.
// TODO serve all of them so we can support multiple signature algorithms.
Expand Down
20 changes: 15 additions & 5 deletions rs/moq-relay/src/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,30 @@ use std::{
};

use axum::{
extract::{Path, Query, State, WebSocketUpgrade},
extract::{
Path, Query, State, WebSocketUpgrade,
rejection::{PathRejection, QueryRejection},
ws::rejection::WebSocketUpgradeRejection,
},
http::StatusCode,
response::Response,
};
use moq_lite::{OriginConsumer, OriginProducer};

use crate::{AuthParams, WebState, web::AuthQuery};
use crate::{AuthParams, WebState, web::AuthQuery, web::landing_response};

pub(crate) async fn serve_ws(
ws: WebSocketUpgrade,
Path(path): Path<String>,
Query(query): Query<AuthQuery>,
ws: Result<WebSocketUpgrade, WebSocketUpgradeRejection>,
path: Result<Path<String>, PathRejection>,
query: Result<Query<AuthQuery>, QueryRejection>,
State(state): State<Arc<WebState>>,
) -> axum::response::Result<Response> {
// If this isn't a WebSocket upgrade (e.g. a plain browser visit), serve
// the informational landing page instead of an error response.
let (Ok(ws), Ok(Path(path)), Ok(Query(query))) = (ws, path, query) else {
return Ok(landing_response());
};

let ws = ws.protocols(["webtransport"]);

let params = AuthParams {
Expand Down
Loading