@@ -5,16 +5,9 @@ use futures_util::{
5
5
future:: { select, Either } ,
6
6
StreamExt as _,
7
7
} ;
8
- use tokio:: {
9
- pin,
10
- sync:: {
11
- mpsc:: { self , UnboundedSender } ,
12
- oneshot,
13
- } ,
14
- time:: interval,
15
- } ;
8
+ use tokio:: { pin, sync:: mpsc, time:: interval} ;
16
9
17
- use crate :: { Command , ConnId , RoomId } ;
10
+ use crate :: { ChatServerHandle , ConnId } ;
18
11
19
12
/// How often heartbeat pings are sent
20
13
const HEARTBEAT_INTERVAL : Duration = Duration :: from_secs ( 5 ) ;
@@ -25,27 +18,20 @@ const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
25
18
/// Echo text & binary messages received from the client, respond to ping messages, and monitor
26
19
/// connection health to detect network issues and free up resources.
27
20
pub async fn chat_ws (
28
- server_tx : UnboundedSender < Command > ,
21
+ chat_server : ChatServerHandle ,
29
22
mut session : actix_ws:: Session ,
30
23
mut msg_stream : actix_ws:: MessageStream ,
31
24
) {
32
25
log:: info!( "connected" ) ;
33
26
34
27
let mut name = None ;
35
- let mut room = "main" . to_owned ( ) ;
36
28
let mut last_heartbeat = Instant :: now ( ) ;
37
29
let mut interval = interval ( HEARTBEAT_INTERVAL ) ;
38
30
39
31
let ( conn_tx, mut conn_rx) = mpsc:: unbounded_channel ( ) ;
40
- let ( res_tx, res_rx) = oneshot:: channel ( ) ;
41
32
42
33
// unwrap: chat server is not dropped before the HTTP server
43
- server_tx
44
- . send ( Command :: Connect { conn_tx, res_tx } )
45
- . unwrap ( ) ;
46
-
47
- // unwrap: chat server does not drop our response channel
48
- let conn_id = res_rx. await . unwrap ( ) ;
34
+ let conn_id = chat_server. connect ( conn_tx) . await ;
49
35
50
36
let close_reason = loop {
51
37
// most of the futures we process need to be stack-pinned to work with select()
@@ -56,6 +42,7 @@ pub async fn chat_ws(
56
42
let msg_rx = conn_rx. recv ( ) ;
57
43
pin ! ( msg_rx) ;
58
44
45
+ // TODO: nested select is pretty gross for readability on the match
59
46
let messages = select ( msg_stream. next ( ) , msg_rx) ;
60
47
pin ! ( messages) ;
61
48
@@ -76,15 +63,8 @@ pub async fn chat_ws(
76
63
}
77
64
78
65
Message :: Text ( text) => {
79
- process_text_msg (
80
- & server_tx,
81
- & mut session,
82
- & text,
83
- conn_id,
84
- & mut room,
85
- & mut name,
86
- )
87
- . await ;
66
+ process_text_msg ( & chat_server, & mut session, & text, conn_id, & mut name)
67
+ . await ;
88
68
}
89
69
90
70
Message :: Binary ( _bin) => {
@@ -113,8 +93,10 @@ pub async fn chat_ws(
113
93
session. text ( chat_msg) . await . unwrap ( ) ;
114
94
}
115
95
116
- // all connection's msg senders were dropped
117
- Either :: Left ( ( Either :: Right ( ( None , _) ) , _) ) => unreachable ! ( ) ,
96
+ // all connection's message senders were dropped
97
+ Either :: Left ( ( Either :: Right ( ( None , _) ) , _) ) => unreachable ! (
98
+ "all connection message senders were dropped; chat server may have panicked"
99
+ ) ,
118
100
119
101
// heartbeat internal tick
120
102
Either :: Right ( ( _inst, _) ) => {
@@ -132,16 +114,17 @@ pub async fn chat_ws(
132
114
} ;
133
115
} ;
134
116
117
+ chat_server. disconnect ( conn_id) ;
118
+
135
119
// attempt to close connection gracefully
136
120
let _ = session. close ( close_reason) . await ;
137
121
}
138
122
139
123
async fn process_text_msg (
140
- server_tx : & UnboundedSender < Command > ,
124
+ chat_server : & ChatServerHandle ,
141
125
session : & mut actix_ws:: Session ,
142
126
text : & str ,
143
127
conn : ConnId ,
144
- room : & mut RoomId ,
145
128
name : & mut Option < String > ,
146
129
) {
147
130
// strip leading and trailing whitespace (spaces, newlines, etc.)
@@ -154,46 +137,32 @@ async fn process_text_msg(
154
137
// unwrap: we have guaranteed non-zero string length already
155
138
match cmd_args. next ( ) . unwrap ( ) {
156
139
"/list" => {
157
- // Send ListRooms message to chat server and wait for
158
- // response
159
- log:: info!( "List rooms" ) ;
140
+ log:: info!( "conn {conn}: listing rooms" ) ;
160
141
161
- let ( res_tx, res_rx) = oneshot:: channel ( ) ;
162
- server_tx. send ( Command :: List { res_tx } ) . unwrap ( ) ;
163
- // unwrap: chat server does not drop our response channel
164
- let rooms = res_rx. await . unwrap ( ) ;
142
+ let rooms = chat_server. list_rooms ( ) . await ;
165
143
166
144
for room in rooms {
167
145
session. text ( room) . await . unwrap ( ) ;
168
146
}
169
147
}
170
148
171
149
"/join" => match cmd_args. next ( ) {
172
- Some ( room_id ) => {
173
- * room = room_id . to_owned ( ) ;
150
+ Some ( room ) => {
151
+ log :: info! ( "conn {conn}: joining room {room}" ) ;
174
152
175
- let ( res_tx , res_rx ) = oneshot :: channel ( ) ;
153
+ chat_server . join_room ( conn , room ) . await ;
176
154
177
- server_tx
178
- . send ( Command :: Join {
179
- conn,
180
- room : room. clone ( ) ,
181
- res_tx,
182
- } )
183
- . unwrap ( ) ;
184
-
185
- // unwrap: chat server does not drop our response channel
186
- res_rx. await . unwrap ( ) ;
187
-
188
- session. text ( format ! ( "joined {room_id}" ) ) . await . unwrap ( ) ;
155
+ session. text ( format ! ( "joined {room}" ) ) . await . unwrap ( ) ;
189
156
}
157
+
190
158
None => {
191
159
session. text ( "!!! room name is required" ) . await . unwrap ( ) ;
192
160
}
193
161
} ,
194
162
195
163
"/name" => match cmd_args. next ( ) {
196
164
Some ( new_name) => {
165
+ log:: info!( "conn {conn}: setting name to: {new_name}" ) ;
197
166
name. replace ( new_name. to_owned ( ) ) ;
198
167
}
199
168
None => {
@@ -215,20 +184,6 @@ async fn process_text_msg(
215
184
None => msg. to_owned ( ) ,
216
185
} ;
217
186
218
- let ( res_tx, res_rx) = oneshot:: channel ( ) ;
219
-
220
- // send message to chat server
221
- server_tx
222
- . send ( Command :: Message {
223
- msg,
224
- room : room. clone ( ) ,
225
- skip : conn,
226
- res_tx,
227
- } )
228
- // unwrap: chat server is not dropped before the HTTP server
229
- . unwrap ( ) ;
230
-
231
- // unwrap: chat server does not drop our response channel
232
- res_rx. await . unwrap ( ) ;
187
+ chat_server. send_message ( conn, msg) . await
233
188
}
234
189
}
0 commit comments