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::request::{CacheMode, CorsSettings, Destination, RequestBuilder, RequestId};
23use net_traits::{FetchMetadata, FilteredMetadata, NetworkError, ResourceFetchTiming};
24use script_bindings::cell::DomRefCell;
25use script_bindings::reflector::reflect_weak_referenceable_dom_object_with_proto;
26use servo_url::ServoUrl;
27use stylo_atoms::Atom;
28
29use crate::dom::bindings::codegen::Bindings::EventSourceBinding::{
30 EventSourceInit, EventSourceMethods,
31};
32use crate::dom::bindings::error::{Error, Fallible};
33use crate::dom::bindings::inheritance::Castable;
34use crate::dom::bindings::refcounted::Trusted;
35use crate::dom::bindings::reflector::DomGlobal;
36use crate::dom::bindings::root::DomRoot;
37use crate::dom::bindings::str::DOMString;
38use crate::dom::csp::{GlobalCspReporting, Violation};
39use crate::dom::event::Event;
40use crate::dom::eventtarget::EventTarget;
41use crate::dom::globalscope::GlobalScope;
42use crate::dom::messageevent::MessageEvent;
43use crate::dom::performance::performanceresourcetiming::InitiatorType;
44use crate::event_loop::timers::OneshotTimerCallback;
45use crate::fetch::fetch::{
46 FetchCanceller, RequestWithGlobalScope, create_a_potential_cors_request,
47};
48use crate::fetch::network_listener::{self, FetchResponseListener, ResourceTimingListener};
49use crate::realms::enter_auto_realm;
50
51const DEFAULT_RECONNECTION_TIME: Duration = Duration::from_millis(5000);
52
53#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
54struct GenerationId(u32);
55
56#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
57enum ReadyState {
59 Connecting = 0,
60 Open = 1,
61 Closed = 2,
62}
63
64#[derive(JSTraceable, MallocSizeOf)]
65struct DroppableEventSource {
66 canceller: DomRefCell<FetchCanceller>,
67}
68
69impl DroppableEventSource {
70 pub(crate) fn new(canceller: DomRefCell<FetchCanceller>) -> Self {
71 DroppableEventSource { canceller }
72 }
73
74 pub(crate) fn cancel(&self) {
75 self.canceller.borrow_mut().abort();
76 }
77
78 pub(crate) fn set_canceller(&self, data: FetchCanceller) {
79 *self.canceller.borrow_mut() = data;
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(fm) => {
401 let meta = match fm {
402 FetchMetadata::Unfiltered(m) => m,
403 FetchMetadata::Filtered { unsafe_, filtered } => match filtered {
404 FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_) => {
405 return self.fail_the_connection();
406 },
407 _ => unsafe_,
408 },
409 };
410 if meta.status.code() != StatusCode::OK {
413 return self.fail_the_connection();
414 }
415 let mime = match meta.content_type {
416 None => return self.fail_the_connection(),
417 Some(ct) => <ContentType as Into<Mime>>::into(ct.into_inner()),
418 };
419 if (mime.type_(), mime.subtype()) != (mime::TEXT, mime::EVENT_STREAM) {
420 return self.fail_the_connection();
421 }
422 self.origin = meta.final_url.origin().ascii_serialization().into_owned();
423 self.announce_the_connection();
425 },
426 Err(error) => {
427 if error.is_permanent_failure() {
431 self.fail_the_connection()
432 } else {
433 self.reestablish_the_connection()
434 }
435 },
436 }
437 }
438
439 fn process_response_chunk(&mut self, cx: &mut JSContext, _: RequestId, chunk: Bytes) {
440 let mut output = String::with_capacity(chunk.len());
441 let mut input = &chunk[..];
442
443 loop {
444 if input.is_empty() {
445 return;
446 }
447 let (result, bytes_read) =
448 self.decoder
449 .decode_to_string_without_replacement(input, &mut output, false);
450 match result {
451 encoding_rs::DecoderResult::InputEmpty => {
452 self.parse(cx, output.chars());
453 return;
454 },
455 encoding_rs::DecoderResult::Malformed(_, _) => {
456 self.parse(cx, output.chars());
457 self.parse(cx, "\u{FFFD}".chars());
458 output.clear();
459 input = &input[bytes_read..];
460 },
461 encoding_rs::DecoderResult::OutputFull => {
462 self.parse(cx, output.chars());
463 output.clear();
464 input = &input[bytes_read..];
465 },
466 }
467 }
468 }
469
470 fn process_response_eof(
471 mut self,
472 cx: &mut JSContext,
473 _: RequestId,
474 response: Result<(), NetworkError>,
475 timing: ResourceFetchTiming,
476 ) {
477 let mut output = String::new();
478 let (result, _) = self
479 .decoder
480 .decode_to_string_without_replacement(&[], &mut output, true);
481 if !output.is_empty() {
482 self.parse(cx, "\u{FFFD}".chars());
483 }
484 if matches!(result, encoding_rs::DecoderResult::Malformed(_, _)) {
485 self.parse(cx, "\u{FFFD}".chars());
486 }
487 if response.is_ok() {
488 self.reestablish_the_connection();
489 }
490
491 network_listener::submit_timing(cx, &self, &response, &timing);
492 }
493
494 fn process_csp_violations(
495 &mut self,
496 cx: &mut JSContext,
497 _request_id: RequestId,
498 violations: Vec<Violation>,
499 ) {
500 let global = &self.resource_timing_global();
501 global.report_csp_violations(cx, violations, None, None);
502 }
503}
504
505impl ResourceTimingListener for EventSourceContext {
506 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
507 (InitiatorType::Other, self.event_source.root().url().clone())
508 }
509
510 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
511 self.event_source.root().global()
512 }
513}
514
515impl EventSource {
516 fn new_inherited(url: ServoUrl, with_credentials: bool) -> EventSource {
517 EventSource {
518 eventtarget: EventTarget::new_inherited(),
519 url,
520 request: DomRefCell::new(None),
521 last_event_id: DomRefCell::new(DOMString::new()),
522 reconnection_time: Cell::new(DEFAULT_RECONNECTION_TIME),
523 generation_id: Cell::new(GenerationId(0)),
524
525 ready_state: Cell::new(ReadyState::Connecting),
526 with_credentials,
527 droppable: DroppableEventSource::new(DomRefCell::new(Default::default())),
528 }
529 }
530
531 fn new(
532 cx: &mut JSContext,
533 global: &GlobalScope,
534 proto: Option<HandleObject>,
535 url: ServoUrl,
536 with_credentials: bool,
537 ) -> DomRoot<EventSource> {
538 reflect_weak_referenceable_dom_object_with_proto(
539 cx,
540 Rc::new(EventSource::new_inherited(url, with_credentials)),
541 global,
542 proto,
543 )
544 }
545
546 pub(crate) fn cancel(&self) {
548 self.droppable.cancel();
549 self.fail_the_connection();
550 }
551
552 pub(crate) fn fail_the_connection(&self) {
554 let global = self.global();
555 let event_source = Trusted::new(self);
556 global.task_manager().remote_event_task_source().queue(
557 task!(fail_the_event_source_connection: move |cx| {
558 let event_source = event_source.root();
559 if event_source.ready_state.get() != ReadyState::Closed {
560 event_source.ready_state.set(ReadyState::Closed);
561 event_source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
562 }
563 }),
564 );
565 }
566
567 pub(crate) fn request(&self) -> RequestBuilder {
568 self.request.borrow().clone().unwrap()
569 }
570
571 pub(crate) fn url(&self) -> &ServoUrl {
572 &self.url
573 }
574}
575
576impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
577 fn Constructor(
579 cx: &mut JSContext,
580 global: &GlobalScope,
581 proto: Option<HandleObject>,
582 url: DOMString,
583 event_source_init: &EventSourceInit,
584 ) -> Fallible<DomRoot<EventSource>> {
585 let url_record = match global.encoding_parse_a_url(&url.str()) {
589 Ok(u) => u,
590 Err(_) => return Err(Error::Syntax(None)),
592 };
593 let event_source = EventSource::new(
595 cx,
596 global,
597 proto,
598 url_record.clone(),
600 event_source_init.withCredentials,
601 );
602 global.track_event_source(&event_source);
603 let cors_attribute_state = if event_source_init.withCredentials {
604 CorsSettings::UseCredentials
608 } else {
609 CorsSettings::Anonymous
611 };
612 let mut request = create_a_potential_cors_request(
617 global.webview_id(),
618 url_record,
619 Destination::None,
620 Some(cors_attribute_state),
621 Some(true),
622 global.get_referrer(),
623 )
624 .with_global_scope(global);
625
626 request.headers.insert(
629 header::ACCEPT,
630 HeaderValue::from_static("text/event-stream"),
631 );
632 request.cache_mode = CacheMode::NoStore;
634 *event_source.request.safe_borrow_mut(cx.no_gc()) = Some(request.clone());
636 event_source.droppable.set_canceller(FetchCanceller::new(
640 request.id,
641 false,
642 global.core_resource_thread(),
643 ));
644
645 let context = EventSourceContext {
646 decoder: UTF_8.new_decoder_with_bom_removal(),
647 event_source: Trusted::new(&event_source),
648 gen_id: event_source.generation_id.get(),
649 parser_state: ParserState::Eol,
650 field: String::new(),
651 value: String::new(),
652 origin: String::new(),
653
654 event_type: String::new(),
655 data: String::new(),
656 last_event_id: String::new(),
657 };
658
659 let task_source = global.task_manager().networking_task_source().into();
660 global.fetch(request, context, task_source);
661
662 Ok(event_source)
664 }
665
666 event_handler!(open, GetOnopen, SetOnopen);
668
669 event_handler!(message, GetOnmessage, SetOnmessage);
671
672 event_handler!(error, GetOnerror, SetOnerror);
674
675 fn Url(&self) -> DOMString {
677 DOMString::from(self.url.as_str())
678 }
679
680 fn WithCredentials(&self) -> bool {
682 self.with_credentials
683 }
684
685 fn ReadyState(&self) -> u16 {
687 self.ready_state.get() as u16
688 }
689
690 fn Close(&self) {
692 let GenerationId(prev_id) = self.generation_id.get();
693 self.generation_id.set(GenerationId(prev_id + 1));
694 self.droppable.cancel();
695 self.ready_state.set(ReadyState::Closed);
696 }
697}
698
699#[derive(JSTraceable, MallocSizeOf)]
700pub(crate) struct EventSourceTimeoutCallback {
701 #[ignore_malloc_size_of = "Because it is non-owning"]
702 event_source: Trusted<EventSource>,
703 #[no_trace]
704 event_source_context: EventSourceContext,
705}
706
707impl EventSourceTimeoutCallback {
708 pub(crate) fn invoke(self) {
710 let event_source = self.event_source.root();
711 let global = event_source.global();
712
713 if event_source.ready_state.get() != ReadyState::Connecting {
715 return;
716 }
717
718 let mut request = event_source.request();
720
721 if !event_source.last_event_id.borrow().is_empty() &&
725 let Ok(header_value) =
726 HeaderValue::from_str(&String::from(event_source.last_event_id.borrow().clone()))
727 {
728 request
730 .headers
731 .insert(HeaderName::from_static("last-event-id"), header_value);
732 }
733
734 let task_source = global.task_manager().networking_task_source().into();
737 global.fetch(request, self.event_source_context, task_source);
738 }
739}