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