async-graphql/integrations/actix-web/src/subscription.rs

136 lines
4.4 KiB
Rust
Raw Normal View History

2020-03-17 09:26:59 +00:00
use actix::{
2020-03-29 12:02:52 +00:00
Actor, ActorContext, ActorFuture, AsyncContext, ContextFutureSpawner, StreamHandler, WrapFuture,
2020-03-17 09:26:59 +00:00
};
use actix_web_actors::ws::{Message, ProtocolError, WebsocketContext};
2020-09-11 09:52:06 +00:00
use async_graphql::{Data, FieldResult, ObjectType, Schema, SubscriptionType};
2020-03-29 12:02:52 +00:00
use futures::channel::mpsc;
use futures::SinkExt;
2020-03-19 09:20:12 +00:00
use std::time::{Duration, Instant};
2020-03-17 09:26:59 +00:00
2020-04-07 06:30:46 +00:00
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
2020-04-14 01:53:17 +00:00
/// Actor for subscription via websocket
pub struct WSSubscription<Query, Mutation, Subscription> {
2020-03-29 12:02:52 +00:00
schema: Schema<Query, Mutation, Subscription>,
2020-03-17 09:26:59 +00:00
hb: Instant,
2020-09-02 06:27:04 +00:00
sink: Option<mpsc::UnboundedSender<Vec<u8>>>,
2020-09-11 09:52:06 +00:00
initializer: Option<Box<dyn Fn(serde_json::Value) -> FieldResult<Data> + Send + Sync>>,
2020-03-17 09:26:59 +00:00
}
2020-04-14 01:53:17 +00:00
impl<Query, Mutation, Subscription> WSSubscription<Query, Mutation, Subscription>
2020-03-17 09:26:59 +00:00
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Send + Sync + 'static,
Mutation: ObjectType + Send + Sync + 'static,
Subscription: SubscriptionType + Send + Sync + 'static,
2020-03-17 09:26:59 +00:00
{
2020-04-14 01:53:17 +00:00
/// Create an actor for subscription connection via websocket.
2020-04-23 07:46:37 +00:00
pub fn new(schema: &Schema<Query, Mutation, Subscription>) -> Self {
2020-03-17 09:26:59 +00:00
Self {
2020-04-14 01:53:17 +00:00
schema: schema.clone(),
2020-03-17 09:26:59 +00:00
hb: Instant::now(),
2020-03-29 12:02:52 +00:00
sink: None,
2020-09-11 09:52:06 +00:00
initializer: None,
2020-04-23 07:30:12 +00:00
}
}
/// Set a context data initialization function.
2020-09-11 09:52:06 +00:00
pub fn initializer<F>(self, f: F) -> Self
2020-04-23 07:30:12 +00:00
where
2020-04-23 10:11:03 +00:00
F: Fn(serde_json::Value) -> FieldResult<Data> + Send + Sync + 'static,
2020-04-23 07:30:12 +00:00
{
Self {
2020-09-11 09:52:06 +00:00
initializer: Some(Box::new(f)),
2020-04-23 07:30:12 +00:00
..self
2020-03-17 09:26:59 +00:00
}
}
2020-03-19 09:20:12 +00:00
fn hb(&self, ctx: &mut WebsocketContext<Self>) {
2020-04-07 06:30:46 +00:00
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
2020-03-19 09:20:12 +00:00
ctx.stop();
}
2020-04-07 06:30:46 +00:00
ctx.ping(b"");
2020-03-19 09:20:12 +00:00
});
}
2020-03-17 09:26:59 +00:00
}
2020-04-14 01:53:17 +00:00
impl<Query, Mutation, Subscription> Actor for WSSubscription<Query, Mutation, Subscription>
2020-03-17 09:26:59 +00:00
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Sync + Send + 'static,
Mutation: ObjectType + Sync + Send + 'static,
Subscription: SubscriptionType + Send + Sync + 'static,
2020-03-17 09:26:59 +00:00
{
type Context = WebsocketContext<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
2020-03-19 09:20:12 +00:00
self.hb(ctx);
2020-09-11 09:52:06 +00:00
if let Some(initializer) = self.initializer.take() {
let (sink, stream) = async_graphql::transports::websocket::create_with_initializer(
&self.schema,
initializer,
);
ctx.add_stream(stream);
self.sink = Some(sink);
} else {
let (sink, stream) = async_graphql::transports::websocket::create(&self.schema);
ctx.add_stream(stream);
self.sink = Some(sink);
};
2020-03-17 09:26:59 +00:00
}
}
impl<Query, Mutation, Subscription> StreamHandler<Result<Message, ProtocolError>>
2020-04-14 01:53:17 +00:00
for WSSubscription<Query, Mutation, Subscription>
2020-03-17 09:26:59 +00:00
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Sync + Send + 'static,
Mutation: ObjectType + Sync + Send + 'static,
Subscription: SubscriptionType + Send + Sync + 'static,
2020-03-17 09:26:59 +00:00
{
fn handle(&mut self, msg: Result<Message, ProtocolError>, ctx: &mut Self::Context) {
let msg = match msg {
Err(_) => {
ctx.stop();
return;
}
Ok(msg) => msg,
};
match msg {
Message::Ping(msg) => {
self.hb = Instant::now();
ctx.pong(&msg);
}
Message::Pong(_) => {
self.hb = Instant::now();
}
Message::Text(s) => {
2020-03-29 12:02:52 +00:00
if let Some(mut sink) = self.sink.clone() {
async move { sink.send(s.into()).await }
.into_actor(self)
.then(|_, actor, _| async {}.into_actor(actor))
.wait(ctx);
2020-03-17 09:26:59 +00:00
}
}
Message::Binary(_) | Message::Close(_) | Message::Continuation(_) => {
ctx.stop();
}
Message::Nop => {}
}
}
}
2020-09-02 06:27:04 +00:00
impl<Query, Mutation, Subscription> StreamHandler<Vec<u8>>
2020-04-14 01:53:17 +00:00
for WSSubscription<Query, Mutation, Subscription>
2020-03-17 09:26:59 +00:00
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Send + Sync + 'static,
Mutation: ObjectType + Send + Sync + 'static,
Subscription: SubscriptionType + Send + Sync + 'static,
2020-03-17 09:26:59 +00:00
{
2020-09-02 06:27:04 +00:00
fn handle(&mut self, data: Vec<u8>, ctx: &mut Self::Context) {
if let Ok(text) = String::from_utf8(data) {
2020-05-16 13:16:30 +00:00
ctx.text(text);
}
2020-03-17 09:26:59 +00:00
}
}