1use std::cell::Cell;
6use std::mem;
7use std::rc::Rc;
8use std::str::{Chars, FromStr};
9use std::time::Duration;
10
11use bytes::Bytes;
12use dom_struct::dom_struct;
13use encoding_rs::{Decoder, UTF_8};
14use headers::ContentType;
15use http::StatusCode;
16use http::header::{self, HeaderName, HeaderValue};
17use js::context::JSContext;
18use js::conversions::ToJSValConvertible;
19use js::jsval::UndefinedValue;
20use js::rust::HandleObject;
21use mime::{self, Mime};
22use net_traits::fetch::fetch_controller::FetchController;
23use net_traits::request::{CacheMode, CorsSettings, Destination, RequestBuilder, RequestId};
24use net_traits::{FetchMetadata, Metadata, NetworkError, ResourceFetchTiming};
25use script_bindings::cell::DomRefCell;
26use script_bindings::reflector::reflect_weak_referenceable_dom_object_with_proto;
27use servo_url::ServoUrl;
28use stylo_atoms::Atom;
29
30use crate::dom::bindings::codegen::Bindings::EventSourceBinding::{
31 EventSourceInit, EventSourceMethods,
32};
33use crate::dom::bindings::error::{Error, Fallible};
34use crate::dom::bindings::inheritance::Castable;
35use crate::dom::bindings::refcounted::Trusted;
36use crate::dom::bindings::reflector::DomGlobal;
37use crate::dom::bindings::root::DomRoot;
38use crate::dom::bindings::str::DOMString;
39use crate::dom::csp::{GlobalCspReporting, Violation};
40use crate::dom::event::Event;
41use crate::dom::eventtarget::EventTarget;
42use crate::dom::globalscope::GlobalScope;
43use crate::dom::messageevent::MessageEvent;
44use crate::dom::performance::performanceresourcetiming::InitiatorType;
45use crate::event_loop::timers::OneshotTimerCallback;
46use crate::fetch::fetch::{RequestWithGlobalScope, create_a_potential_cors_request};
47use crate::fetch::network_listener::{self, FetchResponseListener, ResourceTimingListener};
48use crate::realms::enter_auto_realm;
49
50const DEFAULT_RECONNECTION_TIME: Duration = Duration::from_millis(5000);
51
52#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
53struct GenerationId(u32);
54
55#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
56enum ReadyState {
58 Connecting = 0,
59 Open = 1,
60 Closed = 2,
61}
62
63#[derive(JSTraceable, MallocSizeOf)]
64struct DroppableEventSource {
65 #[no_trace]
66 controller: DomRefCell<FetchController>,
67}
68
69impl DroppableEventSource {
70 pub(crate) fn new(controller: DomRefCell<FetchController>) -> Self {
71 DroppableEventSource { controller }
72 }
73
74 pub(crate) fn cancel(&self) {
75 self.controller.borrow_mut().abort();
76 }
77
78 pub(crate) fn set_controller(&self, controller: FetchController) {
79 *self.controller.borrow_mut() = controller;
80 }
81}
82
83impl Drop for DroppableEventSource {
85 fn drop(&mut self) {
86 self.cancel();
89 }
90}
91
92#[dom_struct]
93pub(crate) struct EventSource {
94 eventtarget: EventTarget,
95 #[no_trace]
96 url: ServoUrl,
97 #[no_trace]
98 request: DomRefCell<Option<RequestBuilder>>,
99 last_event_id: DomRefCell<DOMString>,
100 reconnection_time: Cell<Duration>,
101 generation_id: Cell<GenerationId>,
102
103 ready_state: Cell<ReadyState>,
104 with_credentials: bool,
105 droppable: DroppableEventSource,
106}
107
108#[derive(Clone, MallocSizeOf)]
109enum ParserState {
110 Field,
111 Comment,
112 Value,
113 Eol,
114}
115
116#[derive(MallocSizeOf)]
117struct EventSourceContext {
118 decoder: Decoder,
119 event_source: Trusted<EventSource>,
120 gen_id: GenerationId,
121 parser_state: ParserState,
122 field: String,
123 value: String,
124 origin: String,
125 event_type: String,
126 data: String,
127 last_event_id: String,
128}
129
130impl Clone for EventSourceContext {
131 fn clone(&self) -> Self {
132 EventSourceContext {
133 decoder: UTF_8.new_decoder_with_bom_removal(),
134 event_source: self.event_source.clone(),
135 gen_id: self.gen_id,
136 parser_state: self.parser_state.clone(),
137 field: self.field.clone(),
138 value: self.value.clone(),
139 origin: self.origin.clone(),
140 event_type: self.event_type.clone(),
141 data: self.data.clone(),
142 last_event_id: self.last_event_id.clone(),
143 }
144 }
145}
146
147impl EventSourceContext {
148 fn announce_the_connection(&self) {
150 let event_source = self.event_source.root();
151 if self.gen_id != event_source.generation_id.get() {
152 return;
153 }
154 let global = event_source.global();
155 let event_source = self.event_source.clone();
156 global.task_manager().remote_event_task_source().queue(
157 task!(announce_the_event_source_connection: move |cx| {
158 let event_source = event_source.root();
159 if event_source.ready_state.get() != ReadyState::Closed {
160 event_source.ready_state.set(ReadyState::Open);
161 event_source.upcast::<EventTarget>().fire_event(cx, atom!("open"));
162 }
163 }),
164 );
165 }
166
167 fn fail_the_connection(&self) {
169 let event_source = self.event_source.root();
170 if self.gen_id != event_source.generation_id.get() {
171 return;
172 }
173 event_source.fail_the_connection();
174 }
175
176 fn reestablish_the_connection(&self) {
178 let event_source = self.event_source.root();
179
180 if self.gen_id != event_source.generation_id.get() {
181 return;
182 }
183
184 let trusted_event_source = self.event_source.clone();
185 let global = event_source.global();
186 let event_source_context = EventSourceContext {
187 decoder: UTF_8.new_decoder_with_bom_removal(),
188 event_source: self.event_source.clone(),
189 gen_id: self.gen_id,
190 parser_state: ParserState::Eol,
191 field: String::new(),
192 value: String::new(),
193 origin: self.origin.clone(),
194 event_type: String::new(),
195 data: String::new(),
196 last_event_id: String::from(event_source.last_event_id.borrow().clone()),
197 };
198 global.task_manager().remote_event_task_source().queue(
199 task!(reestablish_the_event_source_onnection: move |cx| {
200 let event_source = trusted_event_source.root();
201
202 if event_source.ready_state.get() == ReadyState::Closed {
204 return;
205 }
206
207 event_source.ready_state.set(ReadyState::Connecting);
209
210 event_source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
212
213 let duration = event_source.reconnection_time.get();
215
216 let callback = OneshotTimerCallback::EventSourceTimeout(
221 EventSourceTimeoutCallback {
222 event_source: trusted_event_source,
223 event_source_context,
224 }
225 );
226 event_source.global().schedule_callback(callback, duration);
227 }),
228 );
229 }
230
231 fn process_field(&mut self) {
233 match &*self.field {
234 "event" => mem::swap(&mut self.event_type, &mut self.value),
235 "data" => {
236 self.data.push_str(&self.value);
237 self.data.push('\n');
238 },
239 "id" if !self.value.contains('\0') => {
240 mem::swap(&mut self.last_event_id, &mut self.value);
241 },
242 "retry" => {
243 if let Ok(time) = u64::from_str(&self.value) {
244 self.event_source
245 .root()
246 .reconnection_time
247 .set(Duration::from_millis(time));
248 }
249 },
250 _ => (),
251 }
252
253 self.field.clear();
254 self.value.clear();
255 }
256
257 fn dispatch_event(&mut self, cx: &mut JSContext) {
259 let event_source = self.event_source.root();
260 *event_source.last_event_id.safe_borrow_mut(cx.no_gc()) =
262 DOMString::from(self.last_event_id.clone());
263 if self.data.is_empty() {
265 self.data.clear();
266 self.event_type.clear();
267 return;
268 }
269 if let Some(last) = self.data.pop() &&
271 last != '\n'
272 {
273 self.data.push(last);
274 }
275 let type_ = if !self.event_type.is_empty() {
277 Atom::from(self.event_type.clone())
278 } else {
279 atom!("message")
280 };
281 let event = {
283 let mut realm = enter_auto_realm(cx, &*event_source);
284 let cx = &mut realm.current_realm();
285 rooted!(&in(cx) let mut data = UndefinedValue());
286 self.data.to_jsval(cx, data.handle_mut());
287 MessageEvent::new(
288 cx,
289 &event_source.global(),
290 type_,
291 false,
292 false,
293 data.handle(),
294 DOMString::from(self.origin.clone()),
295 None,
296 event_source.last_event_id.borrow().clone(),
297 Vec::with_capacity(0),
298 )
299 };
300 self.event_type.clear();
302 self.data.clear();
303
304 let global = event_source.global();
306 let event_source = self.event_source.clone();
307 let event = Trusted::new(&*event);
308 global.task_manager().remote_event_task_source().queue(
309 task!(dispatch_the_event_source_event: move |cx| {
310 let event_source = event_source.root();
311 if event_source.ready_state.get() != ReadyState::Closed {
312 event.root().upcast::<Event>().fire(cx, event_source.upcast());
313 }
314 }),
315 );
316 }
317
318 fn parse(&mut self, cx: &mut JSContext, stream: Chars) {
320 let mut stream = stream.peekable();
321
322 while let Some(ch) = stream.next() {
323 match (ch, &self.parser_state) {
324 (':', &ParserState::Eol) => self.parser_state = ParserState::Comment,
325 (':', &ParserState::Field) => {
326 self.parser_state = ParserState::Value;
327 if let Some(&' ') = stream.peek() {
328 stream.next();
329 }
330 },
331
332 ('\n', &ParserState::Value) => {
333 self.parser_state = ParserState::Eol;
334 self.process_field();
335 },
336 ('\r', &ParserState::Value) => {
337 if let Some(&'\n') = stream.peek() {
338 continue;
339 }
340 self.parser_state = ParserState::Eol;
341 self.process_field();
342 },
343
344 ('\n', &ParserState::Field) => {
345 self.parser_state = ParserState::Eol;
346 self.process_field();
347 },
348 ('\r', &ParserState::Field) => {
349 if let Some(&'\n') = stream.peek() {
350 continue;
351 }
352 self.parser_state = ParserState::Eol;
353 self.process_field();
354 },
355
356 ('\n', &ParserState::Eol) => self.dispatch_event(cx),
357 ('\r', &ParserState::Eol) => {
358 if let Some(&'\n') = stream.peek() {
359 continue;
360 }
361 self.dispatch_event(cx);
362 },
363
364 ('\n', &ParserState::Comment) => self.parser_state = ParserState::Eol,
365 ('\r', &ParserState::Comment) => {
366 if let Some(&'\n') = stream.peek() {
367 continue;
368 }
369 self.parser_state = ParserState::Eol;
370 },
371
372 (_, &ParserState::Field) => self.field.push(ch),
373 (_, &ParserState::Value) => self.value.push(ch),
374 (_, &ParserState::Eol) => {
375 self.parser_state = ParserState::Field;
376 self.field.push(ch);
377 },
378 (_, &ParserState::Comment) => (),
379 }
380 }
381 }
382}
383
384impl FetchResponseListener for EventSourceContext {
385 fn should_invoke(&self) -> bool {
386 self.event_source.root().generation_id.get() == self.gen_id
387 }
388
389 fn process_request_body(&mut self, _: RequestId) {
390 }
392
393 fn process_response(
394 &mut self,
395 _: &mut JSContext,
396 _: RequestId,
397 metadata: Result<FetchMetadata, NetworkError>,
398 ) {
399 match metadata {
400 Ok(fetch_metadata) => {
401 if fetch_metadata.is_cors_cross_origin() {
402 return self.fail_the_connection();
403 }
404 let metadata: Metadata = fetch_metadata.into();
405 if metadata.status.code() != StatusCode::OK {
408 return self.fail_the_connection();
409 }
410 let mime = match metadata.content_type {
411 None => return self.fail_the_connection(),
412 Some(ct) => <ContentType as Into<Mime>>::into(ct.into_inner()),
413 };
414 if (mime.type_(), mime.subtype()) != (mime::TEXT, mime::EVENT_STREAM) {
415 return self.fail_the_connection();
416 }
417 self.origin = metadata
418 .final_url
419 .origin()
420 .ascii_serialization()
421 .into_owned();
422 self.announce_the_connection();
424 },
425 Err(error) => {
426 if error.is_permanent_failure() {
430 self.fail_the_connection()
431 } else {
432 self.reestablish_the_connection()
433 }
434 },
435 }
436 }
437
438 fn process_response_chunk(&mut self, cx: &mut JSContext, _: RequestId, chunk: Bytes) {
439 let mut output = String::with_capacity(chunk.len());
440 let mut input = &chunk[..];
441
442 loop {
443 if input.is_empty() {
444 return;
445 }
446 let (result, bytes_read) =
447 self.decoder
448 .decode_to_string_without_replacement(input, &mut output, false);
449 match result {
450 encoding_rs::DecoderResult::InputEmpty => {
451 self.parse(cx, output.chars());
452 return;
453 },
454 encoding_rs::DecoderResult::Malformed(_, _) => {
455 self.parse(cx, output.chars());
456 self.parse(cx, "\u{FFFD}".chars());
457 output.clear();
458 input = &input[bytes_read..];
459 },
460 encoding_rs::DecoderResult::OutputFull => {
461 self.parse(cx, output.chars());
462 output.clear();
463 input = &input[bytes_read..];
464 },
465 }
466 }
467 }
468
469 fn process_response_eof(
470 mut self,
471 cx: &mut JSContext,
472 _: RequestId,
473 response: Result<(), NetworkError>,
474 timing: ResourceFetchTiming,
475 ) {
476 let mut output = String::new();
477 let (result, _) = self
478 .decoder
479 .decode_to_string_without_replacement(&[], &mut output, true);
480 if !output.is_empty() {
481 self.parse(cx, "\u{FFFD}".chars());
482 }
483 if matches!(result, encoding_rs::DecoderResult::Malformed(_, _)) {
484 self.parse(cx, "\u{FFFD}".chars());
485 }
486 if response.is_ok() {
487 self.reestablish_the_connection();
488 }
489
490 network_listener::submit_timing(cx, &self, &response, &timing);
491 }
492
493 fn process_csp_violations(
494 &mut self,
495 cx: &mut JSContext,
496 _request_id: RequestId,
497 violations: Vec<Violation>,
498 ) {
499 let global = &self.resource_timing_global();
500 global.report_csp_violations(cx, violations, None, None);
501 }
502}
503
504impl ResourceTimingListener for EventSourceContext {
505 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
506 (InitiatorType::Other, self.event_source.root().url().clone())
507 }
508
509 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
510 self.event_source.root().global()
511 }
512}
513
514impl EventSource {
515 fn new_inherited(url: ServoUrl, with_credentials: bool) -> EventSource {
516 EventSource {
517 eventtarget: EventTarget::new_inherited(),
518 url,
519 request: DomRefCell::new(None),
520 last_event_id: DomRefCell::new(DOMString::new()),
521 reconnection_time: Cell::new(DEFAULT_RECONNECTION_TIME),
522 generation_id: Cell::new(GenerationId(0)),
523
524 ready_state: Cell::new(ReadyState::Connecting),
525 with_credentials,
526 droppable: DroppableEventSource::new(DomRefCell::new(Default::default())),
527 }
528 }
529
530 fn new(
531 cx: &mut JSContext,
532 global: &GlobalScope,
533 proto: Option<HandleObject>,
534 url: ServoUrl,
535 with_credentials: bool,
536 ) -> DomRoot<EventSource> {
537 reflect_weak_referenceable_dom_object_with_proto(
538 cx,
539 Rc::new(EventSource::new_inherited(url, with_credentials)),
540 global,
541 proto,
542 )
543 }
544
545 pub(crate) fn cancel(&self) {
547 self.droppable.cancel();
548 self.fail_the_connection();
549 }
550
551 pub(crate) fn fail_the_connection(&self) {
553 let global = self.global();
554 let event_source = Trusted::new(self);
555 global.task_manager().remote_event_task_source().queue(
556 task!(fail_the_event_source_connection: move |cx| {
557 let event_source = event_source.root();
558 if event_source.ready_state.get() != ReadyState::Closed {
559 event_source.ready_state.set(ReadyState::Closed);
560 event_source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
561 }
562 }),
563 );
564 }
565
566 pub(crate) fn request(&self) -> RequestBuilder {
567 self.request.borrow().clone().unwrap()
568 }
569
570 pub(crate) fn url(&self) -> &ServoUrl {
571 &self.url
572 }
573}
574
575impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
576 fn Constructor(
578 cx: &mut JSContext,
579 global: &GlobalScope,
580 proto: Option<HandleObject>,
581 url: DOMString,
582 event_source_init: &EventSourceInit,
583 ) -> Fallible<DomRoot<EventSource>> {
584 let url_record = match global.encoding_parse_a_url(&url.str()) {
588 Ok(u) => u,
589 Err(_) => return Err(Error::Syntax(None)),
591 };
592 let event_source = EventSource::new(
594 cx,
595 global,
596 proto,
597 url_record.clone(),
599 event_source_init.withCredentials,
600 );
601 global.track_event_source(&event_source);
602 let cors_attribute_state = if event_source_init.withCredentials {
603 CorsSettings::UseCredentials
607 } else {
608 CorsSettings::Anonymous
610 };
611 let mut request = create_a_potential_cors_request(
616 global.webview_id(),
617 url_record,
618 Destination::None,
619 Some(cors_attribute_state),
620 Some(true),
621 global.get_referrer(),
622 )
623 .with_global_scope(global);
624
625 request.headers.insert(
628 header::ACCEPT,
629 HeaderValue::from_static("text/event-stream"),
630 );
631 request.cache_mode = CacheMode::NoStore;
633 *event_source.request.safe_borrow_mut(cx.no_gc()) = Some(request.clone());
635 event_source.droppable.set_controller(FetchController::new(
639 request.id,
640 false,
641 global.core_resource_thread(),
642 ));
643
644 let context = EventSourceContext {
645 decoder: UTF_8.new_decoder_with_bom_removal(),
646 event_source: Trusted::new(&event_source),
647 gen_id: event_source.generation_id.get(),
648 parser_state: ParserState::Eol,
649 field: String::new(),
650 value: String::new(),
651 origin: String::new(),
652
653 event_type: String::new(),
654 data: String::new(),
655 last_event_id: String::new(),
656 };
657
658 let task_source = global.task_manager().networking_task_source().into();
659 global.fetch(request, context, task_source);
660
661 Ok(event_source)
663 }
664
665 event_handler!(open, GetOnopen, SetOnopen);
667
668 event_handler!(message, GetOnmessage, SetOnmessage);
670
671 event_handler!(error, GetOnerror, SetOnerror);
673
674 fn Url(&self) -> DOMString {
676 DOMString::from(self.url.as_str())
677 }
678
679 fn WithCredentials(&self) -> bool {
681 self.with_credentials
682 }
683
684 fn ReadyState(&self) -> u16 {
686 self.ready_state.get() as u16
687 }
688
689 fn Close(&self) {
691 let GenerationId(prev_id) = self.generation_id.get();
692 self.generation_id.set(GenerationId(prev_id + 1));
693 self.droppable.cancel();
694 self.ready_state.set(ReadyState::Closed);
695 }
696}
697
698#[derive(JSTraceable, MallocSizeOf)]
699pub(crate) struct EventSourceTimeoutCallback {
700 #[ignore_malloc_size_of = "Because it is non-owning"]
701 event_source: Trusted<EventSource>,
702 #[no_trace]
703 event_source_context: EventSourceContext,
704}
705
706impl EventSourceTimeoutCallback {
707 pub(crate) fn invoke(self) {
709 let event_source = self.event_source.root();
710 let global = event_source.global();
711
712 if event_source.ready_state.get() != ReadyState::Connecting {
714 return;
715 }
716
717 let mut request = event_source.request();
719
720 if !event_source.last_event_id.borrow().is_empty() &&
724 let Ok(header_value) =
725 HeaderValue::from_str(&String::from(event_source.last_event_id.borrow().clone()))
726 {
727 request
729 .headers
730 .insert(HeaderName::from_static("last-event-id"), header_value);
731 }
732
733 let task_source = global.task_manager().networking_task_source().into();
736 global.fetch(request, self.event_source_context, task_source);
737 }
738}