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