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 http://mozilla.org/MPL/2.0/. */
4 
5 //! The listener that encapsulates all state for an in-progress document request.
6 //! Any redirects that are encountered are followed. Whenever a non-redirect
7 //! response is received, it is forwarded to the appropriate script thread.
8 
9 use hyper::header::Location;
10 use ipc_channel::ipc;
11 use ipc_channel::router::ROUTER;
12 use msg::constellation_msg::PipelineId;
13 use net::http_loader::{set_default_accept, set_default_accept_language};
14 use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseMsg};
15 use net_traits::{IpcSend, NetworkError, ResourceThreads};
16 use net_traits::request::{Destination, RequestInit};
17 use net_traits::response::ResponseInit;
18 use std::sync::mpsc::Sender;
19 
20 pub struct NetworkListener {
21     res_init: Option<ResponseInit>,
22     req_init: RequestInit,
23     pipeline_id: PipelineId,
24     resource_threads: ResourceThreads,
25     sender: Sender<(PipelineId, FetchResponseMsg)>,
26     should_send: bool,
27 }
28 
29 impl NetworkListener {
new(req_init: RequestInit, pipeline_id: PipelineId, resource_threads: ResourceThreads, sender: Sender<(PipelineId, FetchResponseMsg)>) -> NetworkListener30     pub fn new(req_init: RequestInit,
31                pipeline_id: PipelineId,
32                resource_threads: ResourceThreads,
33                sender: Sender<(PipelineId, FetchResponseMsg)>) -> NetworkListener {
34         NetworkListener {
35             res_init: None,
36             req_init,
37             pipeline_id,
38             resource_threads,
39             sender,
40             should_send: false
41         }
42     }
43 
initiate_fetch(&self, cancel_chan: Option<ipc::IpcReceiver<()>>)44     pub fn initiate_fetch(&self, cancel_chan: Option<ipc::IpcReceiver<()>>) {
45         let (ipc_sender, ipc_receiver) = ipc::channel().expect("Failed to create IPC channel!");
46 
47         let mut listener = NetworkListener {
48             res_init: self.res_init.clone(),
49             req_init: self.req_init.clone(),
50             resource_threads: self.resource_threads.clone(),
51             sender: self.sender.clone(),
52             pipeline_id: self.pipeline_id.clone(),
53             should_send: false,
54         };
55 
56         let msg = match self.res_init {
57             Some(ref res_init_) => CoreResourceMsg::FetchRedirect(
58                                    self.req_init.clone(),
59                                    res_init_.clone(),
60                                    ipc_sender, None),
61             None => {
62                 set_default_accept(Destination::Document, &mut listener.req_init.headers);
63                 set_default_accept_language(&mut listener.req_init.headers);
64 
65                 CoreResourceMsg::Fetch(
66                 listener.req_init.clone(),
67                 FetchChannels::ResponseMsg(ipc_sender, cancel_chan))
68             }
69         };
70 
71         ROUTER.add_route(ipc_receiver.to_opaque(), Box::new(move |message| {
72             let msg = message.to();
73             match msg {
74                 Ok(FetchResponseMsg::ProcessResponse(res)) => listener.check_redirect(res),
75                 Ok(msg_) => listener.send(msg_),
76                 Err(e) => warn!("Error while receiving network listener message: {}", e),
77             };
78         }));
79 
80         if let Err(e) = self.resource_threads.sender().send(msg) {
81             warn!("Resource thread unavailable ({})", e);
82         }
83     }
84 
check_redirect(&mut self, message: Result<(FetchMetadata), NetworkError>)85     fn check_redirect(&mut self,
86                       message: Result<(FetchMetadata), NetworkError>) {
87         match message {
88             Ok(res_metadata) => {
89                 let metadata = match res_metadata {
90                     FetchMetadata::Filtered { ref unsafe_, .. } => unsafe_,
91                     FetchMetadata::Unfiltered(ref m) => m,
92                 };
93 
94                 match metadata.headers {
95                     Some(ref headers) if headers.has::<Location>() => {
96                         if self.req_init.url_list.is_empty() {
97                             self.req_init.url_list.push(self.req_init.url.clone());
98                         }
99                         self.req_init.url_list.push(metadata.final_url.clone());
100 
101                         self.req_init.referrer_url = metadata.referrer.clone();
102                         self.req_init.referrer_policy = metadata.referrer_policy;
103 
104                         self.res_init = Some(ResponseInit {
105                             url: metadata.final_url.clone(),
106                             location_url: metadata.location_url.clone(),
107                             headers: headers.clone().into_inner(),
108                             referrer: metadata.referrer.clone(),
109                         });
110 
111                         // XXXManishearth we don't have the cancel_chan anymore and
112                         // can't use it here.
113                         //
114                         // Ideally the Fetch code would handle manual redirects on its own
115                         self.initiate_fetch(None);
116                     },
117                     _ => {
118                         // Response should be processed by script thread.
119                         self.should_send = true;
120                         self.send(FetchResponseMsg::ProcessResponse(Ok(res_metadata.clone())));
121                     }
122                 };
123             },
124             Err(e) => {
125                 self.should_send = true;
126                 self.send(FetchResponseMsg::ProcessResponse(Err(e)))
127             }
128         };
129     }
130 
send(&mut self, msg: FetchResponseMsg)131     fn send(&mut self, msg: FetchResponseMsg) {
132         if self.should_send {
133             if let Err(e) = self.sender.send((self.pipeline_id, msg)) {
134                 warn!("Failed to forward network message to pipeline {}: {:?}", self.pipeline_id, e);
135             }
136         }
137     }
138 }
139