Skip to content
This repository was archived by the owner on Aug 30, 2025. It is now read-only.

Commit 092e1e9

Browse files
authored
Merge pull request #60 from polyphony-chat/gateway
Working heartbeats and bugfixes
2 parents 20a8711 + a919632 commit 092e1e9

File tree

9 files changed

+100
-112
lines changed

9 files changed

+100
-112
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ DATABASE_PORT=[Postgres port, usually 5432]
5050
DATABASE_USERNAME=[Your Postgres username]
5151
DATABASE_PASSWORD=[Your Postgres password]
5252
DATABASE_NAME=[Your Postgres database name]
53+
API_BIND=[ip:port to bind the HTTP API server to. Defaults to 0.0.0.0:3001 if not set]
54+
GATEWAY_BIND=[ip:port to bind the Gateway server to. Defaults to 0.0.0.0:3003 if not set]
5355
```
5456

5557
4. Install the sqlx CLI with `cargo install sqlx-cli`

src/api/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7+
static DEFAULT_API_BIND: &str = "0.0.0.0:3001";
8+
79
use poem::{
810
listener::TcpListener,
911
middleware::{NormalizePath, TrailingSlash},
@@ -86,7 +88,10 @@ pub async fn start_api(
8688
.with(NormalizePath::new(TrailingSlash::Trim))
8789
.catch_all_error(custom_error);
8890

89-
let bind = std::env::var("API_BIND").unwrap_or_else(|_| String::from("localhost:3001"));
91+
let bind = &std::env::var("API_BIND").unwrap_or_else(|_| {
92+
log::warn!(target: "symfonia::db", "You did not specify API_BIND environment variable. Defaulting to '{DEFAULT_API_BIND}'.");
93+
DEFAULT_API_BIND.to_string()
94+
});
9095
let bind_clone = bind.clone();
9196

9297
log::info!(target: "symfonia::api", "Starting HTTP Server");

src/database/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,25 @@ static DEFAULT_CONNECTION_PORT: u16 = 5432;
2222

2323
pub async fn establish_connection() -> Result<sqlx::PgPool, Error> {
2424
let db_url = std::env::var("DATABASE_HOST").unwrap_or_else(|_| {
25-
log::warn!(target: "symfonia::db", "You did not specify `DATABASE_HOST` environment variable, defaulting to '{DEFAULT_CONNECTION_HOST}'.");
25+
log::warn!(target: "symfonia::db", "You did not specify DATABASE_HOST environment variable, defaulting to '{DEFAULT_CONNECTION_HOST}'.");
2626
DEFAULT_CONNECTION_HOST.to_string()
2727
});
2828
let connect_options = PgConnectOptions::new()
2929
.host(&db_url)
3030
.port(std::env::var("DATABASE_PORT").unwrap_or_else(|_| {
31-
log::warn!(target: "symfonia::db", "You did not specify `DATABASE_PORT` environment variable. Defaulting to '{DEFAULT_CONNECTION_PORT}'.");
31+
log::warn!(target: "symfonia::db", "You did not specify DATABASE_PORT environment variable. Defaulting to '{DEFAULT_CONNECTION_PORT}'.");
3232
DEFAULT_CONNECTION_PORT.to_string()
3333
}).parse::<u16>().expect("DATABASE_PORT must be a valid 16 bit unsigned integer."))
3434
.username(&std::env::var("DATABASE_USERNAME").unwrap_or_else(|_| {
35-
log::warn!(target: "symfonia::db", "You did not specify `DATABASE_USERNAME` environment variable. Defaulting to '{DEFAULT_CONNECTION_USERNAME}'.");
35+
log::warn!(target: "symfonia::db", "You did not specify DATABASE_USERNAME environment variable. Defaulting to '{DEFAULT_CONNECTION_USERNAME}'.");
3636
DEFAULT_CONNECTION_USERNAME.to_string()
3737
}))
3838
.password(&std::env::var("DATABASE_PASSWORD").unwrap_or_else(|_| {
39-
log::warn!(target: "symfonia::db", "You did not specify `DATABASE_PASSWORD` environment variable. Defaulting to '{DEFAULT_CONNECTION_PASSWORD}'.");
39+
log::warn!(target: "symfonia::db", "You did not specify DATABASE_PASSWORD environment variable. Defaulting to '{DEFAULT_CONNECTION_PASSWORD}'.");
4040
DEFAULT_CONNECTION_PASSWORD.to_string()
4141
}))
4242
.database(&std::env::var("DATABASE_NAME").unwrap_or_else(|_| {
43-
log::warn!(target: "symfonia::db", "You did not specify `DATABASE_NAME` environment variable. Defaulting to '{DEFAULT_CONNECTION_NAME}'.");
43+
log::warn!(target: "symfonia::db", "You did not specify DATABASE_NAME environment variable. Defaulting to '{DEFAULT_CONNECTION_NAME}'.");
4444
DEFAULT_CONNECTION_NAME.to_string()
4545
}));
4646
let pool = PgPool::connect_with(connect_options).await?;

src/gateway/establish_connection.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ struct State {
4242
config: Config,
4343
connected_users: ConnectedUsers,
4444
sequence_number: Arc<Mutex<u64>>,
45-
kill_send: Sender<()>,
46-
kill_receive: tokio::sync::broadcast::Receiver<()>,
4745
/// Receiver for heartbeat messages. The `HeartbeatHandler` will receive messages from this channel.
4846
heartbeat_receive: tokio::sync::broadcast::Receiver<GatewayHeartbeat>,
4947
/// Sender for heartbeat messages. The main gateway task will send messages to this channel for the `HeartbeatHandler` to receive and handle.
@@ -100,8 +98,6 @@ pub(super) async fn establish_connection(
10098
config: config.clone(),
10199
connected_users: connected_users.clone(),
102100
sequence_number: sequence_number.clone(),
103-
kill_send: kill_send.clone(),
104-
kill_receive: kill_receive.resubscribe(),
105101
heartbeat_receive: message_receive.resubscribe(),
106102
heartbeat_send: message_send.clone(),
107103
session_id_send: session_id_send.clone(),
@@ -150,7 +146,11 @@ async fn finish_connecting(
150146
Ok(next) => next,
151147
Err(_) => {
152148
log::debug!(target: "symfonia::gateway::finish_connecting", "Encountered error when trying to receive message. Sending kill signal...");
153-
state.kill_send.send(()).expect("Failed to send kill_send");
149+
state
150+
.connection
151+
.kill_send
152+
.send(())
153+
.expect("Failed to send kill_send");
154154
return Err(GatewayError::Timeout.into());
155155
}
156156
};
@@ -172,8 +172,6 @@ async fn finish_connecting(
172172
heartbeat_handler_handle = Some(tokio::spawn({
173173
let mut heartbeat_handler = HeartbeatHandler::new(
174174
state.connection.clone(),
175-
state.kill_receive.resubscribe(),
176-
state.kill_send.clone(),
177175
state.heartbeat_receive.resubscribe(),
178176
state.sequence_number.clone(),
179177
state.session_id_receive.resubscribe(),
@@ -205,6 +203,7 @@ async fn finish_connecting(
205203
Err(_) => {
206204
log::trace!(target: "symfonia::gateway::establish_connection::finish_connecting", "Failed to verify token");
207205
state
206+
.connection
208207
.kill_send
209208
.send(())
210209
.expect("Failed to send kill signal");
@@ -217,8 +216,6 @@ async fn finish_connecting(
217216
let main_task_handle = tokio::spawn(gateway_task::gateway_task(
218217
state.connection.clone(),
219218
gateway_user.lock().await.inbox.resubscribe(),
220-
state.kill_receive.resubscribe(),
221-
state.kill_send.clone(),
222219
state.heartbeat_send.clone(),
223220
state.sequence_number.clone(),
224221
));
@@ -235,8 +232,6 @@ async fn finish_connecting(
235232
log::trace!(target: "symfonia::gateway::establish_connection::finish_connecting", "No heartbeat_handler yet. Creating one...");
236233
let mut heartbeat_handler = HeartbeatHandler::new(
237234
state.connection.clone(),
238-
state.kill_receive.resubscribe(),
239-
state.kill_send.clone(),
240235
state.heartbeat_receive.resubscribe(),
241236
state.sequence_number.clone(),
242237
state.session_id_receive.resubscribe(),
@@ -246,7 +241,6 @@ async fn finish_connecting(
246241
}
247242
}),
248243
},
249-
state.kill_send.clone(),
250244
&identify.event_data.token,
251245
state.sequence_number.clone(),
252246
)
@@ -256,6 +250,7 @@ async fn finish_connecting(
256250
Err(_) => {
257251
log::error!(target: "symfonia::gateway::establish_connection::finish_connecting", "Failed to send session_id to heartbeat handler");
258252
state
253+
.connection
259254
.kill_send
260255
.send(())
261256
.expect("Failed to send kill signal");
@@ -289,6 +284,7 @@ async fn finish_connecting(
289284
.into(),
290285
})))?;
291286
state
287+
.connection
292288
.kill_send
293289
.send(())
294290
.expect("Failed to send kill signal");

src/gateway/gateway_task.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,11 @@ use super::{Event, GatewayClient, GatewayPayload};
1818
pub(super) async fn gateway_task(
1919
mut connection: super::WebSocketConnection,
2020
mut inbox: tokio::sync::broadcast::Receiver<Event>,
21-
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
22-
mut kill_send: tokio::sync::broadcast::Sender<()>,
2321
mut heartbeat_send: tokio::sync::broadcast::Sender<GatewayHeartbeat>,
2422
last_sequence_number: Arc<Mutex<u64>>,
2523
) {
2624
log::trace!(target: "symfonia::gateway::gateway_task", "Started a new gateway task!");
27-
let inbox_processor = tokio::spawn(process_inbox(
28-
connection.clone(),
29-
inbox.resubscribe(),
30-
kill_receive.resubscribe(),
31-
));
25+
let inbox_processor = tokio::spawn(process_inbox(connection.clone(), inbox.resubscribe()));
3226

3327
/*
3428
Before we can respond to any gateway event we receive, we need to figure out what kind of event
@@ -39,18 +33,45 @@ pub(super) async fn gateway_task(
3933

4034
loop {
4135
tokio::select! {
42-
_ = kill_receive.recv() => {
36+
_ = connection.kill_receive.recv() => {
4337
return;
4438
},
4539
message_result = connection.receiver.recv() => {
4640
match message_result {
4741
Ok(message_of_unknown_type) => {
48-
let event = unwrap_event(Event::try_from(message_of_unknown_type), connection.clone(), kill_send.clone());
49-
// TODO: Handle event
42+
log::trace!(target: "symfonia::gateway::gateway_task", "Received raw message {:?}", message_of_unknown_type);
43+
let event = unwrap_event(Event::try_from(message_of_unknown_type), connection.clone(), connection.kill_send.clone());
44+
log::trace!(target: "symfonia::gateway::gateway_task", "Event type of received message: {:?}", event);
45+
match event {
46+
Event::Dispatch(_) => {
47+
// Receiving a dispatch event from a client is never correct
48+
log::debug!(target: "symfonia::gateway::gateway_task", "Received an unexpected message: {:?}", event);
49+
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4002), reason: "DECODE_ERROR".into() })));
50+
connection.kill_send.send(()).expect("Failed to send kill_send");
51+
panic!("Killing gateway task: Received an unexpected message");
52+
},
53+
Event::Heartbeat(hearbeat_event) => {
54+
match heartbeat_send.send(hearbeat_event) {
55+
Err(e) => {
56+
log::debug!(target: "symfonia::gateway::gateway_task", "Received Heartbeat but HeartbeatHandler seems to be dead?");
57+
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4002), reason: "DECODE_ERROR".into() })));
58+
connection.kill_send.send(()).expect("Failed to send kill_send");
59+
panic!("Killing gateway task: Received an unexpected message");
60+
},
61+
Ok(_) => {
62+
log::trace!(target: "symfonia::gateway::gateway_task", "Forwarded heartbeat message to HeartbeatHandler!");
63+
}
64+
}
65+
}
66+
_ => {
67+
log::error!(target: "symfonia::gateway::gateway_task", "Received an event type for which no code is yet implemented in the gateway_task. Please open a issue or PR at the symfonia repository. {:?}", event);
68+
}
69+
}
70+
5071
},
5172
Err(error) => {
5273
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4000), reason: "INTERNAL_SERVER_ERROR".into() })));
53-
kill_send.send(()).expect("Failed to send kill_send");
74+
connection.kill_send.send(()).expect("Failed to send kill_send");
5475
return;
5576
},
5677
}
@@ -81,26 +102,26 @@ fn unwrap_event(
81102
match e {
82103
Error::Gateway(g) => match g {
83104
GatewayError::UnexpectedOpcode(o) => {
84-
log::debug!(target: "symfonia::gateway::gateway_task", "Received an unexpected opcode: {:?}", o);
105+
log::debug!(target: "symfonia::gateway::gateway_task::unwrap_event", "Received an unexpected opcode: {:?}", o);
85106
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4001), reason: "UNKNOWN_OPCODE".into() })));
86107
kill_send.send(()).expect("Failed to send kill_send");
87108
panic!("Killing gateway task: Received an unexpected opcode");
88109
}
89110
GatewayError::UnexpectedMessage(m) => {
90-
log::debug!(target: "symfonia::gateway::gateway_task", "Received an unexpected message: {:?}", m);
111+
log::debug!(target: "symfonia::gateway::gateway_task::unwrap_event", "Received an unexpected message: {:?}", m);
91112
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4002), reason: "DECODE_ERROR".into() })));
92113
kill_send.send(()).expect("Failed to send kill_send");
93114
panic!("Killing gateway task: Received an unexpected message");
94115
}
95116
_ => {
96-
log::debug!(target: "symfonia::gateway::gateway_task", "Received an unexpected error: {:?}", g);
117+
log::debug!(target: "symfonia::gateway::gateway_task::unwrap_event", "Received an unexpected error: {:?}", g);
97118
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4000), reason: "INTERNAL_SERVER_ERROR".into() })));
98119
kill_send.send(()).expect("Failed to send kill_send");
99120
panic!("Killing gateway task: Received an unexpected error");
100121
}
101122
},
102123
_ => {
103-
log::debug!(target: "symfonia::gateway::gateway_task", "Received an unexpected error: {:?}", e);
124+
log::debug!(target: "symfonia::gateway::gateway_task::unwrap_event", "Received an unexpected error: {:?}", e);
104125
connection.sender.send(Message::Close(Some(CloseFrame { code: tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode::Library(4000), reason: "INTERNAL_SERVER_ERROR".into() })));
105126
kill_send.send(()).expect("Failed to send kill_send");
106127
panic!("Killing gateway task: Received an unexpected error");
@@ -112,13 +133,12 @@ fn unwrap_event(
112133
}
113134

114135
async fn process_inbox(
115-
connection: super::WebSocketConnection,
136+
mut connection: super::WebSocketConnection,
116137
mut inbox: tokio::sync::broadcast::Receiver<Event>,
117-
mut kill_receive: tokio::sync::broadcast::Receiver<()>,
118138
) {
119139
loop {
120140
tokio::select! {
121-
_ = kill_receive.recv() => {
141+
_ = connection.kill_receive.recv() => {
122142
return;
123143
}
124144
event = inbox.recv() => {

0 commit comments

Comments
 (0)