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