Skip to main content

script/dom/
websocket.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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// Close codes defined in https://tools.ietf.org/html/rfc6455#section-7.4.1
65// Names are from https://github.com/mozilla/gecko-dev/blob/master/netwerk/protocol/websocket/nsIWebSocketChannel.idl
66#[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>, // Flag to tell if there is a running thread to clear buffered_amount
113    #[no_trace]
114    callback: LazyCallback<WebSocketDomAction>,
115    binary_type: Cell<BinaryType>,
116    protocol: DomRefCell<String>, // Subprotocol selected by server
117}
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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
153    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            // TODO(mrobinson): Should this task be cancellable?
177            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    /// <https://websockets.spec.whatwg.org/#make-disappear>
191    /// Returns true if any action was taken.
192    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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket>
201    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        // Step 1. Let baseURL be this's relevant settings object's API base URL.
209        let base_url = global.api_base_url();
210        // Step 2. Let urlRecord be the result of applying the URL parser to url with baseURL.
211        // Step 3. If urlRecord is failure, then throw a "SyntaxError" DOMException.
212        let mut url_record =
213            ServoUrl::parse_with_base(Some(&base_url), &url.str()).or(Err(Error::Syntax(None)))?;
214
215        // Step 4. If urlRecord’s scheme is "http", then set urlRecord’s scheme to "ws".
216        // Step 5. Otherwise, if urlRecord’s scheme is "https", set urlRecord’s scheme to "wss".
217        // Step 6. If urlRecord’s scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException.
218        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        // Step 7. If urlRecord’s fragment is non-null, then throw a "SyntaxError" DOMException.
236        if url_record.fragment().is_some() {
237            return Err(Error::Syntax(None));
238        }
239
240        // Step 8. If protocols is a string, set protocols to a sequence consisting of just that string.
241        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        // Step 9. If any of the values in protocols occur more than once or otherwise fail to match the requirements
249        // for elements that comprise the value of `Sec-WebSocket-Protocol` fields as defined by The WebSocket protocol,
250        // then throw a "SyntaxError" DOMException.
251        for (i, protocol) in protocols.iter().enumerate() {
252            // https://tools.ietf.org/html/rfc6455#section-4.1
253            // Handshake requirements, step 10
254
255            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            // https://tools.ietf.org/html/rfc6455#section-4.1
263            if !is_token(protocol.as_bytes()) {
264                return Err(Error::Syntax(None));
265            }
266        }
267
268        // Create the interface for communication with the resource thread
269        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        // Step 12. Establish a WebSocket connection given urlRecord, protocols, and client.
274        let ws = WebSocket::new(cx, global, proto, url_record.clone(), dom_action_sender);
275        let address = Trusted::new(&*ws);
276
277        // https://websockets.spec.whatwg.org/#concept-websocket-establish
278        //
279        // Let request be a new request, whose URL is requestURL, client is client, service-workers
280        // mode is "none", referrer is "no-referrer", mode is "websocket", credentials mode is
281        // "include", cache mode is "no-store" , and redirect mode is "error"
282        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    // https://html.spec.whatwg.org/multipage/#handler-websocket-onopen
343    event_handler!(open, GetOnopen, SetOnopen);
344
345    // https://html.spec.whatwg.org/multipage/#handler-websocket-onclose
346    event_handler!(close, GetOnclose, SetOnclose);
347
348    // https://html.spec.whatwg.org/multipage/#handler-websocket-onerror
349    event_handler!(error, GetOnerror, SetOnerror);
350
351    // https://html.spec.whatwg.org/multipage/#handler-websocket-onmessage
352    event_handler!(message, GetOnmessage, SetOnmessage);
353
354    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-url>
355    fn Url(&self) -> DOMString {
356        DOMString::from(self.url.as_str())
357    }
358
359    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-readystate>
360    fn ReadyState(&self) -> u16 {
361        self.ready_state.get() as u16
362    }
363
364    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount>
365    fn BufferedAmount(&self) -> u64 {
366        self.buffered_amount.get()
367    }
368
369    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-binarytype>
370    fn BinaryType(&self) -> BinaryType {
371        self.binary_type.get()
372    }
373
374    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-binarytype>
375    fn SetBinaryType(&self, btype: BinaryType) {
376        self.binary_type.set(btype)
377    }
378
379    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-protocol>
380    fn Protocol(&self) -> DOMString {
381        DOMString::from(self.protocol.borrow().clone())
382    }
383
384    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
385    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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
399    fn Send_(&self, blob: &Blob) -> ErrorResult {
400        /* As per https://html.spec.whatwg.org/multipage/#websocket
401           the buffered amount needs to be clamped to u32, even though Blob.Size() is u64
402           If the buffer limit is reached in the first place, there are likely other major problems
403        */
404        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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
418    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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-send>
432    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    /// <https://html.spec.whatwg.org/multipage/#dom-websocket-close>
446    fn Close(&self, code: Option<u16>, reason: Option<USVString>) -> ErrorResult {
447        if let Some(code) = code {
448            // Fail if the supplied code isn't normal and isn't reserved for libraries, frameworks, and applications
449            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            // reason cannot be larger than 123 bytes
457            return Err(Error::Syntax(Some("Reason too long".to_string())));
458        }
459
460        match self.ready_state.get() {
461            WebSocketRequestState::Closing | WebSocketRequestState::Closed => {}, // Do nothing
462            WebSocketRequestState::Connecting => {
463                // Connection is not yet established
464                /*By setting the state to closing, the open function
465                will abort connecting the websocket*/
466                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                // Kick off _Start the WebSocket Closing Handshake_
481                // https://tools.ietf.org/html/rfc6455#section-7.1.2
482                let reason = reason.map(|reason| reason.0);
483                let _ = self.callback.send(WebSocketDomAction::Close(code, reason));
484            },
485        }
486        Ok(()) // Return Ok
487    }
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
502/// Task queued when *the WebSocket connection is established*.
503/// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
504struct ConnectionEstablishedTask {
505    address: Trusted<WebSocket>,
506    protocol_in_use: Option<String>,
507}
508
509impl TaskOnce for ConnectionEstablishedTask {
510    /// <https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established>
511    fn run_once(self, cx: &mut JSContext) {
512        let ws = self.address.root();
513
514        // Step 1.
515        ws.ready_state.set(WebSocketRequestState::Open);
516
517        // Step 2: Extensions.
518        // TODO: Set extensions to extensions in use.
519
520        // Step 3.
521        if let Some(protocol_name) = self.protocol_in_use {
522            *ws.protocol.borrow_mut() = protocol_name;
523        };
524
525        // Step 4.
526        ws.upcast().fire_event(cx, atom!("open"));
527    }
528}
529
530struct BufferedAmountTask {
531    address: Trusted<WebSocket>,
532}
533
534impl TaskOnce for BufferedAmountTask {
535    // See https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount
536    //
537    // To be compliant with standards, we need to reset bufferedAmount only when the event loop
538    // reaches step 1.  In our implementation, the bytes will already have been sent on a background
539    // thread.
540    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            // Do nothing if already closed.
561            return;
562        }
563
564        // Perform _the WebSocket connection is closed_ steps.
565        // https://html.spec.whatwg.org/multipage/#closeWebSocket
566
567        // Step 1.
568        ws.ready_state.set(WebSocketRequestState::Closed);
569
570        // Step 2.
571        if self.failed {
572            ws.upcast().fire_event(cx, atom!("error"));
573        }
574
575        // Step 3.
576        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        // Step 1.
609        if ws.ready_state.get() != WebSocketRequestState::Open {
610            return;
611        }
612
613        // Step 2-5.
614        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}