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