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