1use std::cell::Cell;
6use std::ptr::{self, NonNull};
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use js::conversions::ToJSValConvertible;
12use js::jsapi::JSObject;
13use js::jsval::UndefinedValue;
14use js::realm::AutoRealm;
15use js::rust::{CustomAutoRooterGuard, HandleObject};
16use js::typedarray::{ArrayBuffer, ArrayBufferU8, ArrayBufferView};
17use net_traits::blob_url_store::UrlWithBlobClaim;
18use net_traits::request::{
19 CacheMode, CredentialsMode, RedirectMode, Referrer, RequestBuilder, RequestMode,
20 ServiceWorkersMode,
21};
22use net_traits::{
23 CoreResourceMsg, FetchChannels, MessageData, WebSocketDomAction, WebSocketNetworkEvent,
24};
25use profile_traits::generic_callback::GenericCallback as ProfileGenericCallback;
26use script_bindings::cell::DomRefCell;
27use script_bindings::reflector::{DomObject, reflect_weak_referenceable_dom_object_with_proto};
28use servo_base::generic_channel::{LazyCallback, lazy_callback};
29use servo_constellation_traits::BlobImpl;
30use servo_url::{ImmutableOrigin, ServoUrl};
31
32use crate::dom::bindings::buffer_source::create_buffer_source;
33use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
34use crate::dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods};
35use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
36use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence;
37use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
38use crate::dom::bindings::inheritance::Castable;
39use crate::dom::bindings::refcounted::Trusted;
40use crate::dom::bindings::reflector::DomGlobal;
41use crate::dom::bindings::root::DomRoot;
42use crate::dom::bindings::str::{DOMString, USVString, is_token};
43use crate::dom::blob::Blob;
44use crate::dom::closeevent::CloseEvent;
45use crate::dom::csp::{GlobalCspReporting, Violation};
46use crate::dom::event::{Event, EventBubbles, EventCancelable};
47use crate::dom::eventtarget::EventTarget;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::messageevent::MessageEvent;
50use crate::dom::window::Window;
51use crate::fetch::fetch::RequestWithGlobalScope;
52use crate::tasks::task::TaskOnce;
53use crate::tasks::task_source::SendableTaskSource;
54
55#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
56enum WebSocketRequestState {
57 Connecting = 0,
58 Open = 1,
59 Closing = 2,
60 Closed = 3,
61}
62
63#[expect(dead_code)]
66mod close_code {
67 pub(crate) const NORMAL: u16 = 1000;
68 pub(crate) const GOING_AWAY: u16 = 1001;
69 pub(crate) const PROTOCOL_ERROR: u16 = 1002;
70 pub(crate) const UNSUPPORTED_DATATYPE: u16 = 1003;
71 pub(crate) const NO_STATUS: u16 = 1005;
72 pub(crate) const ABNORMAL: u16 = 1006;
73 pub(crate) const INVALID_PAYLOAD: u16 = 1007;
74 pub(crate) const POLICY_VIOLATION: u16 = 1008;
75 pub(crate) const TOO_LARGE: u16 = 1009;
76 pub(crate) const EXTENSION_MISSING: u16 = 1010;
77 pub(crate) const INTERNAL_ERROR: u16 = 1011;
78 pub(crate) const TLS_FAILED: u16 = 1015;
79}
80
81fn close_the_websocket_connection(
82 address: Trusted<WebSocket>,
83 task_source: &SendableTaskSource,
84 code: Option<u16>,
85 reason: String,
86) {
87 task_source.queue(CloseTask {
88 address,
89 failed: false,
90 code,
91 reason: Some(reason),
92 });
93}
94
95fn fail_the_websocket_connection(address: Trusted<WebSocket>, task_source: &SendableTaskSource) {
96 task_source.queue(CloseTask {
97 address,
98 failed: true,
99 code: Some(close_code::ABNORMAL),
100 reason: None,
101 });
102}
103
104#[dom_struct]
105pub(crate) struct WebSocket {
106 eventtarget: EventTarget,
107 #[no_trace]
108 url: ServoUrl,
109 ready_state: Cell<WebSocketRequestState>,
110 buffered_amount: Cell<u64>,
111 clearing_buffer: Cell<bool>, #[no_trace]
113 callback: LazyCallback<WebSocketDomAction>,
114 binary_type: Cell<BinaryType>,
115 protocol: DomRefCell<String>, }
117
118impl WebSocket {
119 fn new_inherited(url: ServoUrl, callback: LazyCallback<WebSocketDomAction>) -> WebSocket {
120 WebSocket {
121 eventtarget: EventTarget::new_inherited(),
122 url,
123 ready_state: Cell::new(WebSocketRequestState::Connecting),
124 buffered_amount: Cell::new(0),
125 clearing_buffer: Cell::new(false),
126 callback,
127 binary_type: Cell::new(BinaryType::Blob),
128 protocol: Default::default(),
129 }
130 }
131
132 fn new(
133 cx: &mut JSContext,
134 global: &GlobalScope,
135 proto: Option<HandleObject>,
136 url: ServoUrl,
137 sender: LazyCallback<WebSocketDomAction>,
138 ) -> DomRoot<WebSocket> {
139 let websocket = reflect_weak_referenceable_dom_object_with_proto(
140 cx,
141 Rc::new(WebSocket::new_inherited(url, sender)),
142 global,
143 proto,
144 );
145 if let Some(window) = global.downcast::<Window>() {
146 window.Document().track_websocket(&websocket);
147 }
148 websocket
149 }
150
151 fn send_impl(&self, data_byte_len: u64) -> Fallible<bool> {
153 let return_after_buffer = match self.ready_state.get() {
154 WebSocketRequestState::Connecting => {
155 return Err(Error::InvalidState(Some(
156 "Cannot send while socket is connecting".into(),
157 )));
158 },
159 WebSocketRequestState::Open => false,
160 WebSocketRequestState::Closing | WebSocketRequestState::Closed => true,
161 };
162
163 let address = Trusted::new(self);
164
165 match data_byte_len.checked_add(self.buffered_amount.get()) {
166 None => panic!(),
167 Some(new_amount) => self.buffered_amount.set(new_amount),
168 };
169
170 if return_after_buffer {
171 return Ok(false);
172 }
173
174 if !self.clearing_buffer.get() && self.ready_state.get() == WebSocketRequestState::Open {
175 self.clearing_buffer.set(true);
176
177 self.global()
179 .task_manager()
180 .websocket_task_source()
181 .queue_unconditionally(BufferedAmountTask { address });
182 }
183
184 Ok(true)
185 }
186
187 pub(crate) fn origin(&self) -> ImmutableOrigin {
188 self.url.origin()
189 }
190
191 pub(crate) fn make_disappear(&self) -> bool {
194 let result = self.ready_state.get() != WebSocketRequestState::Closed;
195 let _ = self.Close(Some(1001), None);
196 result
197 }
198}
199
200impl WebSocketMethods<crate::DomTypeHolder> for WebSocket {
201 fn Constructor(
203 cx: &mut JSContext,
204 global: &GlobalScope,
205 proto: Option<HandleObject>,
206 url: DOMString,
207 protocols: Option<StringOrStringSequence>,
208 ) -> Fallible<DomRoot<WebSocket>> {
209 let base_url = global.api_base_url();
211 let mut url_record = ServoUrl::parse_with_base(Some(&base_url), &url.str())
214 .or(Err(Error::Syntax(Some("Failed to parse url".into()))))?;
215
216 match url_record.scheme() {
220 "http" => {
221 url_record
222 .as_mut_url()
223 .set_scheme("ws")
224 .expect("Can't set scheme from http to ws");
225 },
226 "https" => {
227 url_record
228 .as_mut_url()
229 .set_scheme("wss")
230 .expect("Can't set scheme from https to wss");
231 },
232 "ws" | "wss" => {},
233 _ => {
234 return Err(Error::Syntax(Some("Forbidden URL scheme".into())));
235 },
236 }
237
238 if url_record.fragment().is_some() {
240 return Err(Error::Syntax(Some("Forbidden URL fragment".into())));
241 }
242
243 let protocols = protocols.map_or(vec![], |p| match p {
245 StringOrStringSequence::String(string) => vec![string.into()],
246 StringOrStringSequence::StringSequence(seq) => {
247 seq.into_iter().map(String::from).collect()
248 },
249 });
250
251 for (i, protocol) in protocols.iter().enumerate() {
255 if protocols[i + 1..]
259 .iter()
260 .any(|p| p.eq_ignore_ascii_case(protocol))
261 {
262 return Err(Error::Syntax(Some("Duplicate protocol header".into())));
263 }
264
265 if !is_token(protocol.as_bytes()) {
267 return Err(Error::Syntax(Some(
268 "Protocol header field is not a valid token".into(),
269 )));
270 }
271 }
272
273 let (dom_action_sender, resource_action_receiver) = lazy_callback();
275
276 let ws = WebSocket::new(cx, global, proto, url_record.clone(), dom_action_sender);
278 let address = Trusted::new(&*ws);
279
280 let request = RequestBuilder::new(
286 global.webview_id(),
287 UrlWithBlobClaim::from_url_without_having_claimed_blob(url_record.clone()),
288 Referrer::NoReferrer,
289 )
290 .with_global_scope(global)
291 .mode(RequestMode::WebSocket {
292 protocols,
293 original_url: url_record,
294 })
295 .service_workers_mode(ServiceWorkersMode::None)
296 .credentials_mode(CredentialsMode::Include)
297 .cache_mode(CacheMode::NoCache)
298 .redirect_mode(RedirectMode::Error);
299
300 let task_source = global.task_manager().websocket_task_source().to_sendable();
301 let resource_event_sender =
302 ProfileGenericCallback::new(move |message| match message.unwrap() {
303 WebSocketNetworkEvent::ReportCSPViolations(violations) => {
304 let task = ReportCSPViolationTask {
305 websocket: address.clone(),
306 violations,
307 };
308 task_source.queue(task);
309 },
310 WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use } => {
311 let open_thread = ConnectionEstablishedTask {
312 address: address.clone(),
313 protocol_in_use,
314 };
315 task_source.queue(open_thread);
316 },
317 WebSocketNetworkEvent::MessageReceived(message) => {
318 let message_thread = MessageReceivedTask {
319 address: address.clone(),
320 message,
321 };
322 task_source.queue(message_thread);
323 },
324 WebSocketNetworkEvent::Fail => {
325 fail_the_websocket_connection(address.clone(), &task_source);
326 },
327 WebSocketNetworkEvent::Close(code, reason) => {
328 close_the_websocket_connection(address.clone(), &task_source, code, reason);
329 },
330 })
331 .expect("Couldn't create web socket callback.");
332
333 let channels = FetchChannels::WebSocket {
334 event_sender: resource_event_sender,
335 action_receiver: resource_action_receiver,
336 };
337 let _ = global
338 .core_resource_thread()
339 .send(CoreResourceMsg::Fetch(request, channels));
340
341 Ok(ws)
342 }
343
344 event_handler!(open, GetOnopen, SetOnopen);
346
347 event_handler!(close, GetOnclose, SetOnclose);
349
350 event_handler!(error, GetOnerror, SetOnerror);
352
353 event_handler!(message, GetOnmessage, SetOnmessage);
355
356 fn Url(&self) -> DOMString {
358 DOMString::from(self.url.as_str())
359 }
360
361 fn ReadyState(&self) -> u16 {
363 self.ready_state.get() as u16
364 }
365
366 fn BufferedAmount(&self) -> u64 {
368 self.buffered_amount.get()
369 }
370
371 fn BinaryType(&self) -> BinaryType {
373 self.binary_type.get()
374 }
375
376 fn SetBinaryType(&self, btype: BinaryType) {
378 self.binary_type.set(btype)
379 }
380
381 fn Protocol(&self) -> DOMString {
383 DOMString::from(self.protocol.borrow().clone())
384 }
385
386 fn Send(&self, data: USVString) -> ErrorResult {
388 let data_byte_len = data.0.len() as u64;
389 let send_data = self.send_impl(data_byte_len)?;
390
391 if send_data {
392 let _ = self
393 .callback
394 .send(WebSocketDomAction::SendMessage(MessageData::Text(data.0)));
395 }
396
397 Ok(())
398 }
399
400 fn Send_(&self, blob: &Blob) -> ErrorResult {
402 let data_byte_len = blob.Size();
407 let send_data = self.send_impl(data_byte_len)?;
408
409 if send_data {
410 let bytes = blob.get_bytes().unwrap_or_default();
411 let _ = self
412 .callback
413 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
414 }
415
416 Ok(())
417 }
418
419 fn Send__(&self, array: CustomAutoRooterGuard<ArrayBuffer>) -> ErrorResult {
421 let bytes = array.to_vec().unwrap_or_default();
422 let data_byte_len = bytes.len();
423 let send_data = self.send_impl(data_byte_len as u64)?;
424
425 if send_data {
426 let _ = self
427 .callback
428 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
429 }
430 Ok(())
431 }
432
433 fn Send___(&self, array: CustomAutoRooterGuard<ArrayBufferView>) -> ErrorResult {
435 let bytes = array.to_vec().unwrap_or_default();
436 let data_byte_len = bytes.len();
437 let send_data = self.send_impl(data_byte_len as u64)?;
438
439 if send_data {
440 let _ = self
441 .callback
442 .send(WebSocketDomAction::SendMessage(MessageData::Binary(bytes)));
443 }
444 Ok(())
445 }
446
447 fn Close(&self, code: Option<u16>, reason: Option<USVString>) -> ErrorResult {
449 if let Some(code) = code &&
451 code != close_code::NORMAL &&
452 !(3000..=4999).contains(&code)
453 {
454 return Err(Error::InvalidAccess(Some(
455 "Invalid WebSocket connection close code".into(),
456 )));
457 }
458
459 if let Some(ref reason) = reason &&
461 reason.0.len() > 123
462 {
463 return Err(Error::Syntax(Some("Reason too long".to_string())));
464 }
465
466 match self.ready_state.get() {
468 WebSocketRequestState::Closing | WebSocketRequestState::Closed => {}, WebSocketRequestState::Connecting => {
470 self.ready_state.set(WebSocketRequestState::Closing);
472
473 fail_the_websocket_connection(
474 Trusted::new(self),
475 &self
476 .global()
477 .task_manager()
478 .websocket_task_source()
479 .to_sendable(),
480 );
481 },
482 WebSocketRequestState::Open => {
483 self.ready_state.set(WebSocketRequestState::Closing);
485
486 let reason = reason.map(|reason| reason.0);
489 let _ = self.callback.send(WebSocketDomAction::Close(code, reason));
490 },
491 }
492 Ok(()) }
494}
495
496struct ReportCSPViolationTask {
497 websocket: Trusted<WebSocket>,
498 violations: Vec<Violation>,
499}
500
501impl TaskOnce for ReportCSPViolationTask {
502 fn run_once(self, cx: &mut JSContext) {
503 let global = self.websocket.root().global();
504 global.report_csp_violations(cx, self.violations, None, None);
505 }
506}
507
508struct ConnectionEstablishedTask {
511 address: Trusted<WebSocket>,
512 protocol_in_use: Option<String>,
513}
514
515impl TaskOnce for ConnectionEstablishedTask {
516 fn run_once(self, cx: &mut JSContext) {
518 let ws = self.address.root();
519
520 ws.ready_state.set(WebSocketRequestState::Open);
522
523 if let Some(protocol_name) = self.protocol_in_use {
528 *ws.protocol.borrow_mut() = protocol_name;
529 };
530
531 ws.upcast().fire_event(cx, atom!("open"));
533 }
534}
535
536struct BufferedAmountTask {
537 address: Trusted<WebSocket>,
538}
539
540impl TaskOnce for BufferedAmountTask {
541 fn run_once(self, _cx: &mut JSContext) {
547 let ws = self.address.root();
548
549 ws.buffered_amount.set(0);
550 ws.clearing_buffer.set(false);
551 }
552}
553
554struct CloseTask {
555 address: Trusted<WebSocket>,
556 failed: bool,
557 code: Option<u16>,
558 reason: Option<String>,
559}
560
561impl TaskOnce for CloseTask {
562 fn run_once(self, cx: &mut JSContext) {
563 let ws = self.address.root();
564
565 if ws.ready_state.get() == WebSocketRequestState::Closed {
566 return;
568 }
569
570 ws.ready_state.set(WebSocketRequestState::Closed);
575
576 if self.failed {
578 ws.upcast().fire_event(cx, atom!("error"));
579 }
580
581 let clean_close = !self.failed;
583 let code = self.code.unwrap_or(close_code::NO_STATUS);
584 let reason = DOMString::from(self.reason.unwrap_or_default());
585 let close_event = CloseEvent::new(
586 cx,
587 &ws.global(),
588 atom!("close"),
589 EventBubbles::DoesNotBubble,
590 EventCancelable::NotCancelable,
591 clean_close,
592 code,
593 reason,
594 );
595 close_event.upcast::<Event>().fire(cx, ws.upcast());
596 }
597}
598
599struct MessageReceivedTask {
600 address: Trusted<WebSocket>,
601 message: MessageData,
602}
603
604impl TaskOnce for MessageReceivedTask {
605 fn run_once(self, cx: &mut JSContext) {
606 let ws = self.address.root();
607 debug!(
608 "MessageReceivedTask::handler({:p}): readyState={:?}",
609 &*ws,
610 ws.ready_state.get()
611 );
612
613 if ws.ready_state.get() != WebSocketRequestState::Open {
615 return;
616 }
617
618 let global = ws.global();
620 let mut realm = AutoRealm::new(
621 cx,
622 NonNull::new(ws.reflector().get_jsobject().get()).unwrap(),
623 );
624 let cx = &mut *realm;
625 rooted!(&in(cx) let mut message = UndefinedValue());
626 match self.message {
627 MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()),
628 MessageData::Binary(data) => match ws.binary_type.get() {
629 BinaryType::Blob => {
630 let blob =
631 Blob::new(cx, &global, BlobImpl::new_from_bytes(data, String::new()));
632 blob.to_jsval(cx, message.handle_mut());
633 },
634 BinaryType::Arraybuffer => {
635 rooted!(&in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
636 assert!(
637 create_buffer_source::<ArrayBufferU8>(cx, &data, array_buffer.handle_mut())
638 .is_ok()
639 );
640
641 (*array_buffer).to_jsval(cx, message.handle_mut());
642 },
643 },
644 }
645 MessageEvent::dispatch_jsval(
646 cx,
647 ws.upcast(),
648 &global,
649 message.handle(),
650 Some(ws.origin().ascii_serialization().as_ref()),
651 None,
652 vec![],
653 );
654 }
655}