Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: WebSocket #220

Merged
merged 15 commits into from
Jul 20, 2024
Merged
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,32 @@ async fn main() {

<br>

### WebSocket with `"ws"` feature

Currently, WebSocket on `rt_worker` is not supported.

```rust,no_run
use ohkami::prelude::*;
use ohkami::ws::{WebSocketContext, WebSocket, Message};

async fn echo_text(c: WebSocketContext<'_>) -> WebSocket {
c.connect(|mut ws| async move {
while let Ok(Some(Message::Text(text))) = ws.recv().await {
ws.send(Message::Text(text)).await.expect("Failed to send text");
}
})
}

#[tokio::main]
async fn main() {
Ohkami::new((
"/ws".GET(echo_text),
)).howl("localhost:3030").await
}
```

<br>

### Pack of Ohkamis

```rust,no_run
Expand Down Expand Up @@ -384,7 +410,7 @@ async fn test_my_ohkami() {
- [ ] HTTP/3
- [ ] HTTPS
- [x] Server-Sent Events
- [ ] WebSocket
- [x] WebSocket

## MSRV (Minimum Supported Rust Version)

Expand Down
8 changes: 5 additions & 3 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tasks:
test_doc:
dir: ohkami
cmds:
- cargo test --doc --features DEBUG,rt_tokio,sse
- cargo test --doc --features DEBUG,rt_tokio,sse,ws

test_examples:
dir: examples
Expand All @@ -58,7 +58,7 @@ tasks:
dir: ohkami
cmds:
- cargo test --lib --features rt_tokio,DEBUG,{{.MAYBE_NIGHTLY}}
- cargo test --lib --features rt_tokio,DEBUG,sse,{{.MAYBE_NIGHTLY}}
- cargo test --lib --features rt_tokio,DEBUG,sse,ws,{{.MAYBE_NIGHTLY}}

test_rt_async-std:
vars:
Expand All @@ -67,7 +67,7 @@ tasks:
dir: ohkami
cmds:
- cargo test --lib --features rt_async-std,DEBUG,{{.MAYBE_NIGHTLY}}
- cargo test --lib --features rt_async-std,DEBUG,sse,{{.MAYBE_NIGHTLY}}
- cargo test --lib --features rt_async-std,DEBUG,sse,ws,{{.MAYBE_NIGHTLY}}

test_rt_worker:
vars:
Expand Down Expand Up @@ -100,6 +100,7 @@ tasks:
cmds:
- cargo check --lib --features rt_tokio,{{.MAYBE_NIGHTLY}}
- cargo check --lib --features rt_tokio,sse,{{.MAYBE_NIGHTLY}}
- cargo check --lib --features rt_tokio,sse,ws,{{.MAYBE_NIGHTLY}}

check_rt_async-std:
vars:
Expand All @@ -109,6 +110,7 @@ tasks:
cmds:
- cargo check --lib --features rt_async-std,{{.MAYBE_NIGHTLY}}
- cargo check --lib --features rt_async-std,sse,{{.MAYBE_NIGHTLY}}
- cargo check --lib --features rt_async-std,sse,ws,{{.MAYBE_NIGHTLY}}

check_rt_worker:
vars:
Expand Down
6 changes: 3 additions & 3 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"form",
"hello",
"openai",
"websocket",
"realworld",
"basic_auth",
"quick_start",
Expand All @@ -14,9 +15,8 @@ members = [
]

[workspace.dependencies]
# To assure "DEBUG" feature be off even if DEBUGing `../ohkami`,
# explicitly set `default-features = false`
ohkami = { path = "../ohkami", default-features = false, features = ["rt_tokio", "testing", "sse"] }
# set `default-features = false` to assure "DEBUG" feature be off even when DEBUGing `../ohkami`
ohkami = { path = "../ohkami", default-features = false, features = ["rt_tokio", "testing", "sse", "ws"] }
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7.3", features = ["runtime-tokio-native-tls", "postgres", "macros", "chrono", "uuid"] }
tracing = "0.1"
Expand Down
11 changes: 11 additions & 0 deletions examples/websocket/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "websocket"
version = "0.1.0"
edition = "2021"

[dependencies]
ohkami = { workspace = true }
tokio = { workspace = true }

[features]
DEBUG = ["ohkami/DEBUG"]
47 changes: 47 additions & 0 deletions examples/websocket/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use ohkami::prelude::*;
use ohkami::ws::{WebSocketContext, WebSocket, Message};


#[derive(Clone)]
struct Logger;
impl FangAction for Logger {
async fn fore<'a>(&'a self, req: &'a mut Request) -> Result<(), Response> {
Ok(println!("\n{req:#?}"))
}

async fn back<'a>(&'a self, res: &'a mut Response) {
println!("\n{res:#?}")
}
}

async fn echo_text(c: WebSocketContext<'_>) -> WebSocket {
c.connect(|mut ws| async move {
#[cfg(feature="DEBUG")] {
println!("WebSocket handler is called");
}

#[cfg(feature="DEBUG")] {
loop {
let r = dbg!(ws.recv().await);
let Ok(Some(Message::Text(text))) = r else {
break
};
println!("recv: `{text}`");
ws.send(Message::Text(text)).await.expect("Failed to send text");
}
}
#[cfg(not(feature="DEBUG"))] {
while let Ok(Some(Message::Text(text))) = ws.recv().await {
ws.send(Message::Text(text)).await.expect("Failed to send text");
}
}
})
}

#[tokio::main]
async fn main() {
Ohkami::with(Logger, (
"/".Dir("./template").omit_extensions([".html"]),
"/echo".GET(echo_text),
)).howl("localhost:3030").await
}
36 changes: 36 additions & 0 deletions examples/websocket/template/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ohkami WebSocket Example</title>
</head>
<body>
<h1>Echo Text</h1>

<div>
<input id="text-input"/>
<button id="send-button">send</button>
</div>

<script>
const ws = new WebSocket("ws://localhost:3030/echo");
ws.addEventListener("message", (e) => {
console.log("ws got message: ", e.data);
});
ws.addEventListener("open", (e) => {
console.log(e);
ws.send("test");
});

const text_input = document.getElementById("text-input");
const send_button = document.getElementById("send-button");
send_button.addEventListener(
"click", (e) => {
console.log("sending: ", text_input.value);
ws.send(text_input.value);
}
);
</script>
</body>
</html>
8 changes: 4 additions & 4 deletions ohkami/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ license = "MIT"


[package.metadata.docs.rs]
features = ["rt_tokio", "nightly", "sse"]
features = ["rt_tokio", "nightly", "sse", "ws"]


[dependencies]
Expand All @@ -33,7 +33,7 @@ rustc-hash = { version = "2.0" }

hmac = { version = "0.12", default-features = false }
sha2 = { version = "0.10", default-features = false }
#sha1 = { version = "0.10", optional = true, default-features = false }
sha1 = { version = "0.10", optional = true, default-features = false }


[features]
Expand All @@ -45,8 +45,8 @@ rt_worker = ["dep:worker", "ohkami_macros/worker"]

nightly = []
testing = []
#websocket = ["dep:sha1"]
sse = ["ohkami_lib/stream"]
ws = ["dep:sha1"]

##### DEBUG #####
DEBUG = [
Expand All @@ -58,7 +58,7 @@ DEBUG = [
# "nightly",
# "testing",
# "sse",
# #"websocket",
# "ws",
# "rt_tokio",
# #"rt_async-std",
# #"rt_worker",
Expand Down
3 changes: 3 additions & 0 deletions ohkami/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ pub mod builtin;

pub mod typed;

#[cfg(all(feature="ws", any(feature="rt_tokio",feature="rt_async-std")))]
pub mod ws;

#[cfg(feature="testing")]
#[cfg(any(feature="rt_tokio",feature="rt_async-std",feature="rt_worker"))]
pub mod testing;
Expand Down
2 changes: 1 addition & 1 deletion ohkami/src/ohkami/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ trait RoutingItem {
Handlers::new(Box::leak({
let base_path = self.route.trim_end_matches('/').to_string();
match &*path.join("/") {
"" => base_path,
"" => if !base_path.is_empty() {base_path} else {"/".into()},
some => base_path + "/" + some,
}
}.into_boxed_str())).GET(handler)
Expand Down
9 changes: 9 additions & 0 deletions ohkami/src/response/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use ohkami_lib::CowSlice;
#[cfg(feature="sse")]
use ohkami_lib::Stream;

#[cfg(all(feature="ws", any(feature="rt_tokio",feature="rt_async-std")))]
use crate::ws::{Config, Handler};


pub enum Content {
None,
Expand All @@ -11,6 +14,9 @@ pub enum Content {

#[cfg(feature="sse")]
Stream(std::pin::Pin<Box<dyn Stream<Item = Result<String, String>> + Send>>),

#[cfg(all(feature="ws", any(feature="rt_tokio",feature="rt_async-std")))]
WebSocket((Config, Handler)),
} const _: () = {
impl Default for Content {
fn default() -> Self {
Expand Down Expand Up @@ -42,6 +48,9 @@ pub enum Content {

#[cfg(feature="sse")]
Self::Stream(_) => f.write_str("{stream}"),

#[cfg(feature="ws")]
Self::WebSocket(_) => f.write_str("{websocket}"),
}
}
}
Expand Down
Loading
Loading