///|
/// callback_server.native.mbt — CallbackServer via @http.Server (native target).
///|
pub(all) struct NativeCallbackServer {
mut code : String
mut server : @http.Server?
}
///|
pub fn NativeCallbackServer::NativeCallbackServer() -> NativeCallbackServer {
{ code: "", server: None, }
}
///|
/// The CallbackServer impls are target-specific pub API for
/// `run_pkce_browser_flow`, which no in-workspace product calls yet; the
/// compiler's unused-impl analysis only sees dispatch through `&CallbackServer`
/// and flags `start`, so silence just that diagnostic here.
#warnings("-unused_value")
pub impl CallbackServer for NativeCallbackServer with fn start(
self : NativeCallbackServer,
port : Int,
) -> Unit raise OAuthError {
let addr = @socket.Addr::resolve("127.0.0.1", port~) catch {
err => raise OAuthError::HttpError("resolve: " + err.to_string())
}
let srv = @http.Server(addr) catch {
err => raise OAuthError::HttpError("server bind: " + err.to_string())
}
self.server = Some(srv)
// No background accept loop — wait_callback polls accept directly for the
// single callback.
}
///|
pub extend NativeCallbackServer with CallbackServer::{
start,
stop,
wait_callback,
}
///|
pub impl CallbackServer for NativeCallbackServer with fn wait_callback(
self : NativeCallbackServer,
) -> (String, String) raise OAuthError {
let deadline = (@async.now() / 1000L).to_int() + 300 // 5 min
match self.server {
Some(srv) => {
while true {
let now = (@async.now() / 1000L).to_int()
if now >= deadline {
raise OAuthError::ExpiredToken
}
let (conn, _) = srv.accept() catch {
_ => {
@async.sleep(1)
continue
}
}
let request = conn.read_request() catch {
_ => {
conn.close()
continue
}
}
match extract_query_param(request.path, "code") {
Some(code) => {
conn.send_response(200, "Login successful. You can close this tab.") catch {
_ => ()
}
conn.end_response() catch {
_ => ()
}
conn.close()
// A missing state surfaces later as "oauth state mismatch".
let state = match extract_query_param(request.path, "state") {
Some(s) => s
None => ""
}
return (code, state)
}
None => {
conn.send_response(404, "Not Found") catch {
_ => ()
}
conn.end_response() catch {
_ => ()
}
conn.close()
continue
}
}
}
abort("unreachable")
}
None => raise OAuthError::HttpError("server not started")
}
}
///|
pub impl CallbackServer for NativeCallbackServer with fn stop(
self : NativeCallbackServer,
) -> Unit {
match self.server {
Some(s) => {
s.close()
self.server = None
}
None => ()
}
}
///|
/// Factory: create a native CallbackServer.
pub fn create_callback_server() -> NativeCallbackServer {
NativeCallbackServer::NativeCallbackServer()
}