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 use cookie::Cookie;
6 use fetch::methods::{should_be_blocked_due_to_bad_port, should_be_blocked_due_to_nosniff};
7 use hosts::replace_host;
8 use http_loader::{HttpState, is_redirect_status, set_default_accept};
9 use http_loader::{set_default_accept_language, set_request_cookies};
10 use hyper::buffer::BufReader;
11 use hyper::header::{CacheControl, CacheDirective, Connection, ConnectionOption};
12 use hyper::header::{Headers, Host, SetCookie, Pragma, Protocol, ProtocolName, Upgrade};
13 use hyper::http::h1::{LINE_ENDING, parse_response};
14 use hyper::method::Method;
15 use hyper::net::HttpStream;
16 use hyper::status::StatusCode;
17 use hyper::version::HttpVersion;
18 use ipc_channel::ipc::{IpcReceiver, IpcSender};
19 use net_traits::{CookieSource, MessageData, NetworkError};
20 use net_traits::{WebSocketDomAction, WebSocketNetworkEvent};
21 use net_traits::request::{Destination, RequestInit, RequestMode};
22 use servo_url::ServoUrl;
23 use std::io::{self, Write};
24 use std::net::TcpStream;
25 use std::sync::{Arc, Mutex};
26 use std::sync::atomic::{AtomicBool, Ordering};
27 use std::thread;
28 use url::Position;
29 use websocket::{Message, Receiver as WSReceiver, Sender as WSSender};
30 use websocket::header::{Origin, WebSocketAccept, WebSocketKey, WebSocketProtocol, WebSocketVersion};
31 use websocket::message::Type as MessageType;
32 use websocket::receiver::Receiver;
33 use websocket::sender::Sender;
34 
init( req_init: RequestInit, resource_event_sender: IpcSender<WebSocketNetworkEvent>, dom_action_receiver: IpcReceiver<WebSocketDomAction>, http_state: Arc<HttpState> )35 pub fn init(
36     req_init: RequestInit,
37     resource_event_sender: IpcSender<WebSocketNetworkEvent>,
38     dom_action_receiver: IpcReceiver<WebSocketDomAction>,
39     http_state: Arc<HttpState>
40 ) {
41     thread::Builder::new().name(format!("WebSocket connection to {}", req_init.url)).spawn(move || {
42         let channel = establish_a_websocket_connection(req_init, &http_state);
43         let (ws_sender, mut receiver) = match channel {
44             Ok((protocol_in_use, sender, receiver)) => {
45                 let _ = resource_event_sender.send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use });
46                 (sender, receiver)
47             },
48             Err(e) => {
49                 debug!("Failed to establish a WebSocket connection: {:?}", e);
50                 let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
51                 return;
52             }
53 
54         };
55 
56         let initiated_close = Arc::new(AtomicBool::new(false));
57         let ws_sender = Arc::new(Mutex::new(ws_sender));
58 
59         let initiated_close_incoming = initiated_close.clone();
60         let ws_sender_incoming = ws_sender.clone();
61         thread::spawn(move || {
62             for message in receiver.incoming_messages() {
63                 let message: Message = match message {
64                     Ok(m) => m,
65                     Err(e) => {
66                         debug!("Error receiving incoming WebSocket message: {:?}", e);
67                         let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
68                         break;
69                     }
70                 };
71                 let message = match message.opcode {
72                     MessageType::Text => MessageData::Text(String::from_utf8_lossy(&message.payload).into_owned()),
73                     MessageType::Binary => MessageData::Binary(message.payload.into_owned()),
74                     MessageType::Ping => {
75                         let pong = Message::pong(message.payload);
76                         ws_sender_incoming.lock().unwrap().send_message(&pong).unwrap();
77                         continue;
78                     },
79                     MessageType::Pong => continue,
80                     MessageType::Close => {
81                         if !initiated_close_incoming.fetch_or(true, Ordering::SeqCst) {
82                             ws_sender_incoming.lock().unwrap().send_message(&message).unwrap();
83                         }
84                         let code = message.cd_status_code;
85                         let reason = String::from_utf8_lossy(&message.payload).into_owned();
86                         let _ = resource_event_sender.send(WebSocketNetworkEvent::Close(code, reason));
87                         break;
88                     },
89                 };
90                 let _ = resource_event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
91             }
92         });
93 
94         while let Ok(dom_action) = dom_action_receiver.recv() {
95             match dom_action {
96                 WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
97                     ws_sender.lock().unwrap().send_message(&Message::text(data)).unwrap();
98                 },
99                 WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
100                     ws_sender.lock().unwrap().send_message(&Message::binary(data)).unwrap();
101                 },
102                 WebSocketDomAction::Close(code, reason) => {
103                     if !initiated_close.fetch_or(true, Ordering::SeqCst) {
104                         let message = match code {
105                             Some(code) => Message::close_because(code, reason.unwrap_or("".to_owned())),
106                             None => Message::close()
107                         };
108                         ws_sender.lock().unwrap().send_message(&message).unwrap();
109                     }
110                 },
111             }
112         }
113     }).expect("Thread spawning failed");
114 }
115 
116 type Stream = HttpStream;
117 
118 // https://fetch.spec.whatwg.org/#concept-websocket-connection-obtain
obtain_a_websocket_connection(url: &ServoUrl) -> Result<Stream, NetworkError>119 fn obtain_a_websocket_connection(url: &ServoUrl) -> Result<Stream, NetworkError> {
120     // Step 1.
121     let host = url.host_str().unwrap();
122 
123     // Step 2.
124     let port = url.port_or_known_default().unwrap();
125 
126     // Step 3.
127     // We did not replace the scheme by "http" or "https" in step 1 of
128     // establish_a_websocket_connection.
129     let secure = match url.scheme() {
130         "ws" => false,
131         "wss" => true,
132         _ => panic!("URL's scheme should be ws or wss"),
133     };
134 
135     if secure {
136         return Err(NetworkError::Internal("WSS is disabled for now.".into()));
137     }
138 
139     // Steps 4-5.
140     let host = replace_host(host);
141     let tcp_stream = TcpStream::connect((&*host, port)).map_err(|e| {
142         NetworkError::Internal(format!("Could not connect to host: {}", e))
143     })?;
144     Ok(HttpStream(tcp_stream))
145 }
146 
147 // https://fetch.spec.whatwg.org/#concept-websocket-establish
establish_a_websocket_connection( req_init: RequestInit, http_state: &HttpState ) -> Result<(Option<String>, Sender<Stream>, Receiver<Stream>), NetworkError>148 fn establish_a_websocket_connection(
149     req_init: RequestInit,
150     http_state: &HttpState
151 ) -> Result<(Option<String>, Sender<Stream>, Receiver<Stream>), NetworkError>
152 {
153     let protocols = match req_init.mode {
154         RequestMode::WebSocket { protocols } => protocols.clone(),
155         _ => panic!("Received a RequestInit with a non-websocket mode in websocket_loader"),
156     };
157     // Steps 1 is not really applicable here, given we don't exactly go
158     // through the same infrastructure as the Fetch spec.
159 
160     // Step 2, slimmed down because we don't go through the whole Fetch infra.
161     let mut headers = Headers::new();
162 
163     // Step 3.
164     headers.set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]));
165 
166     // Step 4.
167     headers.set(Connection(vec![ConnectionOption::ConnectionHeader("upgrade".into())]));
168 
169     // Step 5.
170     let key_value = WebSocketKey::new();
171 
172     // Step 6.
173     headers.set(key_value);
174 
175     // Step 7.
176     headers.set(WebSocketVersion::WebSocket13);
177 
178     // Step 8.
179     if !protocols.is_empty() {
180         headers.set(WebSocketProtocol(protocols.clone()));
181     }
182 
183     // Steps 9-10.
184     // TODO: handle permessage-deflate extension.
185 
186     // Step 11 and network error check from step 12.
187     let response = fetch(req_init.url, req_init.origin.ascii_serialization(), headers, http_state)?;
188 
189     // Step 12, the status code check.
190     if response.status != StatusCode::SwitchingProtocols {
191         return Err(NetworkError::Internal("Response's status should be 101.".into()));
192     }
193 
194     // Step 13.
195     if !protocols.is_empty() {
196         if response.headers.get::<WebSocketProtocol>().map_or(true, |protocols| protocols.is_empty()) {
197             return Err(NetworkError::Internal(
198                 "Response's Sec-WebSocket-Protocol header is missing, malformed or empty.".into()));
199         }
200     }
201 
202     // Step 14.2.
203     let upgrade_header = response.headers.get::<Upgrade>().ok_or_else(|| {
204         NetworkError::Internal("Response should have an Upgrade header.".into())
205     })?;
206     if upgrade_header.len() != 1 {
207         return Err(NetworkError::Internal("Response's Upgrade header should have only one value.".into()));
208     }
209     if upgrade_header[0].name != ProtocolName::WebSocket {
210         return Err(NetworkError::Internal("Response's Upgrade header value should be \"websocket\".".into()));
211     }
212 
213     // Step 14.3.
214     let connection_header = response.headers.get::<Connection>().ok_or_else(|| {
215         NetworkError::Internal("Response should have a Connection header.".into())
216     })?;
217     let connection_includes_upgrade = connection_header.iter().any(|option| {
218         match *option {
219             ConnectionOption::ConnectionHeader(ref option) => *option == "upgrade",
220             _ => false,
221         }
222     });
223     if !connection_includes_upgrade {
224         return Err(NetworkError::Internal("Response's Connection header value should include \"upgrade\".".into()));
225     }
226 
227     // Step 14.4.
228     let accept_header = response.headers.get::<WebSocketAccept>().ok_or_else(|| {
229         NetworkError::Internal("Response should have a Sec-Websocket-Accept header.".into())
230     })?;
231     if *accept_header != WebSocketAccept::new(&key_value) {
232         return Err(NetworkError::Internal(
233             "Response's Sec-WebSocket-Accept header value did not match the sent key.".into()));
234     }
235 
236     // Step 14.5.
237     // TODO: handle permessage-deflate extension.
238     // We don't support any extension, so we fail at the mere presence of
239     // a Sec-WebSocket-Extensions header.
240     if response.headers.get_raw("Sec-WebSocket-Extensions").is_some() {
241         return Err(NetworkError::Internal(
242             "Response's Sec-WebSocket-Extensions header value included unsupported extensions.".into()));
243     }
244 
245     // Step 14.6.
246     let protocol_in_use = if let Some(response_protocols) = response.headers.get::<WebSocketProtocol>() {
247         for replied in &**response_protocols {
248             if !protocols.iter().any(|requested| requested.eq_ignore_ascii_case(replied)) {
249                 return Err(NetworkError::Internal(
250                     "Response's Sec-WebSocket-Protocols contain values that were not requested.".into()));
251             }
252         }
253         response_protocols.first().cloned()
254     } else {
255         None
256     };
257 
258     let sender = Sender::new(response.writer, true);
259     let receiver = Receiver::new(response.reader, false);
260     Ok((protocol_in_use, sender, receiver))
261 }
262 
263 struct Response {
264     status: StatusCode,
265     headers: Headers,
266     reader: BufReader<Stream>,
267     writer: Stream,
268 }
269 
270 // https://fetch.spec.whatwg.org/#concept-fetch
fetch(url: ServoUrl, origin: String, mut headers: Headers, http_state: &HttpState) -> Result<Response, NetworkError>271 fn fetch(url: ServoUrl,
272          origin: String,
273          mut headers: Headers,
274          http_state: &HttpState)
275          -> Result<Response, NetworkError> {
276     // Step 1.
277     // TODO: handle request's window.
278 
279     // Step 2.
280     // TODO: handle request's origin.
281 
282     // Step 3.
283     set_default_accept(Destination::None, &mut headers);
284 
285     // Step 4.
286     set_default_accept_language(&mut headers);
287 
288     // Step 5.
289     // TODO: handle request's priority.
290 
291     // Step 6.
292     // Not applicable: not a navigation request.
293 
294     // Step 7.
295     // We know this is a subresource request.
296     {
297         // Step 7.1.
298         // Not applicable: client hints list is empty.
299 
300         // Steps 7.2-3.
301         // TODO: handle fetch groups.
302     }
303 
304     // Step 8.
305     main_fetch(url, origin, headers, http_state)
306 }
307 
308 // https://fetch.spec.whatwg.org/#concept-main-fetch
main_fetch(url: ServoUrl, origin: String, mut headers: Headers, http_state: &HttpState) -> Result<Response, NetworkError>309 fn main_fetch(url: ServoUrl,
310               origin: String,
311               mut headers: Headers,
312               http_state: &HttpState)
313               -> Result<Response, NetworkError> {
314     // Step 1.
315     let mut response = None;
316 
317     // Step 2.
318     // Not applicable: request’s local-URLs-only flag is unset.
319 
320     // Step 3.
321     // TODO: handle content security policy violations.
322 
323     // Step 4.
324     // TODO: handle upgrade to a potentially secure URL.
325 
326     // Step 5.
327     if should_be_blocked_due_to_bad_port(&url) {
328         response = Some(Err(NetworkError::Internal("Request should be blocked due to bad port.".into())));
329     }
330     // TODO: handle blocking as mixed content.
331     // TODO: handle blocking by content security policy.
332 
333     // Steps 6-8.
334     // TODO: handle request's referrer policy.
335 
336     // Step 9.
337     // Not applicable: request's current URL's scheme is not "ftp".
338 
339     // Step 10.
340     // TODO: handle known HSTS host domain.
341 
342     // Step 11.
343     // Not applicable: request's synchronous flag is set.
344 
345     // Step 12.
346     let mut response = response.unwrap_or_else(|| {
347         // We must run the first sequence of substeps, given request's mode
348         // is "websocket".
349 
350         // Step 12.1.
351         // Not applicable: the response is never exposed to the Web so it
352         // doesn't need to be filtered at all.
353 
354         // Step 12.2.
355         scheme_fetch(&url, origin, &mut headers, http_state)
356     });
357 
358     // Step 13.
359     // Not applicable: recursive flag is unset.
360 
361     // Step 14.
362     // Not applicable: the response is never exposed to the Web so it doesn't
363     // need to be filtered at all.
364 
365     // Steps 15-16.
366     // Not applicable: no need to maintain an internal response.
367 
368     // Step 17.
369     if response.is_ok() {
370         // TODO: handle blocking as mixed content.
371         // TODO: handle blocking by content security policy.
372         // Not applicable: blocking due to MIME type matters only for scripts.
373         if should_be_blocked_due_to_nosniff(Destination::None, &headers) {
374             response = Err(NetworkError::Internal("Request should be blocked due to nosniff.".into()));
375         }
376     }
377 
378     // Step 18.
379     // Not applicable: we don't care about the body at all.
380 
381     // Step 19.
382     // Not applicable: request's integrity metadata is the empty string.
383 
384     // Step 20.
385     // TODO: wait for response's body here, maybe?
386     response
387 }
388 
389 // https://fetch.spec.whatwg.org/#concept-scheme-fetch
scheme_fetch(url: &ServoUrl, origin: String, headers: &mut Headers, http_state: &HttpState) -> Result<Response, NetworkError>390 fn scheme_fetch(url: &ServoUrl,
391                origin: String,
392                headers: &mut Headers,
393                http_state: &HttpState)
394                -> Result<Response, NetworkError> {
395     // In the case of a WebSocket request, HTTP fetch is always used.
396     http_fetch(url, origin, headers, http_state)
397 }
398 
399 // https://fetch.spec.whatwg.org/#concept-http-fetch
http_fetch(url: &ServoUrl, origin: String, headers: &mut Headers, http_state: &HttpState) -> Result<Response, NetworkError>400 fn http_fetch(url: &ServoUrl,
401               origin: String,
402               headers: &mut Headers,
403               http_state: &HttpState)
404               -> Result<Response, NetworkError> {
405     // Step 1.
406     // Not applicable: with step 3 being useless here, this one is too.
407 
408     // Step 2.
409     // Not applicable: we don't need to maintain an internal response.
410 
411     // Step 3.
412     // Not applicable: request's service-workers mode is "none".
413 
414     // Step 4.
415     // There cannot be a response yet at this point.
416     let mut response = {
417         // Step 4.1.
418         // Not applicable: CORS-preflight flag is unset.
419 
420         // Step 4.2.
421         // Not applicable: request's redirect mode is "error".
422 
423         // Step 4.3.
424         let response = http_network_or_cache_fetch(url, origin, headers, http_state);
425 
426         // Step 4.4.
427         // Not applicable: CORS flag is unset.
428 
429         response
430     };
431 
432     // Step 5.
433     if response.as_ref().ok().map_or(false, |response| is_redirect_status(response.status)) {
434         // Step 5.1.
435         // Not applicable: the connection does not use HTTP/2.
436 
437         // Steps 5.2-4.
438         // Not applicable: matters only if request's redirect mode is not "error".
439 
440         // Step 5.5.
441         // Request's redirect mode is "error".
442         response = Err(NetworkError::Internal("Response should not be a redirection.".into()));
443     }
444 
445     // Step 6.
446     response
447 }
448 
449 // https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch
http_network_or_cache_fetch(url: &ServoUrl, origin: String, headers: &mut Headers, http_state: &HttpState) -> Result<Response, NetworkError>450 fn http_network_or_cache_fetch(url: &ServoUrl,
451                                origin: String,
452                                headers: &mut Headers,
453                                http_state: &HttpState)
454                                -> Result<Response, NetworkError> {
455     // Steps 1-3.
456     // Not applicable: we don't even have a request yet, and there is no body
457     // in a WebSocket request.
458 
459     // Step 4.
460     // Not applicable: credentials flag is always set
461     // because credentials mode is "include."
462 
463     // Steps 5-9.
464     // Not applicable: there is no body in a WebSocket request.
465 
466     // Step 10.
467     // TODO: handle header Referer.
468 
469     // Step 11.
470     // Request's mode is "websocket".
471     headers.set(Origin(origin));
472 
473     // Step 12.
474     // TODO: handle header User-Agent.
475 
476     // Steps 13-14.
477     // Not applicable: request's cache mode is "no-store".
478 
479     // Step 15.
480     {
481         // Step 15.1.
482         // We know there is no Pragma header yet.
483         headers.set(Pragma::NoCache);
484 
485         // Step 15.2.
486         // We know there is no Cache-Control header yet.
487         headers.set(CacheControl(vec![CacheDirective::NoCache]));
488     }
489 
490     // Step 16.
491     // TODO: handle Accept-Encoding.
492     // Not applicable: Connection header is already present.
493     // TODO: handle DNT.
494     headers.set(Host {
495         hostname: url.host_str().unwrap().to_owned(),
496         port: url.port(),
497     });
498 
499     // Step 17.
500     // Credentials flag is set.
501     {
502         // Step 17.1.
503         // TODO: handle user agent configured to block cookies.
504         set_request_cookies(&url, headers, &http_state.cookie_jar);
505 
506         // Steps 17.2-6.
507         // Not applicable: request has no Authorization header.
508     }
509 
510     // Step 18.
511     // TODO: proxy-authentication entry.
512 
513     // Step 19.
514     // Not applicable: with step 21 being useless, this one is too.
515 
516     // Step 20.
517     // Not applicable: revalidatingFlag is only useful if step 21 is.
518 
519     // Step 21.
520     // Not applicable: cache mode is "no-store".
521 
522     // Step 22.
523     // There is no response yet.
524     let response = {
525         // Step 22.1.
526         // Not applicable: cache mode is "no-store".
527 
528         // Step 22.2.
529         let forward_response = http_network_fetch(url, headers, http_state);
530 
531         // Step 22.3.
532         // Not applicable: request's method is not unsafe.
533 
534         // Step 22.4.
535         // Not applicable: revalidatingFlag is unset.
536 
537         // Step 22.5.
538         // There is no response yet and the response should not be cached.
539         forward_response
540     };
541 
542     // Step 23.
543     // TODO: handle 401 status when request's window is not "no-window".
544 
545     // Step 24.
546     // TODO: handle 407 status when request's window is not "no-window".
547 
548     // Step 25.
549     // Not applicable: authentication-fetch flag is unset.
550 
551     // Step 26.
552     response
553 }
554 
555 // https://fetch.spec.whatwg.org/#concept-http-network-fetch
http_network_fetch(url: &ServoUrl, headers: &Headers, http_state: &HttpState) -> Result<Response, NetworkError>556 fn http_network_fetch(url: &ServoUrl,
557                       headers: &Headers,
558                       http_state: &HttpState)
559                       -> Result<Response, NetworkError> {
560     // Step 1.
561     // Not applicable: credentials flag is set.
562 
563     // Steps 2-3.
564     // Request's mode is "websocket".
565     let connection = obtain_a_websocket_connection(url)?;
566 
567     // Step 4.
568     // Not applicable: request’s body is null.
569 
570     // Step 5.
571     let response = make_request(connection, url, headers)?;
572 
573     // Steps 6-12.
574     // Not applicable: correct WebSocket responses don't have a body.
575 
576     // Step 13.
577     // TODO: handle response's CSP list.
578 
579     // Step 14.
580     // Not applicable: request's cache mode is "no-store".
581 
582     // Step 15.
583     if let Some(cookies) = response.headers.get::<SetCookie>() {
584         let mut jar = http_state.cookie_jar.write().unwrap();
585         for cookie in &**cookies {
586             if let Some(cookie) = Cookie::from_cookie_string(cookie.clone(), url, CookieSource::HTTP) {
587                 jar.push(cookie, url, CookieSource::HTTP);
588             }
589         }
590     }
591 
592     // Step 16.
593     // Not applicable: correct WebSocket responses don't have a body.
594 
595     // Step 17.
596     Ok(response)
597 }
598 
make_request(mut stream: Stream, url: &ServoUrl, headers: &Headers) -> Result<Response, NetworkError>599 fn make_request(mut stream: Stream,
600                 url: &ServoUrl,
601                 headers: &Headers)
602                 -> Result<Response, NetworkError> {
603     write_request(&mut stream, url, headers).map_err(|e| {
604         NetworkError::Internal(format!("Request could not be sent: {}", e))
605     })?;
606 
607     // FIXME: Stream isn't supposed to be cloned.
608     let writer = stream.clone();
609 
610     // FIXME: BufReader from hyper isn't supposed to be used.
611     let mut reader = BufReader::new(stream);
612 
613     let head = parse_response(&mut reader).map_err(|e| {
614         NetworkError::Internal(format!("Response could not be read: {}", e))
615     })?;
616 
617     // This isn't in the spec, but this is the correct thing to do for WebSocket requests.
618     if head.version != HttpVersion::Http11 {
619         return Err(NetworkError::Internal("Response's HTTP version should be HTTP/1.1.".into()));
620     }
621 
622     // FIXME: StatusCode::from_u16 isn't supposed to be used.
623     let status = StatusCode::from_u16(head.subject.0);
624     Ok(Response {
625         status: status,
626         headers: head.headers,
627         reader: reader,
628         writer: writer,
629     })
630 }
631 
write_request(stream: &mut Stream, url: &ServoUrl, headers: &Headers) -> io::Result<()>632 fn write_request(stream: &mut Stream,
633                  url: &ServoUrl,
634                  headers: &Headers)
635                  -> io::Result<()> {
636     // Write "GET /foo/bar HTTP/1.1\r\n".
637     let method = Method::Get;
638     let request_uri = &url.as_url()[Position::BeforePath..Position::AfterQuery];
639     let version = HttpVersion::Http11;
640     write!(stream, "{} {} {}{}", method, request_uri, version, LINE_ENDING)?;
641 
642     // Write the headers.
643     write!(stream, "{}{}", headers, LINE_ENDING)
644 }
645