// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Handle a WebSocket handshake request
/// and convert an existing HTTP server connection to a WebSocket tunnel.
///
/// - `request`: the WebSocket handshake request
/// - `conn`: the HTTP server connection
///
/// If the handshake succeeds, a new WebSocket tunnel will be created and returned.
///
/// The ownership of `conn` will be transferred to
/// `@websocket.ServerConnection::from_http`,
/// so the user must NOT use or close the HTTP server connection anymore.
#as_free_fn
pub async fn Conn::from_http_server(
request : @http.Request,
conn : @http.ServerConnection,
) -> Conn {
errdefer conn.close()
async fn bad_request(msg : String) {
conn..send_response(400, "Bad Request")..write(msg).end_response()
raise InvalidHandshake(msg)
}
guard request.meth is Get else { bad_request("Invalid request: must be GET") }
// Validate WebSocket handshake headers
guard request.headers.get("upgrade") is Some(upgrade) &&
upgrade.to_lower() == "websocket" else {
bad_request("Missing or invalid Upgrade header")
}
guard request.headers.get("connection") is Some(connection) &&
connection.to_lower().contains("upgrade") else {
bad_request("Missing or invalid Connection header")
}
guard request.headers.get("sec-websocket-version") is Some(version) &&
version == "13" else {
bad_request("Missing or unsupported WebSocket version")
}
guard request.headers.get("sec-websocket-key") is Some(key) else {
bad_request("Missing Sec-WebSocket-Key header")
}
// Generate accept key
let accept_key = generate_accept_key(key)
conn
..send_response(101, "Switching Protocols", extra_headers={
"Upgrade": "websocket",
"Connection": "Upgrade",
"Sec-WebSocket-Accept": accept_key,
})
..end_response()
.enter_passthrough_mode()
Conn::new(conn, 4096, mask=None)
}