├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── ROADMAP.md ├── autobahn ├── client-results.json ├── fuzzingclient.json ├── fuzzingserver.json └── server-results.json ├── examples ├── async-autobahn-client.rs ├── async-autobahn-server.rs ├── async-client.rs ├── async-server.rs ├── autobahn-client.rs ├── autobahn-server.rs ├── client.rs ├── hyper.rs ├── parallel-server.rs ├── server.rs ├── ssl-client.rs └── websockets.html ├── scripts ├── autobahn-client.sh ├── autobahn-server.sh └── build-all.sh ├── src ├── client │ ├── builder.rs │ ├── mod.rs │ └── sync.rs ├── codec │ ├── http.rs │ └── mod.rs ├── header │ ├── accept.rs │ ├── extensions.rs │ ├── key.rs │ ├── mod.rs │ ├── origin.rs │ ├── protocol.rs │ └── version.rs ├── lib.rs ├── receiver.rs ├── result.rs ├── sender.rs └── server │ ├── mod.rs │ ├── sync.rs │ └── upgrade │ ├── mod.rs │ └── sync.rs └── websocket-base ├── Cargo.toml ├── LICENSE └── src ├── codec ├── mod.rs └── ws.rs ├── dataframe.rs ├── header.rs ├── lib.rs ├── message.rs ├── result.rs ├── stream.rs └── ws ├── dataframe.rs ├── message.rs ├── mod.rs ├── receiver.rs ├── sender.rs └── util ├── header.rs ├── mask.rs └── mod.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | 4 | # emacs 5 | *.#*.rs 6 | 7 | # Windows image file caches 8 | Thumbs.db 9 | ehthumbs.db 10 | 11 | # Folder config file 12 | Desktop.ini 13 | 14 | # Recycle Bin used on file shares 15 | $RECYCLE.BIN/ 16 | 17 | # Windows Installer files 18 | *.cab 19 | *.msi 20 | *.msm 21 | *.msp 22 | 23 | # ========================= 24 | # Operating System Files 25 | # ========================= 26 | 27 | # OSX 28 | # ========================= 29 | 30 | .DS_Store 31 | .AppleDouble 32 | .LSOverride 33 | 34 | # Icon must end with two \r 35 | Icon 36 | 37 | 38 | # Thumbnails 39 | ._* 40 | 41 | # Files that might appear on external disk 42 | .Spotlight-V100 43 | .Trashes 44 | 45 | # Directories potentially created on remote AFP share 46 | .AppleDB 47 | .AppleDesktop 48 | Network Trash Folder 49 | Temporary Items 50 | 51 | # Autobahn Output 52 | autobahn/client 53 | autobahn/server 54 | 55 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: nightly-2019-11-04 3 | 4 | before_script: 5 | - export PATH="$PATH:$HOME/.cargo/bin" 6 | 7 | install: 8 | - rustup component add rustfmt 9 | - rustup component add clippy 10 | script: 11 | - cargo fmt -- --version 12 | - cargo fmt -- --check 13 | - cargo clippy --all-features -- 14 | - ./scripts/build-all.sh 15 | - cargo test --features nightly 16 | - cargo bench --features nightly 17 | 18 | after_success: 19 | - sudo apt-get install python-unittest2 20 | - sudo pip install ghp-import urllib3[secure] autobahntestsuite 21 | - echo "Running Autobahn TestSuite for client" && ./scripts/autobahn-client.sh 22 | - echo "Running Autobahn TestSuite for server" && ./scripts/autobahn-server.sh 23 | - > 24 | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && { 25 | echo "Building docs and gh-pages" ; 26 | PROJECT_VERSION=$(cargo doc --features nightly | grep "Documenting websocket v" | sed 's/.*Documenting websocket v\(.*\) .*/\1/') ; 27 | curl -sL https://github.com/${TRAVIS_REPO_SLUG}/archive/html.tar.gz | tar xz ; 28 | cd ./rust-websocket-html && 29 | find . -type f | xargs sed -i 's//'"${PROJECT_VERSION}"'/g' ; 30 | mv ../target/doc ./doc ; 31 | mv ../autobahn/server ./autobahn/server ; 32 | mv ../autobahn/client ./autobahn/client ; 33 | mv ./autobahn/server/index.json ./autobahn/server/index.temp && rm ./autobahn/server/*.json && mv ./autobahn/server/index.temp ./autobahn/server/index.json ; 34 | mv ./autobahn/client/index.json ./autobahn/client/index.temp && rm ./autobahn/client/*.json && mv ./autobahn/client/index.temp ./autobahn/client/index.json ; 35 | cd ../ ; } 36 | - > 37 | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && { 38 | echo "Pushing gh-pages" ; 39 | ghp-import -n ./rust-websocket-html -m "Generated by Travis CI build ${TRAVIS_BUILD_NUMBER} for commit ${TRAVIS_COMMIT}" && 40 | git push -fq https://${TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages ; } 41 | 42 | env: 43 | global: 44 | secure: "g79arUER26fJvQu5/e/KvPl8jgnOv+LYD64x0PEZzRY7x+Bo0F45gjDTUG40AEdeh4upxT6twnSo6y+/v8V71NY0b+lPM2q3pS4KicQaDSLfigR1ogMo5A+Iv6l3shRFJhMiDapO3OlcvI2i+U6Er7sUcqIkZaUbbohGI1/0DtE=" 45 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "websocket" 3 | version = "0.26.2" 4 | authors = ["cyderize ", "Michael Eden "] 5 | edition = "2018" 6 | 7 | description = "A WebSocket (RFC6455) library for Rust." 8 | 9 | documentation = "https://docs.rs/websocket/" 10 | repository = "https://github.com/websockets-rs/rust-websocket" 11 | 12 | readme = "README.md" 13 | 14 | keywords = ["websocket", "websockets", "rfc6455", "async", "tokio"] 15 | categories = ["asynchronous", "network-programming", "web-programming", "web-programming::websocket"] 16 | 17 | license = "MIT" 18 | 19 | [dependencies] 20 | hyper = "^0.10.9" 21 | unicase = "1.0" 22 | url = "2.2.2" 23 | rand = "0.8.3" 24 | websocket-base = { path = "websocket-base", version="0.26.0", default-features=false } 25 | 26 | [dev-dependencies] 27 | futures-cpupool = "0.1" 28 | 29 | [dev-dependencies.tokio] 30 | version = "0.1" 31 | default-features = false 32 | features = ["codec", "tcp", "rt-full"] 33 | 34 | [workspace] 35 | members = [ 36 | "websocket-base" 37 | ] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Rust Websockets Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rust-WebSocket [![Build Status](https://travis-ci.com/websockets-rs/rust-websocket.svg?branch=master)](https://travis-ci.com/websockets-rs/rust-websocket) [![docs.rs](https://docs.rs/websocket/badge.svg)](https://docs.rs/websocket) 2 | ============== 3 | 4 | 5 | |**Note: Maintainership of this project is slugglish. You may want to use [tungstenite](https://crates.io/crates/tungstenite) or [tokio-tungstenite](https://crates.io/crates/tokio-tungstenite) instead.**| 6 | |----| 7 | 8 | 9 | Rust-WebSocket is a WebSocket ([RFC6455](https://datatracker.ietf.org/doc/rfc6455/)) library written in Rust. 10 | 11 | Rust-WebSocket provides a framework for dealing with WebSocket connections (both clients and servers). The library is currently in an experimental state, but provides functionality for both normal and secure WebSockets, a message level API supporting fragmentation, a data frame level API, and the ability to extend and customize behaviour. 12 | 13 | ## Installation 14 | 15 | To add a library release version from [crates.io](https://crates.io/crates/websocket) to a Cargo project, add this to the 'dependencies' section of your Cargo.toml: 16 | 17 | ```INI 18 | websocket = "0.24.0" 19 | ``` 20 | 21 | To add the library's Git repository to a Cargo project, add this to your Cargo.toml: 22 | 23 | ```INI 24 | [dependencies.websocket] 25 | 26 | git = "https://github.com/websockets-rs/rust-websocket.git" 27 | ``` 28 | 29 | Optionally add ```extern crate websocket;``` to your project. 30 | 31 | Note that `0.24.0` is the last version of `rust-websocket` that supports some very old Rust versions (I'm not sure which exactly, maybe 1.28). 32 | 33 | ## Usage 34 | 35 | The library can be compiled with tests and benches and some extra capabilities on Rust nightly. To enable the nightly features, use `cargo --features nightly ...`. 36 | 37 | See the documentation for the latest release of the library [here](https://docs.rs/websocket/), and also the examples, which are located in `/examples` and can be run with: 38 | 39 | ``` 40 | cargo run --example server 41 | ``` 42 | 43 | And in a separate terminal: 44 | 45 | ``` 46 | cargo run --example client 47 | ``` 48 | 49 | ## Testing 50 | 51 | The library can be tested using `cargo test` to run tests and `cargo bench` to run bench tests. 52 | 53 | A number of tests are included, which ensure core WebSocket functionality works as expected. These tests are not yet comprehensive, and are still being worked on. 54 | 55 | ## Autobahn TestSuite 56 | 57 | Rust-WebSocket uses the [Autobahn TestSuite](https://crossbar.io/autobahn/) to test conformance to RFC6455. If you have Autobahn TestSuite installed you can run these tests yourself using the commands: 58 | 59 | ``` 60 | wstest -m fuzzingserver 61 | cargo run --example autobahn-client 62 | ``` 63 | 64 | To test the client implementation, and 65 | 66 | ``` 67 | wstest -m fuzzingclient 68 | cargo run --example autobahn-server 69 | ``` 70 | 71 | To test the server implementation. The spec files are available [here](http://websockets-rs.github.io/rust-websocket/autobahn). 72 | 73 | The results of these tests are available [here](http://websockets-rs.github.io/rust-websocket/autobahn). 74 | 75 | ## Contributing 76 | 77 | Before you make a PR be sure to run all the tests! 78 | 79 | ```bash 80 | # install 81 | rustup component add rustfmt-preview 82 | rustup component add clippy-preview 83 | 84 | # test 85 | cargo +nightly fmt -- --check 86 | cargo +nightly clippy --all-features -- -D clippy::all 87 | cargo test --features nightly 88 | cargo bench --features nightly 89 | ./scripts/build-all.sh 90 | ``` 91 | 92 | ## License 93 | 94 | ### The MIT License (MIT) 95 | 96 | Copyright (c) 2014-2015 Cyderize 97 | 98 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 99 | 100 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 101 | 102 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 103 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # The Roadmap 2 | 3 | ## More Docs, Examples and Tests 4 | 5 | Easy as that, every method should be tested and documented. 6 | Every use-case should have an example. 7 | 8 | ## Adding Features 9 | 10 | ### `net2` Feature 11 | 12 | This is a feature to add the `net2` crate which will let us do cool things 13 | like set the option `SO_REUSEADDR` and similar when making TCP connections. 14 | 15 | This is discussed in [vi/rust-websocket#2](https://github.com/vi/rust-websocket/pull/2). 16 | 17 | ### Add Mio & Tokio (Evented Websocket) 18 | 19 | There are a lot of issues that would be solved if this was evented, such as: 20 | 21 | - [#88 tokio support](https://github.com/cyderize/rust-websocket/issues/88) 22 | - [#66 Timeout on recv_message](https://github.com/cyderize/rust-websocket/issues/66) 23 | - [#6 one client, one thread?](https://github.com/cyderize/rust-websocket/issues/6) 24 | 25 | So maybe we should _just_ add `tokio` support, or maybe `mio` is still used and popular. 26 | 27 | ### Support Permessage-Deflate 28 | 29 | We need this to pass more autobahn tests! 30 | 31 | ### Buffer Reads and Writes 32 | 33 | In the old crate the stream was split up into a reader and writer stream so you could 34 | have both a `BufReader` and a `BufWriter` to buffer your operations to gain some speed. 35 | However is doesn't make sense to split the stream up anymore 36 | (see [#83](https://github.com/cyderize/rust-websocket/issues/83)) 37 | meaning that we should buffer reads and writes in some other way. 38 | 39 | Some work has begun on this, like [#91](https://github.com/cyderize/rust-websocket/pull/91), 40 | but is this enough? And what about writing? 41 | 42 | -------------------------------------------------------------------------------- /autobahn/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | { 2 | "outdir": "./autobahn/server", 3 | "servers": [ 4 | { 5 | "agent": "rust-websocket", 6 | "url": "ws://127.0.0.1:9002" 7 | } 8 | ], 9 | "cases": ["*"], 10 | "exclude-cases": [], 11 | "exclude-agent-cases": {} 12 | } -------------------------------------------------------------------------------- /autobahn/fuzzingserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "ws://127.0.0.1:9001", 3 | "outdir": "./autobahn/client", 4 | "cases": ["*"], 5 | "exclude-cases": [], 6 | "exclude-agent-cases": {} 7 | } -------------------------------------------------------------------------------- /examples/async-autobahn-client.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use futures::future::{self, Loop}; 6 | use futures::sink::Sink; 7 | use futures::stream::Stream; 8 | use futures::Future; 9 | use tokio::runtime::current_thread::Runtime; 10 | use websocket::result::WebSocketError; 11 | use websocket::{ClientBuilder, OwnedMessage}; 12 | 13 | fn main() { 14 | let addr = "ws://127.0.0.1:9001".to_string(); 15 | let agent = "rust-websocket"; 16 | let mut runtime = tokio::runtime::current_thread::Builder::new() 17 | .build() 18 | .unwrap(); 19 | 20 | println!("Using fuzzingserver {}", addr); 21 | println!("Using agent {}", agent); 22 | 23 | let case_count = get_case_count(addr.clone(), &mut runtime); 24 | println!("We will be running {} test cases!", case_count); 25 | 26 | println!("Running test suite..."); 27 | for case_id in 1..(case_count + 1) { 28 | let url = addr.clone() + "/runCase?case=" + &case_id.to_string()[..] + "&agent=" + agent; 29 | 30 | let test_case = ClientBuilder::new(&url) 31 | .unwrap() 32 | .async_connect_insecure() 33 | .and_then(|(duplex, _)| { 34 | println!("Executing test case: {}/{}", case_id, case_count); 35 | future::loop_fn(duplex, |stream| { 36 | stream 37 | .into_future() 38 | .or_else(|(err, stream)| { 39 | println!("Could not receive message: {:?}", err); 40 | stream.send(OwnedMessage::Close(None)).map(|s| (None, s)) 41 | }) 42 | .and_then(|(msg, stream)| -> Box> { 43 | match msg { 44 | Some(OwnedMessage::Text(txt)) => Box::new( 45 | stream 46 | .send(OwnedMessage::Text(txt)) 47 | .map(|s| Loop::Continue(s)), 48 | ), 49 | Some(OwnedMessage::Binary(bin)) => Box::new( 50 | stream 51 | .send(OwnedMessage::Binary(bin)) 52 | .map(|s| Loop::Continue(s)), 53 | ), 54 | Some(OwnedMessage::Ping(data)) => Box::new( 55 | stream 56 | .send(OwnedMessage::Pong(data)) 57 | .map(|s| Loop::Continue(s)), 58 | ), 59 | Some(OwnedMessage::Close(_)) => Box::new( 60 | stream 61 | .send(OwnedMessage::Close(None)) 62 | .map(|_| Loop::Break(())), 63 | ), 64 | Some(OwnedMessage::Pong(_)) => { 65 | Box::new(future::ok(Loop::Continue(stream))) 66 | } 67 | None => Box::new(future::ok(Loop::Break(()))), 68 | } 69 | }) 70 | }) 71 | }) 72 | .map(|_| { 73 | println!("Test case {} is finished!", case_id); 74 | }) 75 | .or_else(|err| { 76 | println!("Test case {} ended with an error: {:?}", case_id, err); 77 | Ok(()) as Result<(), ()> 78 | }); 79 | 80 | runtime.block_on(test_case).ok(); 81 | } 82 | 83 | update_reports(addr.clone(), agent, &mut runtime); 84 | println!("Test suite finished!"); 85 | } 86 | 87 | fn get_case_count(addr: String, runtime: &mut Runtime) -> usize { 88 | let url = addr + "/getCaseCount"; 89 | let err = "Unsupported message in /getCaseCount"; 90 | 91 | let counter = ClientBuilder::new(&url) 92 | .unwrap() 93 | .async_connect_insecure() 94 | .and_then(|(s, _)| s.into_future().map_err(|e| e.0)) 95 | .and_then(|(msg, _)| match msg { 96 | Some(OwnedMessage::Text(txt)) => Ok(txt.parse().unwrap()), 97 | _ => Err(WebSocketError::ProtocolError(err)), 98 | }); 99 | runtime.block_on(counter).unwrap() 100 | } 101 | 102 | fn update_reports(addr: String, agent: &str, runtime: &mut Runtime) { 103 | println!("Updating reports..."); 104 | let url = addr + "/updateReports?agent=" + agent; 105 | 106 | let updater = ClientBuilder::new(&url) 107 | .unwrap() 108 | .async_connect_insecure() 109 | .and_then(|(sink, _)| sink.send(OwnedMessage::Close(None))); 110 | runtime.block_on(updater).unwrap(); 111 | 112 | println!("Reports updated."); 113 | } 114 | -------------------------------------------------------------------------------- /examples/async-autobahn-server.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use websocket::message::OwnedMessage; 6 | use websocket::r#async::Server; 7 | use websocket::server::InvalidConnection; 8 | 9 | use futures::{Future, Sink, Stream}; 10 | 11 | fn main() { 12 | let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 13 | let executor = runtime.executor(); 14 | // bind to the server 15 | let server = Server::bind("127.0.0.1:9002", &tokio::reactor::Handle::default()).unwrap(); 16 | 17 | // time to build the server's future 18 | // this will be a struct containing everything the server is going to do 19 | 20 | // a stream of incoming connections 21 | let f = server 22 | .incoming() 23 | // we don't wanna save the stream if it drops 24 | .map_err(|InvalidConnection { error, .. }| error) 25 | .for_each(move |(upgrade, addr)| { 26 | // accept the request to be a ws connection 27 | println!("Got a connection from: {}", addr); 28 | let f = upgrade.accept().and_then(|(s, _)| { 29 | // simple echo server impl 30 | let (sink, stream) = s.split(); 31 | stream 32 | .take_while(|m| Ok(!m.is_close())) 33 | .filter_map(|m| match m { 34 | OwnedMessage::Ping(p) => Some(OwnedMessage::Pong(p)), 35 | OwnedMessage::Pong(_) => None, 36 | _ => Some(m), 37 | }) 38 | .forward(sink) 39 | .and_then(|(_, sink)| sink.send(OwnedMessage::Close(None))) 40 | }); 41 | 42 | executor.spawn( 43 | f.map_err(move |e| println!("{}: '{:?}'", addr, e)) 44 | .map(move |_| println!("{} closed.", addr)), 45 | ); 46 | Ok(()) 47 | }); 48 | 49 | runtime.block_on(f).unwrap(); 50 | } 51 | -------------------------------------------------------------------------------- /examples/async-client.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use futures::future::Future; 6 | use futures::sink::Sink; 7 | use futures::stream::Stream; 8 | use futures::sync::mpsc; 9 | use std::io::stdin; 10 | use std::thread; 11 | use websocket::result::WebSocketError; 12 | use websocket::{ClientBuilder, OwnedMessage}; 13 | 14 | const CONNECTION: &'static str = "ws://127.0.0.1:2794"; 15 | 16 | // Async websocket chat client 17 | fn main() { 18 | println!("Connecting to {}", CONNECTION); 19 | 20 | // Construct new Tokio runtime environment 21 | let mut runtime = tokio::runtime::current_thread::Builder::new() 22 | .build() 23 | .unwrap(); 24 | 25 | let (usr_msg, stdin_ch) = mpsc::channel(0); 26 | 27 | // Spawn new thread to read user input 28 | // stdin isn't supported in mio yet, so we use a thread 29 | // see https://github.com/carllerche/mio/issues/321 30 | thread::spawn(|| { 31 | let mut input = String::new(); 32 | let mut stdin_sink = usr_msg.wait(); 33 | loop { 34 | // Read user input from stdin 35 | input.clear(); 36 | stdin().read_line(&mut input).unwrap(); 37 | 38 | // Trim whitespace and match input to known chat commands 39 | // If input is unknown, send trimmed input as a chat message 40 | let trimmed = input.trim(); 41 | let (close, msg) = match trimmed { 42 | "/close" => (true, OwnedMessage::Close(None)), 43 | "/ping" => (false, OwnedMessage::Ping(b"PING".to_vec())), 44 | _ => (false, OwnedMessage::Text(trimmed.to_string())), 45 | }; 46 | // Send message to websocket server 47 | stdin_sink 48 | .send(msg) 49 | .expect("Sending message across stdin channel."); 50 | // If user entered the "/close" command, break the loop 51 | if close { 52 | break; 53 | } 54 | } 55 | }); 56 | 57 | // Construct a new connection to the websocket server 58 | let runner = ClientBuilder::new(CONNECTION) 59 | .unwrap() 60 | .add_protocol("rust-websocket") 61 | .async_connect_insecure() 62 | .and_then(|(duplex, _)| { 63 | let (sink, stream) = duplex.split(); 64 | stream 65 | // Iterate over message as they arrive in stream 66 | .filter_map(|message| { 67 | println!("Received Message: {:?}", message); 68 | // Respond to close or ping commands from the server 69 | match message { 70 | OwnedMessage::Ping(d) => Some(OwnedMessage::Pong(d)), 71 | _ => None, 72 | } 73 | }) 74 | // Takes in messages from both sinks 75 | .select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable)) 76 | // Return a future that completes once all incoming data from the above streams has been processed into the sink 77 | .forward(sink) 78 | }); 79 | // Start our websocket client runner in the Tokio environment 80 | let _ = runtime.block_on(runner).unwrap(); 81 | } 82 | -------------------------------------------------------------------------------- /examples/async-server.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use std::fmt::Debug; 6 | 7 | use websocket::message::{Message, OwnedMessage}; 8 | use websocket::r#async::Server; 9 | use websocket::server::InvalidConnection; 10 | 11 | use futures::{future, Future, Sink, Stream}; 12 | use tokio::runtime::TaskExecutor; 13 | 14 | fn main() { 15 | let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 16 | let executor = runtime.executor(); 17 | // bind to the server 18 | let server = Server::bind("127.0.0.1:2794", &tokio::reactor::Handle::default()).unwrap(); 19 | 20 | // time to build the server's future 21 | // this will be a struct containing everything the server is going to do 22 | 23 | // a stream of incoming connections 24 | let f = server 25 | .incoming() 26 | .then(future::ok) // wrap good and bad events into future::ok 27 | .filter(|event| { 28 | match event { 29 | Ok(_) => true, // a good connection 30 | Err(InvalidConnection { ref error, .. }) => { 31 | println!("Bad client: {}", error); 32 | false // we want to save the stream if a client cannot make a valid handshake 33 | } 34 | } 35 | }) 36 | .and_then(|event| event) // unwrap good connections 37 | .map_err(|_| ()) // and silently ignore errors (in `.filter`) 38 | .for_each(move |(upgrade, addr)| { 39 | println!("Got a connection from: {}", addr); 40 | // check if it has the protocol we want 41 | if !upgrade.protocols().iter().any(|s| s == "rust-websocket") { 42 | // reject it if it doesn't 43 | spawn_future(upgrade.reject(), "Upgrade Rejection", &executor); 44 | return Ok(()); 45 | } 46 | 47 | // accept the request to be a ws connection if it does 48 | let f = upgrade 49 | .use_protocol("rust-websocket") 50 | .accept() 51 | // send a greeting! 52 | .and_then(|(s, _)| s.send(Message::text("Hello World!").into())) 53 | // simple echo server impl 54 | .and_then(|s| { 55 | let (sink, stream) = s.split(); 56 | stream 57 | .take_while(|m| Ok(!m.is_close())) 58 | .filter_map(|m| { 59 | println!("Message from Client: {:?}", m); 60 | match m { 61 | OwnedMessage::Ping(p) => Some(OwnedMessage::Pong(p)), 62 | OwnedMessage::Pong(_) => None, 63 | _ => Some(m), 64 | } 65 | }) 66 | .forward(sink) 67 | .and_then(|(_, sink)| sink.send(OwnedMessage::Close(None))) 68 | }); 69 | 70 | spawn_future(f, "Client Status", &executor); 71 | Ok(()) 72 | }); 73 | 74 | runtime.block_on(f).unwrap(); 75 | } 76 | 77 | fn spawn_future(f: F, desc: &'static str, executor: &TaskExecutor) 78 | where 79 | F: Future + 'static + Send, 80 | E: Debug, 81 | { 82 | executor.spawn( 83 | f.map_err(move |e| println!("{}: '{:?}'", desc, e)) 84 | .map(move |_| println!("{}: Finished.", desc)), 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /examples/autobahn-client.rs: -------------------------------------------------------------------------------- 1 | extern crate websocket; 2 | 3 | use websocket::ClientBuilder; 4 | use websocket::Message; 5 | use websocket::OwnedMessage; 6 | 7 | fn main() { 8 | let addr = "ws://127.0.0.1:9001".to_string(); 9 | let agent = "rust-websocket"; 10 | 11 | println!("Using fuzzingserver {}", addr); 12 | println!("Using agent {}", agent); 13 | 14 | println!("Running test suite..."); 15 | 16 | let mut current_case_id = 1; 17 | let case_count = get_case_count(addr.clone()); 18 | 19 | while current_case_id <= case_count { 20 | let case_id = current_case_id; 21 | current_case_id += 1; 22 | let url = addr.clone() + "/runCase?case=" + &case_id.to_string()[..] + "&agent=" + agent; 23 | 24 | let client = ClientBuilder::new(&url) 25 | .unwrap() 26 | .connect_insecure() 27 | .unwrap(); 28 | 29 | let (mut receiver, mut sender) = client.split().unwrap(); 30 | 31 | println!("Executing test case: {}/{}", case_id, case_count); 32 | 33 | for message in receiver.incoming_messages() { 34 | let message = match message { 35 | Ok(message) => message, 36 | Err(e) => { 37 | println!("Error: {:?}", e); 38 | let _ = sender.send_message(&Message::close()); 39 | break; 40 | } 41 | }; 42 | 43 | match message { 44 | OwnedMessage::Text(txt) => { 45 | sender.send_message(&OwnedMessage::Text(txt)).unwrap(); 46 | } 47 | OwnedMessage::Binary(bin) => { 48 | sender.send_message(&OwnedMessage::Binary(bin)).unwrap(); 49 | } 50 | OwnedMessage::Close(_) => { 51 | let _ = sender.send_message(&OwnedMessage::Close(None)); 52 | break; 53 | } 54 | OwnedMessage::Ping(data) => { 55 | sender.send_message(&OwnedMessage::Pong(data)).unwrap(); 56 | } 57 | _ => (), 58 | } 59 | } 60 | } 61 | 62 | update_reports(addr.clone(), agent); 63 | } 64 | 65 | fn get_case_count(addr: String) -> usize { 66 | let url = addr + "/getCaseCount"; 67 | 68 | let client = match ClientBuilder::new(&url).unwrap().connect_insecure() { 69 | Ok(c) => c, 70 | Err(e) => { 71 | println!("{:?}", e); 72 | return 0; 73 | } 74 | }; 75 | 76 | let (mut receiver, mut sender) = client.split().unwrap(); 77 | 78 | let mut count = 0; 79 | 80 | for message in receiver.incoming_messages() { 81 | let message = match message { 82 | Ok(message) => message, 83 | Err(e) => { 84 | println!("Error: {:?}", e); 85 | let _ = sender.send_message(&Message::close_because(1002, "".to_string())); 86 | break; 87 | } 88 | }; 89 | match message { 90 | OwnedMessage::Text(txt) => { 91 | count = txt.parse().unwrap(); 92 | println!("Will run {} cases...", count); 93 | } 94 | OwnedMessage::Close(_) => { 95 | let _ = sender.send_message(&Message::close()); 96 | break; 97 | } 98 | OwnedMessage::Ping(data) => { 99 | sender.send_message(&OwnedMessage::Pong(data)).unwrap(); 100 | } 101 | _ => (), 102 | } 103 | } 104 | 105 | count 106 | } 107 | 108 | fn update_reports(addr: String, agent: &str) { 109 | let url = addr + "/updateReports?agent=" + agent; 110 | 111 | let client = match ClientBuilder::new(&url).unwrap().connect_insecure() { 112 | Ok(c) => c, 113 | Err(e) => { 114 | println!("{:?}", e); 115 | return; 116 | } 117 | }; 118 | 119 | let (mut receiver, mut sender) = client.split().unwrap(); 120 | 121 | println!("Updating reports..."); 122 | 123 | for message in receiver.incoming_messages() { 124 | let message = match message { 125 | Ok(message) => message, 126 | Err(e) => { 127 | println!("Error: {:?}", e); 128 | let _ = sender.send_message(&Message::close()); 129 | return; 130 | } 131 | }; 132 | match message { 133 | OwnedMessage::Close(_) => { 134 | let _ = sender.send_message(&Message::close()); 135 | println!("Reports updated."); 136 | println!("Test suite finished!"); 137 | return; 138 | } 139 | OwnedMessage::Ping(data) => { 140 | sender.send_message(&OwnedMessage::Pong(data)).unwrap(); 141 | } 142 | _ => (), 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /examples/autobahn-server.rs: -------------------------------------------------------------------------------- 1 | extern crate websocket; 2 | 3 | use std::thread; 4 | use websocket::sync::Server; 5 | use websocket::{Message, OwnedMessage}; 6 | 7 | fn main() { 8 | let server = Server::bind("127.0.0.1:9002").unwrap(); 9 | 10 | for connection in server.filter_map(Result::ok) { 11 | thread::spawn(|| { 12 | let client = connection.accept().unwrap(); 13 | 14 | let (mut receiver, mut sender) = client.split().unwrap(); 15 | 16 | for message in receiver.incoming_messages() { 17 | let message = match message { 18 | Ok(message) => message, 19 | Err(e) => { 20 | println!("{:?}", e); 21 | let _ = sender.send_message(&Message::close()); 22 | return; 23 | } 24 | }; 25 | 26 | match message { 27 | OwnedMessage::Text(txt) => { 28 | sender.send_message(&OwnedMessage::Text(txt)).unwrap() 29 | } 30 | OwnedMessage::Binary(bin) => { 31 | sender.send_message(&OwnedMessage::Binary(bin)).unwrap() 32 | } 33 | OwnedMessage::Close(_) => { 34 | sender.send_message(&OwnedMessage::Close(None)).ok(); 35 | return; 36 | } 37 | OwnedMessage::Ping(data) => { 38 | sender.send_message(&OwnedMessage::Pong(data)).unwrap(); 39 | } 40 | _ => (), 41 | } 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/client.rs: -------------------------------------------------------------------------------- 1 | extern crate websocket; 2 | 3 | use std::io::stdin; 4 | use std::sync::mpsc::channel; 5 | use std::thread; 6 | 7 | use websocket::client::ClientBuilder; 8 | use websocket::{Message, OwnedMessage}; 9 | 10 | const CONNECTION: &'static str = "ws://127.0.0.1:2794"; 11 | 12 | fn main() { 13 | println!("Connecting to {}", CONNECTION); 14 | 15 | let client = ClientBuilder::new(CONNECTION) 16 | .unwrap() 17 | .add_protocol("rust-websocket") 18 | .connect_insecure() 19 | .unwrap(); 20 | 21 | println!("Successfully connected"); 22 | 23 | let (mut receiver, mut sender) = client.split().unwrap(); 24 | 25 | let (tx, rx) = channel(); 26 | 27 | let tx_1 = tx.clone(); 28 | 29 | let send_loop = thread::spawn(move || { 30 | loop { 31 | // Send loop 32 | let message = match rx.recv() { 33 | Ok(m) => m, 34 | Err(e) => { 35 | println!("Send Loop: {:?}", e); 36 | return; 37 | } 38 | }; 39 | match message { 40 | OwnedMessage::Close(_) => { 41 | let _ = sender.send_message(&message); 42 | // If it's a close message, just send it and then return. 43 | return; 44 | } 45 | _ => (), 46 | } 47 | // Send the message 48 | match sender.send_message(&message) { 49 | Ok(()) => (), 50 | Err(e) => { 51 | println!("Send Loop: {:?}", e); 52 | let _ = sender.send_message(&Message::close()); 53 | return; 54 | } 55 | } 56 | } 57 | }); 58 | 59 | let receive_loop = thread::spawn(move || { 60 | // Receive loop 61 | for message in receiver.incoming_messages() { 62 | let message = match message { 63 | Ok(m) => m, 64 | Err(e) => { 65 | println!("Receive Loop: {:?}", e); 66 | let _ = tx_1.send(OwnedMessage::Close(None)); 67 | return; 68 | } 69 | }; 70 | match message { 71 | OwnedMessage::Close(_) => { 72 | // Got a close message, so send a close message and return 73 | let _ = tx_1.send(OwnedMessage::Close(None)); 74 | return; 75 | } 76 | OwnedMessage::Ping(data) => { 77 | match tx_1.send(OwnedMessage::Pong(data)) { 78 | // Send a pong in response 79 | Ok(()) => (), 80 | Err(e) => { 81 | println!("Receive Loop: {:?}", e); 82 | return; 83 | } 84 | } 85 | } 86 | // Say what we received 87 | _ => println!("Receive Loop: {:?}", message), 88 | } 89 | } 90 | }); 91 | 92 | loop { 93 | let mut input = String::new(); 94 | 95 | stdin().read_line(&mut input).unwrap(); 96 | 97 | let trimmed = input.trim(); 98 | 99 | let message = match trimmed { 100 | "/close" => { 101 | // Close the connection 102 | let _ = tx.send(OwnedMessage::Close(None)); 103 | break; 104 | } 105 | // Send a ping 106 | "/ping" => OwnedMessage::Ping(b"PING".to_vec()), 107 | // Otherwise, just send text 108 | _ => OwnedMessage::Text(trimmed.to_string()), 109 | }; 110 | 111 | match tx.send(message) { 112 | Ok(()) => (), 113 | Err(e) => { 114 | println!("Main Loop: {:?}", e); 115 | break; 116 | } 117 | } 118 | } 119 | 120 | // We're exiting 121 | 122 | println!("Waiting for child threads to exit"); 123 | 124 | let _ = send_loop.join(); 125 | let _ = receive_loop.join(); 126 | 127 | println!("Exited"); 128 | } 129 | -------------------------------------------------------------------------------- /examples/hyper.rs: -------------------------------------------------------------------------------- 1 | extern crate hyper; 2 | extern crate websocket; 3 | 4 | use hyper::net::Fresh; 5 | use hyper::server::request::Request; 6 | use hyper::server::response::Response; 7 | use hyper::Server as HttpServer; 8 | use std::io::Write; 9 | use std::thread; 10 | use websocket::sync::Server; 11 | use websocket::{Message, OwnedMessage}; 12 | 13 | const HTML: &'static str = include_str!("websockets.html"); 14 | 15 | // The HTTP server handler 16 | fn http_handler(_: Request, response: Response) { 17 | let mut response = response.start().unwrap(); 18 | // Send a client webpage 19 | response.write_all(HTML.as_bytes()).unwrap(); 20 | response.end().unwrap(); 21 | } 22 | 23 | fn main() { 24 | // Start listening for http connections 25 | thread::spawn(|| { 26 | let http_server = HttpServer::http("127.0.0.1:8080").unwrap(); 27 | http_server.handle(http_handler).unwrap(); 28 | }); 29 | 30 | // Start listening for WebSocket connections 31 | let ws_server = Server::bind("127.0.0.1:2794").unwrap(); 32 | 33 | for connection in ws_server.filter_map(Result::ok) { 34 | // Spawn a new thread for each connection. 35 | thread::spawn(|| { 36 | if !connection 37 | .protocols() 38 | .contains(&"rust-websocket".to_string()) 39 | { 40 | connection.reject().unwrap(); 41 | return; 42 | } 43 | 44 | let mut client = connection.use_protocol("rust-websocket").accept().unwrap(); 45 | 46 | let ip = client.peer_addr().unwrap(); 47 | 48 | println!("Connection from {}", ip); 49 | 50 | let message = Message::text("Hello"); 51 | client.send_message(&message).unwrap(); 52 | 53 | let (mut receiver, mut sender) = client.split().unwrap(); 54 | 55 | for message in receiver.incoming_messages() { 56 | let message = message.unwrap(); 57 | 58 | match message { 59 | OwnedMessage::Close(_) => { 60 | let message = Message::close(); 61 | sender.send_message(&message).unwrap(); 62 | println!("Client {} disconnected", ip); 63 | return; 64 | } 65 | OwnedMessage::Ping(data) => { 66 | let message = Message::pong(data); 67 | sender.send_message(&message).unwrap(); 68 | } 69 | _ => sender.send_message(&message).unwrap(), 70 | } 71 | } 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/parallel-server.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use websocket::message::OwnedMessage; 6 | use websocket::r#async::Server; 7 | use websocket::server::InvalidConnection; 8 | 9 | use tokio::runtime; 10 | use tokio::runtime::TaskExecutor; 11 | 12 | use futures::future::{self, Loop}; 13 | use futures::sync::mpsc; 14 | use futures::{Future, Sink, Stream}; 15 | 16 | use std::collections::HashMap; 17 | use std::fmt::Debug; 18 | use std::sync::{Arc, RwLock}; 19 | use std::time::{Duration, Instant}; 20 | 21 | type Id = u32; 22 | 23 | fn main() { 24 | let runtime = runtime::Builder::new().build().unwrap(); 25 | let executor = runtime.executor(); 26 | let server = Server::bind("127.0.0.1:8081", &tokio::reactor::Handle::default()) 27 | .expect("Failed to create server"); 28 | let connections = Arc::new(RwLock::new(HashMap::new())); 29 | let (receive_channel_out, receive_channel_in) = mpsc::unbounded(); 30 | let conn_id = Arc::new(RwLock::new(Counter::new())); 31 | let connections_inner = connections.clone(); 32 | // Handle new connection 33 | let connection_handler = server 34 | .incoming() 35 | // we don't wanna save the stream if it drops 36 | .map_err(|InvalidConnection { error, .. }| error) 37 | .for_each(move |(upgrade, addr)| { 38 | let connections_inner = connections_inner.clone(); 39 | println!("Got a connection from: {}", addr); 40 | let channel = receive_channel_out.clone(); 41 | let executor_inner = executor.clone(); 42 | let conn_id = conn_id.clone(); 43 | let f = upgrade.accept().and_then(move |(framed, _)| { 44 | let id = conn_id 45 | .write() 46 | .unwrap() 47 | .next() 48 | .expect("maximum amount of ids reached"); 49 | let (sink, stream) = framed.split(); 50 | let f = channel.send((id, stream)); 51 | spawn_future(f, "Send stream to connection pool", &executor_inner); 52 | connections_inner.write().unwrap().insert(id, sink); 53 | Ok(()) 54 | }); 55 | spawn_future(f, "Handle new connection", &executor); 56 | Ok(()) 57 | }) 58 | .map_err(|_| ()); 59 | 60 | // Handle receiving messages from a client 61 | let receive_handler = receive_channel_in.for_each(|(id, stream)| { 62 | stream 63 | .for_each(move |msg| { 64 | process_message(id, &msg); 65 | Ok(()) 66 | }) 67 | .map_err(|_| ()) 68 | }); 69 | 70 | let (send_channel_out, send_channel_in) = mpsc::unbounded(); 71 | 72 | // Handle sending messages to a client 73 | let connections_inner = connections.clone(); 74 | let executor = runtime.executor(); 75 | let executor_inner = executor.clone(); 76 | let send_handler = send_channel_in 77 | .for_each(move |(id, msg): (Id, String)| { 78 | let connections = connections_inner.clone(); 79 | let sink = connections 80 | .write() 81 | .unwrap() 82 | .remove(&id) 83 | .expect("Tried to send to invalid client id"); 84 | 85 | println!("Sending message '{}' to id {}", msg, id); 86 | let f = sink 87 | .send(OwnedMessage::Text(msg)) 88 | .and_then(move |sink| { 89 | connections.write().unwrap().insert(id, sink); 90 | Ok(()) 91 | }) 92 | .map_err(|_| ()); 93 | executor_inner.spawn(f); 94 | Ok(()) 95 | }) 96 | .map_err(|_| ()); 97 | 98 | // Main 'logic' loop 99 | let main_loop = future::loop_fn((), move |_| { 100 | let connections = connections.clone(); 101 | let send_channel_out = send_channel_out.clone(); 102 | let executor = executor.clone(); 103 | tokio::timer::Delay::new(Instant::now() + Duration::from_millis(100)) 104 | .map_err(|_| ()) 105 | .and_then(move |_| { 106 | let should_continue = update(connections, send_channel_out, &executor); 107 | match should_continue { 108 | Ok(true) => Ok(Loop::Continue(())), 109 | Ok(false) => Ok(Loop::Break(())), 110 | Err(()) => Err(()), 111 | } 112 | }) 113 | }); 114 | 115 | let handlers = 116 | main_loop.select2(connection_handler.select2(receive_handler.select(send_handler))); 117 | 118 | runtime 119 | .block_on_all(handlers) 120 | .map_err(|_| println!("Error while running core loop")) 121 | .unwrap(); 122 | } 123 | 124 | fn spawn_future(f: F, desc: &'static str, executor: &TaskExecutor) 125 | where 126 | F: Future + 'static + Send, 127 | E: Debug, 128 | { 129 | executor.spawn( 130 | f.map_err(move |e| println!("Error in {}: '{:?}'", desc, e)) 131 | .map(move |_| println!("{}: Finished.", desc)), 132 | ); 133 | } 134 | 135 | fn process_message(id: u32, msg: &OwnedMessage) { 136 | if let OwnedMessage::Text(ref txt) = *msg { 137 | println!("Received message '{}' from id {}", txt, id); 138 | } 139 | } 140 | 141 | type SinkContent = websocket::client::r#async::Framed< 142 | tokio::net::TcpStream, 143 | websocket::r#async::MessageCodec, 144 | >; 145 | type SplitSink = futures::stream::SplitSink; 146 | // Represents one tick in the main loop 147 | fn update( 148 | connections: Arc>>, 149 | channel: mpsc::UnboundedSender<(Id, String)>, 150 | executor: &TaskExecutor, 151 | ) -> Result { 152 | let executor_inner = executor.clone(); 153 | executor.spawn(futures::lazy(move || { 154 | for (id, _) in connections.read().unwrap().iter() { 155 | let f = channel.clone().send((*id, "Hi there!".to_owned())); 156 | spawn_future(f, "Send message to write handler", &executor_inner); 157 | } 158 | Ok(()) 159 | })); 160 | Ok(true) 161 | } 162 | 163 | struct Counter { 164 | count: Id, 165 | } 166 | impl Counter { 167 | fn new() -> Self { 168 | Counter { count: 0 } 169 | } 170 | } 171 | 172 | impl Iterator for Counter { 173 | type Item = Id; 174 | 175 | fn next(&mut self) -> Option { 176 | if self.count != Id::max_value() { 177 | self.count += 1; 178 | Some(self.count) 179 | } else { 180 | None 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /examples/server.rs: -------------------------------------------------------------------------------- 1 | extern crate websocket; 2 | 3 | use std::thread; 4 | use websocket::sync::Server; 5 | use websocket::OwnedMessage; 6 | 7 | fn main() { 8 | let server = Server::bind("127.0.0.1:2794").unwrap(); 9 | 10 | for request in server.filter_map(Result::ok) { 11 | // Spawn a new thread for each connection. 12 | thread::spawn(|| { 13 | if !request.protocols().contains(&"rust-websocket".to_string()) { 14 | request.reject().unwrap(); 15 | return; 16 | } 17 | 18 | let mut client = request.use_protocol("rust-websocket").accept().unwrap(); 19 | 20 | let ip = client.peer_addr().unwrap(); 21 | 22 | println!("Connection from {}", ip); 23 | 24 | let message = OwnedMessage::Text("Hello".to_string()); 25 | client.send_message(&message).unwrap(); 26 | 27 | let (mut receiver, mut sender) = client.split().unwrap(); 28 | 29 | for message in receiver.incoming_messages() { 30 | let message = message.unwrap(); 31 | 32 | match message { 33 | OwnedMessage::Close(_) => { 34 | let message = OwnedMessage::Close(None); 35 | sender.send_message(&message).unwrap(); 36 | println!("Client {} disconnected", ip); 37 | return; 38 | } 39 | OwnedMessage::Ping(ping) => { 40 | let message = OwnedMessage::Pong(ping); 41 | sender.send_message(&message).unwrap(); 42 | } 43 | _ => sender.send_message(&message).unwrap(), 44 | } 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/ssl-client.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio; 3 | extern crate websocket; 4 | 5 | use futures::future::Future; 6 | use futures::sink::Sink; 7 | use futures::stream::Stream; 8 | use futures::sync::mpsc; 9 | use std::io::stdin; 10 | use std::thread; 11 | use websocket::result::WebSocketError; 12 | use websocket::{ClientBuilder, OwnedMessage}; 13 | 14 | const CONNECTION: &'static str = "wss://echo.websocket.org"; 15 | 16 | fn main() { 17 | println!("Connecting to {}", CONNECTION); 18 | let mut runtime = tokio::runtime::current_thread::Builder::new() 19 | .build() 20 | .unwrap(); 21 | 22 | // standard in isn't supported in mio yet, so we use a thread 23 | // see https://github.com/carllerche/mio/issues/321 24 | let (usr_msg, stdin_ch) = mpsc::channel(0); 25 | thread::spawn(|| { 26 | let mut input = String::new(); 27 | let mut stdin_sink = usr_msg.wait(); 28 | loop { 29 | input.clear(); 30 | stdin().read_line(&mut input).unwrap(); 31 | let trimmed = input.trim(); 32 | 33 | let (close, msg) = match trimmed { 34 | "/close" => (true, OwnedMessage::Close(None)), 35 | "/ping" => (false, OwnedMessage::Ping(b"PING".to_vec())), 36 | _ => (false, OwnedMessage::Text(trimmed.to_string())), 37 | }; 38 | 39 | stdin_sink 40 | .send(msg) 41 | .expect("Sending message across stdin channel."); 42 | 43 | if close { 44 | break; 45 | } 46 | } 47 | }); 48 | 49 | let runner = ClientBuilder::new(CONNECTION) 50 | .unwrap() 51 | .async_connect_secure(None) 52 | .and_then(|(duplex, _)| { 53 | let (sink, stream) = duplex.split(); 54 | stream 55 | .filter_map(|message| { 56 | println!("Received Message: {:?}", message); 57 | match message { 58 | OwnedMessage::Close(e) => Some(OwnedMessage::Close(e)), 59 | OwnedMessage::Ping(d) => Some(OwnedMessage::Pong(d)), 60 | _ => None, 61 | } 62 | }) 63 | .select(stdin_ch.map_err(|_| WebSocketError::NoDataAvailable)) 64 | .forward(sink) 65 | }); 66 | let _ = runtime.block_on(runner).unwrap(); 67 | } 68 | -------------------------------------------------------------------------------- /examples/websockets.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebSocket Test 6 | 7 | 8 | 24 |

25 | Received Messages: 26 |

27 |
28 | 29 | 30 |
31 | 32 | -------------------------------------------------------------------------------- /scripts/autobahn-client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Author michael 3 | set -euo pipefail 4 | set -x 5 | SOURCE_DIR=$(readlink -f "${BASH_SOURCE[0]}") 6 | SOURCE_DIR=$(dirname "$SOURCE_DIR") 7 | cd "${SOURCE_DIR}/.." 8 | 9 | function cleanup() { 10 | kill -9 ${FUZZINGSERVER_PID} 11 | } 12 | trap cleanup TERM EXIT 13 | 14 | wstest -m fuzzingserver -s 'autobahn/fuzzingserver.json' & \ 15 | FUZZINGSERVER_PID=$! 16 | sleep 10 17 | 18 | function test_diff() { 19 | SAVED_RESULTS=$(sed 's/NON-STRICT/OK/g' autobahn/client-results.json) 20 | DIFF=$(diff \ 21 | <(jq -S 'del(."rust-websocket" | .. | .duration?)' "$SAVED_RESULTS") \ 22 | <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/client/index.json') ) 23 | 24 | if [[ $DIFF ]]; then 25 | echo 'Difference in results, either this is a regression or' \ 26 | 'one should update autobahn/client-results.json with the new results.' \ 27 | 'The results are:' 28 | echo $DIFF 29 | exit 64 30 | fi 31 | } 32 | 33 | cargo build --example autobahn-client 34 | cargo run --example autobahn-client 35 | test_diff 36 | 37 | cargo build --example async-autobahn-client 38 | cargo run --example async-autobahn-client 39 | test_diff 40 | 41 | -------------------------------------------------------------------------------- /scripts/autobahn-server.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Author michael 3 | set -euo pipefail 4 | set -x 5 | SOURCE_DIR=$(readlink -f "${BASH_SOURCE[0]}") 6 | SOURCE_DIR=$(dirname "$SOURCE_DIR") 7 | cd "${SOURCE_DIR}/.." 8 | WSSERVER_PID= 9 | 10 | function cleanup() { 11 | kill -9 ${WSSERVER_PID} 12 | } 13 | trap cleanup TERM EXIT 14 | 15 | function test_diff() { 16 | SAVED_RESULTS=$(sed 's/NON-STRICT/OK/g' autobahn/server-results.json) 17 | DIFF=$(diff \ 18 | <(jq -S 'del(."rust-websocket" | .. | .duration?)' "$SAVED_RESULTS") \ 19 | <(jq -S 'del(."rust-websocket" | .. | .duration?)' 'autobahn/server/index.json') ) 20 | 21 | if [[ $DIFF ]]; then 22 | echo Difference in results, either this is a regression or \ 23 | one should update autobahn/server-results.json with the new results. \ 24 | The results are: 25 | echo $DIFF 26 | exit 64 27 | fi 28 | } 29 | 30 | # Test synchronous version 31 | cargo build --example autobahn-server 32 | ./target/debug/examples/autobahn-server & WSSERVER_PID=$! 33 | echo "Server PID: ${WSSERVER_PID}" 34 | sleep 10 35 | wstest -m fuzzingclient -s 'autobahn/fuzzingclient.json' 36 | kill -9 ${WSSERVER_PID} 37 | test_diff 38 | 39 | # Test asynchronous version 40 | cargo build --example async-autobahn-server 41 | ./target/debug/examples/async-autobahn-server & WSSERVER_PID=$! 42 | echo "Server PID: ${WSSERVER_PID}" 43 | sleep 10 44 | wstest -m fuzzingclient -s 'autobahn/fuzzingclient.json' 45 | kill -9 ${WSSERVER_PID} 46 | test_diff 47 | 48 | -------------------------------------------------------------------------------- /scripts/build-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | SOURCE_DIR=$(readlink -f "${BASH_SOURCE[0]}") 4 | SOURCE_DIR=$(dirname "$SOURCE_DIR") 5 | cd "${SOURCE_DIR}/.." 6 | 7 | if [[ ${1:-} = "-i" ]]; then 8 | INTERACTIVE=true 9 | fi 10 | 11 | ALL_FEATURES=" 12 | async 13 | sync 14 | sync-ssl 15 | async-ssl 16 | async sync 17 | async sync-ssl 18 | sync async-ssl 19 | sync-ssl async-ssl" 20 | 21 | while read FEATS; do 22 | if [[ ${INTERACTIVE:-} ]]; then 23 | cargo build --no-default-features --features "$FEATS" \ 24 | --color always 2>&1 | less -r 25 | else 26 | set -x 27 | cargo build --no-default-features --features "$FEATS" 28 | set +x 29 | fi 30 | done < <(echo "$ALL_FEATURES") 31 | 32 | ## all combs of features (lol) 33 | # async 34 | # sync 35 | # sync-ssl 36 | # async-ssl 37 | # async sync 38 | # async sync-ssl 39 | # async async-ssl 40 | # sync sync-ssl 41 | # sync async-ssl 42 | # sync-ssl async-ssl 43 | # async sync sync-ssl 44 | # async sync async-ssl 45 | # async sync-ssl async-ssl 46 | # sync sync-ssl async-ssl 47 | # async sync sync-ssl async-ssl 48 | -------------------------------------------------------------------------------- /src/client/mod.rs: -------------------------------------------------------------------------------- 1 | //! Build and use synchronous websocket clients. 2 | //! 3 | //! In general pick a style you would like to write in and use `ClientBuilder` 4 | //! to create your websocket connections. Use the `.async_connect` functions to create 5 | //! async connections, and the normal `.connect` functions for synchronous clients. 6 | //! The `ClientBuilder` creates both async and sync connections, the actual sync and 7 | //! async clients live in the `client::sync` and `client::async` modules, respectively. 8 | //! 9 | //! Many of the useful things from this module will be hoisted and re-exported under the 10 | //! `websocket::{sync, async}::client` module which will have all sync or all async things. 11 | 12 | pub mod builder; 13 | pub use self::builder::{ClientBuilder, ParseError, Url}; 14 | 15 | 16 | pub mod sync; 17 | -------------------------------------------------------------------------------- /src/client/sync.rs: -------------------------------------------------------------------------------- 1 | //! Contains the WebSocket client. 2 | use hyper::buffer::BufReader; 3 | use hyper::header::Headers; 4 | use std::io::Result as IoResult; 5 | use std::io::{Read, Write}; 6 | use std::net::SocketAddr; 7 | use std::net::TcpStream; 8 | 9 | use crate::dataframe::DataFrame; 10 | use crate::header::extensions::Extension; 11 | use crate::header::{WebSocketExtensions, WebSocketProtocol}; 12 | use crate::message::OwnedMessage; 13 | use crate::result::WebSocketResult; 14 | use crate::stream::sync::{AsTcpStream, Shutdown, Splittable, Stream}; 15 | use crate::ws; 16 | use crate::ws::receiver::Receiver as ReceiverTrait; 17 | use crate::ws::receiver::{DataFrameIterator, MessageIterator}; 18 | use crate::ws::sender::Sender as SenderTrait; 19 | 20 | pub use crate::receiver::Reader; 21 | use crate::receiver::Receiver; 22 | use crate::sender::Sender; 23 | pub use crate::sender::Writer; 24 | use crate::ws::dataframe::DataFrame as DataFrameable; 25 | 26 | /// Represents a WebSocket client, which can send and receive messages/data frames. 27 | /// 28 | /// The client just wraps around a `Stream` (which is something that can be read from 29 | /// and written to) and handles the websocket protocol. TCP or SSL over TCP is common, 30 | /// but any stream can be used. 31 | /// 32 | /// A `Client` can also be split into a `Reader` and a `Writer` which can then be moved 33 | /// to different threads, often using a send loop and receiver loop concurrently, 34 | /// as shown in the client example in `examples/client.rs`. 35 | /// This is only possible for streams that implement the `Splittable` trait, which 36 | /// currently is only TCP streams. (it is unsafe to duplicate an SSL stream) 37 | /// 38 | ///# Connecting to a Server 39 | /// 40 | ///```no_run 41 | ///extern crate websocket; 42 | ///# fn main() { 43 | /// 44 | ///use websocket::{ClientBuilder, Message}; 45 | /// 46 | ///let mut client = ClientBuilder::new("ws://127.0.0.1:1234") 47 | /// .unwrap() 48 | /// .connect_insecure() 49 | /// .unwrap(); 50 | /// 51 | ///let message = Message::text("Hello, World!"); 52 | ///client.send_message(&message).unwrap(); // Send message 53 | ///# } 54 | ///``` 55 | pub struct Client 56 | where 57 | S: Stream, 58 | { 59 | stream: BufReader, 60 | headers: Headers, 61 | sender: Sender, 62 | receiver: Receiver, 63 | } 64 | 65 | impl Client { 66 | /// Shuts down the sending half of the client connection, will cause all pending 67 | /// and future IO to return immediately with an appropriate value. 68 | pub fn shutdown_sender(&self) -> IoResult<()> { 69 | self.stream.get_ref().as_tcp().shutdown(Shutdown::Write) 70 | } 71 | 72 | /// Shuts down the receiving half of the client connection, will cause all pending 73 | /// and future IO to return immediately with an appropriate value. 74 | pub fn shutdown_receiver(&self) -> IoResult<()> { 75 | self.stream.get_ref().as_tcp().shutdown(Shutdown::Read) 76 | } 77 | } 78 | 79 | impl Client 80 | where 81 | S: AsTcpStream + Stream, 82 | { 83 | /// Shuts down the client connection, will cause all pending and future IO to 84 | /// return immediately with an appropriate value. 85 | pub fn shutdown(&self) -> IoResult<()> { 86 | self.stream.get_ref().as_tcp().shutdown(Shutdown::Both) 87 | } 88 | 89 | /// See [`TcpStream::peer_addr`] 90 | /// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.peer_addr). 91 | pub fn peer_addr(&self) -> IoResult { 92 | self.stream.get_ref().as_tcp().peer_addr() 93 | } 94 | 95 | /// See [`TcpStream::local_addr`] 96 | /// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.local_addr). 97 | pub fn local_addr(&self) -> IoResult { 98 | self.stream.get_ref().as_tcp().local_addr() 99 | } 100 | 101 | /// See [`TcpStream::set_nodelay`] 102 | /// (https://doc.rust-lang.org/std/net/struct.TcpStream.html#method.set_nodelay). 103 | pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> { 104 | self.stream.get_ref().as_tcp().set_nodelay(nodelay) 105 | } 106 | 107 | /// Changes whether the stream is in nonblocking mode. 108 | pub fn set_nonblocking(&self, nonblocking: bool) -> IoResult<()> { 109 | self.stream.get_ref().as_tcp().set_nonblocking(nonblocking) 110 | } 111 | } 112 | 113 | impl Client 114 | where 115 | S: Stream, 116 | { 117 | /// Creates a Client from a given stream 118 | /// **without sending any handshake** this is meant to only be used with 119 | /// a stream that has a websocket connection already set up. 120 | /// If in doubt, don't use this! 121 | #[doc(hidden)] 122 | pub fn unchecked(stream: BufReader, headers: Headers, out_mask: bool) -> Self { 123 | Client { 124 | headers, 125 | stream, 126 | sender: Sender::new(out_mask), // true 127 | receiver: Receiver::new(), // false 128 | } 129 | } 130 | 131 | /// Sends a single data frame to the remote endpoint. 132 | pub fn send_dataframe(&mut self, dataframe: &D) -> WebSocketResult<()> 133 | where 134 | D: DataFrameable, 135 | { 136 | self.sender.send_dataframe(self.stream.get_mut(), dataframe) 137 | } 138 | 139 | /// Sends a single message to the remote endpoint. 140 | pub fn send_message(&mut self, message: &M) -> WebSocketResult<()> 141 | where 142 | M: ws::Message, 143 | { 144 | self.sender.send_message(self.stream.get_mut(), message) 145 | } 146 | 147 | /// Reads a single data frame from the remote endpoint. 148 | pub fn recv_dataframe(&mut self) -> WebSocketResult { 149 | self.receiver.recv_dataframe(&mut self.stream) 150 | } 151 | 152 | /// Returns an iterator over incoming data frames. 153 | pub fn incoming_dataframes(&mut self) -> DataFrameIterator> { 154 | self.receiver.incoming_dataframes(&mut self.stream) 155 | } 156 | 157 | /// Reads a single message from this receiver. 158 | /// 159 | /// ```rust,no_run 160 | /// use websocket::{ClientBuilder, Message}; 161 | /// let mut client = ClientBuilder::new("ws://localhost:3000") 162 | /// .unwrap() 163 | /// .connect_insecure() 164 | /// .unwrap(); 165 | /// 166 | /// client.send_message(&Message::text("Hello world!")).unwrap(); 167 | /// 168 | /// let response = client.recv_message().unwrap(); 169 | /// ``` 170 | pub fn recv_message(&mut self) -> WebSocketResult { 171 | self.receiver.recv_message(&mut self.stream) 172 | } 173 | 174 | /// Access the headers that were sent in the server's handshake response. 175 | /// This is a catch all for headers other than protocols and extensions. 176 | pub fn headers(&self) -> &Headers { 177 | &self.headers 178 | } 179 | 180 | /// **If you supplied a protocol, you must check that it was accepted by 181 | /// the server** using this function. 182 | /// This is not done automatically because the terms of accepting a protocol 183 | /// can get complicated, especially if some protocols depend on others, etc. 184 | /// 185 | /// ```rust,no_run 186 | /// # use websocket::ClientBuilder; 187 | /// let mut client = ClientBuilder::new("wss://test.fysh.in").unwrap() 188 | /// .add_protocol("xmpp") 189 | /// .connect_insecure() 190 | /// .unwrap(); 191 | /// 192 | /// // be sure to check the protocol is there! 193 | /// assert!(client.protocols().iter().any(|p| p as &str == "xmpp")); 194 | /// ``` 195 | pub fn protocols(&self) -> &[String] { 196 | self.headers 197 | .get::() 198 | .map(|p| p.0.as_slice()) 199 | .unwrap_or(&[]) 200 | } 201 | 202 | /// If you supplied a protocol, be sure to check if it was accepted by the 203 | /// server here. Since no extensions are implemented out of the box yet, using 204 | /// one will require its own implementation. 205 | pub fn extensions(&self) -> &[Extension] { 206 | self.headers 207 | .get::() 208 | .map(|e| e.0.as_slice()) 209 | .unwrap_or(&[]) 210 | } 211 | 212 | /// Get a reference to the stream. 213 | /// Useful to be able to set options on the stream. 214 | /// 215 | /// ```rust,no_run 216 | /// # use websocket::ClientBuilder; 217 | /// let mut client = ClientBuilder::new("ws://double.down").unwrap() 218 | /// .connect_insecure() 219 | /// .unwrap(); 220 | /// 221 | /// client.stream_ref().set_ttl(60).unwrap(); 222 | /// ``` 223 | pub fn stream_ref(&self) -> &S { 224 | self.stream.get_ref() 225 | } 226 | 227 | /// Get a handle to the writable portion of this stream. 228 | /// This can be used to write custom extensions. 229 | /// 230 | /// ```rust,no_run 231 | /// # use websocket::ClientBuilder; 232 | /// use websocket::Message; 233 | /// use websocket::ws::sender::Sender as SenderTrait; 234 | /// use websocket::sender::Sender; 235 | /// 236 | /// let mut client = ClientBuilder::new("ws://the.room").unwrap() 237 | /// .connect_insecure() 238 | /// .unwrap(); 239 | /// 240 | /// let message = Message::text("Oh hi, Mark."); 241 | /// let mut sender = Sender::new(true); 242 | /// let mut buf = Vec::new(); 243 | /// 244 | /// sender.send_message(&mut buf, &message); 245 | /// 246 | /// /* transform buf somehow */ 247 | /// 248 | /// client.writer_mut().write_all(&buf); 249 | /// ``` 250 | pub fn writer_mut(&mut self) -> &mut dyn Write { 251 | self.stream.get_mut() 252 | } 253 | 254 | /// Get a handle to the readable portion of this stream. 255 | /// This can be used to transform raw bytes before they 256 | /// are read in. 257 | /// 258 | /// ```rust,no_run 259 | /// # use websocket::ClientBuilder; 260 | /// use std::io::Cursor; 261 | /// use websocket::ws::receiver::Receiver as ReceiverTrait; 262 | /// use websocket::receiver::Receiver; 263 | /// 264 | /// let mut client = ClientBuilder::new("ws://the.room").unwrap() 265 | /// .connect_insecure() 266 | /// .unwrap(); 267 | /// 268 | /// let mut receiver = Receiver::new(false); 269 | /// let mut buf = Vec::new(); 270 | /// 271 | /// client.reader_mut().read_to_end(&mut buf); 272 | /// 273 | /// /* transform buf somehow */ 274 | /// 275 | /// let mut buf_reader = Cursor::new(&mut buf); 276 | /// let message = receiver.recv_message(&mut buf_reader).unwrap(); 277 | /// ``` 278 | pub fn reader_mut(&mut self) -> &mut dyn Read { 279 | &mut self.stream 280 | } 281 | 282 | /// Deconstruct the client into its underlying stream and 283 | /// maybe some of the buffer that was already read from the stream. 284 | /// The client uses a buffered reader to read in messages, so some 285 | /// bytes might already be read from the stream when this is called, 286 | /// these buffered bytes are returned in the form 287 | /// 288 | /// `(byte_buffer: Vec, buffer_capacity: usize, buffer_position: usize)` 289 | pub fn into_stream(self) -> (S, Option<(Vec, usize, usize)>) { 290 | let (stream, buf, pos, cap) = self.stream.into_parts(); 291 | (stream, Some((buf, pos, cap))) 292 | } 293 | 294 | /// Returns an iterator over incoming messages. 295 | /// 296 | ///```no_run 297 | ///# extern crate websocket; 298 | ///# fn main() { 299 | ///use websocket::ClientBuilder; 300 | /// 301 | ///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap() 302 | /// .connect(None).unwrap(); 303 | /// 304 | ///for message in client.incoming_messages() { 305 | /// println!("Recv: {:?}", message.unwrap()); 306 | ///} 307 | ///# } 308 | ///``` 309 | /// 310 | /// Note that since this method mutably borrows the `Client`, it may be necessary to 311 | /// first `split()` the `Client` and call `incoming_messages()` on the returned 312 | /// `Receiver` to be able to send messages within an iteration. 313 | /// 314 | ///```no_run 315 | ///# extern crate websocket; 316 | ///# fn main() { 317 | ///use websocket::ClientBuilder; 318 | /// 319 | ///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap() 320 | /// .connect_insecure().unwrap(); 321 | /// 322 | ///let (mut receiver, mut sender) = client.split().unwrap(); 323 | /// 324 | ///for message in receiver.incoming_messages() { 325 | /// // Echo the message back 326 | /// sender.send_message(&message.unwrap()).unwrap(); 327 | ///} 328 | ///# } 329 | ///``` 330 | pub fn incoming_messages(&mut self) -> MessageIterator> { 331 | self.receiver.incoming_messages(&mut self.stream) 332 | } 333 | } 334 | 335 | impl Client 336 | where 337 | S: Splittable + Stream, 338 | { 339 | /// Split this client into its constituent Sender and Receiver pair. 340 | /// 341 | /// This allows the Sender and Receiver to be sent to different threads. 342 | /// 343 | ///```no_run 344 | ///# extern crate websocket; 345 | ///# fn main() { 346 | ///use std::thread; 347 | ///use websocket::{ClientBuilder, Message}; 348 | /// 349 | ///let mut client = ClientBuilder::new("ws://127.0.0.1:1234").unwrap() 350 | /// .connect_insecure().unwrap(); 351 | /// 352 | ///let (mut receiver, mut sender) = client.split().unwrap(); 353 | /// 354 | ///thread::spawn(move || { 355 | /// for message in receiver.incoming_messages() { 356 | /// println!("Recv: {:?}", message.unwrap()); 357 | /// } 358 | ///}); 359 | /// 360 | ///let message = Message::text("Hello, World!"); 361 | ///sender.send_message(&message).unwrap(); 362 | ///# } 363 | ///``` 364 | pub fn split( 365 | self, 366 | ) -> IoResult<( 367 | Reader<::Reader>, 368 | Writer<::Writer>, 369 | )> { 370 | let (stream, buf, pos, cap) = self.stream.into_parts(); 371 | let (read, write) = stream.split()?; 372 | Ok(( 373 | Reader { 374 | stream: BufReader::from_parts(read, buf, pos, cap), 375 | receiver: self.receiver, 376 | }, 377 | Writer { 378 | stream: write, 379 | sender: self.sender, 380 | }, 381 | )) 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /src/codec/http.rs: -------------------------------------------------------------------------------- 1 | //! Send HTTP requests and responses asynchronously. 2 | //! 3 | //! This module has both an `HttpClientCodec` for an async HTTP client and an 4 | //! `HttpServerCodec` for an async HTTP server. 5 | use bytes::BufMut; 6 | use bytes::BytesMut; 7 | use hyper::buffer::BufReader; 8 | use hyper::http::h1::parse_request; 9 | use hyper::http::h1::parse_response; 10 | use hyper::http::h1::Incoming; 11 | use hyper::http::RawStatus; 12 | use hyper::method::Method; 13 | use hyper::status::StatusCode; 14 | use hyper::uri::RequestUri; 15 | use std::error::Error; 16 | use std::fmt::{self, Display, Formatter}; 17 | use std::io::{self, Write}; 18 | use tokio_codec::{Decoder, Encoder}; 19 | 20 | #[derive(Copy, Clone, Debug)] 21 | ///A codec to be used with `tokio` codecs that can serialize HTTP requests and 22 | ///deserialize HTTP responses. One can use this on it's own without websockets to 23 | ///make a very bare async HTTP server. 24 | /// 25 | ///# Example 26 | ///```rust,no_run 27 | ///# extern crate tokio; 28 | ///# extern crate websocket; 29 | ///# extern crate hyper; 30 | ///use websocket::r#async::HttpClientCodec; 31 | ///# use websocket::r#async::futures::{Future, Sink, Stream}; 32 | ///# use tokio::net::TcpStream; 33 | ///# use tokio::codec::Decoder; 34 | ///# use hyper::http::h1::Incoming; 35 | ///# use hyper::version::HttpVersion; 36 | ///# use hyper::header::Headers; 37 | ///# use hyper::method::Method; 38 | ///# use hyper::uri::RequestUri; 39 | /// 40 | ///# fn main() { 41 | ///let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 42 | ///let addr = "crouton.net".parse().unwrap(); 43 | /// 44 | ///let f = TcpStream::connect(&addr) 45 | /// .and_then(|s| { 46 | /// Ok(HttpClientCodec.framed(s)) 47 | /// }) 48 | /// .and_then(|s| { 49 | /// s.send(Incoming { 50 | /// version: HttpVersion::Http11, 51 | /// subject: (Method::Get, RequestUri::AbsolutePath("/".to_string())), 52 | /// headers: Headers::new(), 53 | /// }) 54 | /// }) 55 | /// .map_err(|e| e.into()) 56 | /// .and_then(|s| s.into_future().map_err(|(e, _)| e)) 57 | /// .map(|(m, _)| println!("You got a crouton: {:?}", m)); 58 | /// 59 | ///runtime.block_on(f).unwrap(); 60 | ///# } 61 | ///``` 62 | pub struct HttpClientCodec; 63 | 64 | fn split_off_http(src: &mut BytesMut) -> Option { 65 | match src.windows(4).position(|i| i == b"\r\n\r\n") { 66 | Some(p) => Some(src.split_to(p + 4)), 67 | None => None, 68 | } 69 | } 70 | 71 | impl Encoder for HttpClientCodec { 72 | type Item = Incoming<(Method, RequestUri)>; 73 | type Error = io::Error; 74 | 75 | fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { 76 | // TODO: optomize this! 77 | let request = format!( 78 | "{} {} {}\r\n{}\r\n", 79 | item.subject.0, item.subject.1, item.version, item.headers 80 | ); 81 | let byte_len = request.as_bytes().len(); 82 | if byte_len > dst.remaining_mut() { 83 | dst.reserve(byte_len); 84 | } 85 | dst.writer().write(request.as_bytes()).map(|_| ()) 86 | } 87 | } 88 | 89 | impl Decoder for HttpClientCodec { 90 | type Item = Incoming; 91 | type Error = HttpCodecError; 92 | 93 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 94 | // check if we get a request from hyper 95 | // TODO: this is ineffecient, but hyper does not give us a better way to parse 96 | match split_off_http(src) { 97 | Some(buf) => { 98 | let mut reader = BufReader::with_capacity(&*buf as &[u8], buf.len()); 99 | let res = match parse_response(&mut reader) { 100 | Err(hyper::Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => { 101 | return Ok(None); 102 | } 103 | Err(hyper::Error::TooLarge) => return Ok(None), 104 | Err(e) => return Err(e.into()), 105 | Ok(r) => r, 106 | }; 107 | Ok(Some(res)) 108 | } 109 | None => Ok(None), 110 | } 111 | } 112 | } 113 | 114 | ///A codec that can be used with streams implementing `AsyncRead + AsyncWrite` 115 | ///that can serialize HTTP responses and deserialize HTTP requests. Using this 116 | ///with an async `TcpStream` will give you a very bare async HTTP server. 117 | /// 118 | ///This crate sends out one HTTP request / response in order to perform the websocket 119 | ///handshake then never talks HTTP again. Because of this an async HTTP implementation 120 | ///is needed. 121 | /// 122 | ///# Example 123 | /// 124 | ///```rust,no_run 125 | ///# extern crate tokio; 126 | ///# extern crate websocket; 127 | ///# extern crate hyper; 128 | ///# use std::io; 129 | ///use websocket::r#async::HttpServerCodec; 130 | ///# use websocket::r#async::futures::{Future, Sink, Stream}; 131 | ///# use tokio::net::TcpStream; 132 | ///# use tokio::codec::Decoder; 133 | ///# use hyper::http::h1::Incoming; 134 | ///# use hyper::version::HttpVersion; 135 | ///# use hyper::header::Headers; 136 | ///# use hyper::method::Method; 137 | ///# use hyper::uri::RequestUri; 138 | ///# use hyper::status::StatusCode; 139 | ///# fn main() { 140 | /// 141 | ///let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 142 | ///let addr = "nothing-to-see-here.com".parse().unwrap(); 143 | /// 144 | ///let f = TcpStream::connect(&addr) 145 | /// .map(|s| HttpServerCodec.framed(s)) 146 | /// .map_err(|e| e.into()) 147 | /// .and_then(|s| s.into_future().map_err(|(e, _)| e)) 148 | /// .and_then(|(m, s)| match m { 149 | /// Some(ref m) if m.subject.0 == Method::Get => Ok(s), 150 | /// _ => panic!(), 151 | /// }) 152 | /// .and_then(|stream| { 153 | /// stream 154 | /// .send(Incoming { 155 | /// version: HttpVersion::Http11, 156 | /// subject: StatusCode::NotFound, 157 | /// headers: Headers::new(), 158 | /// }) 159 | /// .map_err(|e| e.into()) 160 | /// }); 161 | /// 162 | ///runtime.block_on(f).unwrap(); 163 | ///# } 164 | ///``` 165 | #[derive(Copy, Clone, Debug)] 166 | pub struct HttpServerCodec; 167 | 168 | impl Encoder for HttpServerCodec { 169 | type Item = Incoming; 170 | type Error = io::Error; 171 | 172 | fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { 173 | // TODO: optomize this! 174 | let response = format!("{} {}\r\n{}\r\n", item.version, item.subject, item.headers); 175 | let byte_len = response.as_bytes().len(); 176 | if byte_len > dst.remaining_mut() { 177 | dst.reserve(byte_len); 178 | } 179 | dst.writer().write(response.as_bytes()).map(|_| ()) 180 | } 181 | } 182 | 183 | impl Decoder for HttpServerCodec { 184 | type Item = Incoming<(Method, RequestUri)>; 185 | type Error = HttpCodecError; 186 | 187 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 188 | // check if we get a request from hyper 189 | // TODO: this is ineffecient, but hyper does not give us a better way to parse 190 | match split_off_http(src) { 191 | Some(buf) => { 192 | let mut reader = BufReader::with_capacity(&*buf as &[u8], buf.len()); 193 | let res = match parse_request(&mut reader) { 194 | Err(hyper::Error::Io(ref e)) if e.kind() == io::ErrorKind::UnexpectedEof => { 195 | return Ok(None); 196 | } 197 | Err(hyper::Error::TooLarge) => return Ok(None), 198 | Err(e) => return Err(e.into()), 199 | Ok(r) => r, 200 | }; 201 | Ok(Some(res)) 202 | } 203 | None => Ok(None), 204 | } 205 | } 206 | } 207 | 208 | /// Any error that can happen during the writing or parsing of HTTP requests 209 | /// and responses. This consists of HTTP parsing errors (the `Http` variant) and 210 | /// errors that can occur when writing to IO (the `Io` variant). 211 | #[derive(Debug)] 212 | pub enum HttpCodecError { 213 | /// An error that occurs during the writing or reading of HTTP data 214 | /// from a socket. 215 | Io(io::Error), 216 | /// An error that occurs during the parsing of an HTTP request or response. 217 | Http(hyper::Error), 218 | } 219 | 220 | impl Display for HttpCodecError { 221 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 222 | fmt.write_str(self.description()) 223 | } 224 | } 225 | 226 | impl Error for HttpCodecError { 227 | fn description(&self) -> &str { 228 | match *self { 229 | HttpCodecError::Io(ref e) => e.description(), 230 | HttpCodecError::Http(ref e) => e.description(), 231 | } 232 | } 233 | 234 | fn cause(&self) -> Option<&dyn Error> { 235 | match *self { 236 | HttpCodecError::Io(ref error) => Some(error), 237 | HttpCodecError::Http(ref error) => Some(error), 238 | } 239 | } 240 | } 241 | 242 | impl From for HttpCodecError { 243 | fn from(err: io::Error) -> HttpCodecError { 244 | HttpCodecError::Io(err) 245 | } 246 | } 247 | 248 | impl From for HttpCodecError { 249 | fn from(err: hyper::Error) -> HttpCodecError { 250 | HttpCodecError::Http(err) 251 | } 252 | } 253 | 254 | #[cfg(test)] 255 | mod tests { 256 | use super::*; 257 | use crate::stream::ReadWritePair; 258 | use futures::{Future, Sink, Stream}; 259 | use hyper::header::Headers; 260 | use hyper::version::HttpVersion; 261 | use std::io::Cursor; 262 | use tokio::runtime::current_thread::Builder; 263 | 264 | #[test] 265 | fn test_client_http_codec() { 266 | let mut runtime = Builder::new().build().unwrap(); 267 | let response = "HTTP/1.1 404 Not Found\r\n\r\npssst extra data here"; 268 | let input = Cursor::new(response.as_bytes()); 269 | let output = Cursor::new(Vec::new()); 270 | 271 | let f = HttpClientCodec 272 | .framed(ReadWritePair(input, output)) 273 | .send(Incoming { 274 | version: HttpVersion::Http11, 275 | subject: (Method::Get, RequestUri::AbsolutePath("/".to_string())), 276 | headers: Headers::new(), 277 | }) 278 | .map_err(|e| e.into()) 279 | .and_then(|s| s.into_future().map_err(|(e, _)| e)) 280 | .and_then(|(m, _)| match m { 281 | Some(ref m) if StatusCode::from_u16(m.subject.0) == StatusCode::NotFound => Ok(()), 282 | _ => Err(io::Error::new(io::ErrorKind::Other, "test failed").into()), 283 | }); 284 | runtime.block_on(f).unwrap(); 285 | } 286 | 287 | #[test] 288 | fn test_server_http_codec() { 289 | let mut runtime = Builder::new().build().unwrap(); 290 | let request = "\ 291 | GET / HTTP/1.0\r\n\ 292 | Host: www.rust-lang.org\r\n\ 293 | \r\n\ 294 | " 295 | .as_bytes(); 296 | let input = Cursor::new(request); 297 | let output = Cursor::new(Vec::new()); 298 | 299 | let f = HttpServerCodec 300 | .framed(ReadWritePair(input, output)) 301 | .into_future() 302 | .map_err(|(e, _)| e) 303 | .and_then(|(m, s)| match m { 304 | Some(ref m) if m.subject.0 == Method::Get => Ok(s), 305 | _ => Err(io::Error::new(io::ErrorKind::Other, "test failed").into()), 306 | }) 307 | .and_then(|s| { 308 | s.send(Incoming { 309 | version: HttpVersion::Http11, 310 | subject: StatusCode::NotFound, 311 | headers: Headers::new(), 312 | }) 313 | .map_err(|e| e.into()) 314 | }); 315 | runtime.block_on(f).unwrap(); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /src/codec/mod.rs: -------------------------------------------------------------------------------- 1 | //! Useful `Codec` types for asynchronously encoding and decoding messages. 2 | //! 3 | //! This is intended to be used with the `Framed` type in the `tokio-io` crate. 4 | //! This module contains an `http` codec which can be used to send and receive 5 | //! hyper HTTP requests and responses asynchronously. 6 | //! See it's module level documentation for more info. 7 | //! 8 | //! Second but most importantly this module contains a codec for asynchronously 9 | //! encoding and decoding websocket messages (and dataframes if you want to go 10 | //! more low level) in the `ws` module. 11 | //! See it's module level documentation for more info. 12 | 13 | pub mod http; 14 | pub use websocket_base::codec::ws; 15 | -------------------------------------------------------------------------------- /src/header/accept.rs: -------------------------------------------------------------------------------- 1 | use crate::header::WebSocketKey; 2 | use crate::result::{WebSocketError, WebSocketResult}; 3 | use hyper::header::parsing::from_one_raw_str; 4 | use hyper::header::{Header, HeaderFormat}; 5 | use std::fmt::{self, Debug}; 6 | use std::str::FromStr; 7 | 8 | use websocket_base::header::WebSocketAccept as WebSocketAcceptLL; 9 | 10 | /// Represents a Sec-WebSocket-Accept header 11 | #[derive(PartialEq, Clone, Copy)] 12 | pub struct WebSocketAccept(WebSocketAcceptLL); 13 | 14 | impl Debug for WebSocketAccept { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | self.0.fmt(f) 17 | } 18 | } 19 | 20 | impl FromStr for WebSocketAccept { 21 | type Err = WebSocketError; 22 | 23 | fn from_str(accept: &str) -> WebSocketResult { 24 | Ok(WebSocketAccept(WebSocketAcceptLL::from_str(accept)?)) 25 | } 26 | } 27 | 28 | impl WebSocketAccept { 29 | /// Create a new WebSocketAccept from the given WebSocketKey 30 | pub fn new(key: &WebSocketKey) -> WebSocketAccept { 31 | WebSocketAccept(WebSocketAcceptLL::new(&key.0)) 32 | } 33 | /// Return the Base64 encoding of this WebSocketAccept 34 | pub fn serialize(&self) -> String { 35 | self.0.serialize() 36 | } 37 | } 38 | 39 | impl Header for WebSocketAccept { 40 | fn header_name() -> &'static str { 41 | "Sec-WebSocket-Accept" 42 | } 43 | 44 | fn parse_header(raw: &[Vec]) -> hyper::Result { 45 | from_one_raw_str(raw) 46 | } 47 | } 48 | 49 | impl HeaderFormat for WebSocketAccept { 50 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 51 | write!(fmt, "{}", self.serialize()) 52 | } 53 | } 54 | 55 | #[cfg(all(feature = "nightly", test))] 56 | mod tests { 57 | use super::*; 58 | use crate::header::{Headers, WebSocketKey}; 59 | use hyper::header::Header; 60 | use std::str::FromStr; 61 | use test; 62 | 63 | #[test] 64 | fn test_header_accept() { 65 | let key = FromStr::from_str("dGhlIHNhbXBsZSBub25jZQ==").unwrap(); 66 | let accept = WebSocketAccept::new(&key); 67 | let mut headers = Headers::new(); 68 | headers.set(accept); 69 | 70 | assert_eq!( 71 | &headers.to_string()[..], 72 | "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" 73 | ); 74 | } 75 | 76 | #[test] 77 | fn test_header_from_str() { 78 | let accept = WebSocketAccept::from_str("YSBzaW1wbGUgc2FtcGwgbm9uY2U="); 79 | assert!(accept.is_ok()); // 20 bytes 80 | 81 | let accept = WebSocketAccept::from_str("YSBzaG9ydCBub25jZQ=="); 82 | assert!(accept.is_err()); // < 20 bytes 83 | 84 | let accept = WebSocketAccept::from_str("YSByZWFsbHkgbWFsaWNpb3VzIG5vbmNl"); 85 | assert!(accept.is_err()); // > 20 bytes 86 | } 87 | 88 | #[bench] 89 | fn bench_header_accept_new(b: &mut test::Bencher) { 90 | let key = WebSocketKey::new(); 91 | b.iter(|| { 92 | let mut accept = WebSocketAccept::new(&key); 93 | test::black_box(&mut accept); 94 | }); 95 | } 96 | 97 | #[bench] 98 | fn bench_header_accept_parse(b: &mut test::Bencher) { 99 | let value = vec![b"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".to_vec()]; 100 | b.iter(|| { 101 | let mut accept: WebSocketAccept = Header::parse_header(&value[..]).unwrap(); 102 | test::black_box(&mut accept); 103 | }); 104 | } 105 | 106 | #[bench] 107 | fn bench_header_accept_format(b: &mut test::Bencher) { 108 | let value = vec![b"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".to_vec()]; 109 | let val: WebSocketAccept = Header::parse_header(&value[..]).unwrap(); 110 | b.iter(|| { 111 | format!("{}", val.serialize()); 112 | }); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/header/extensions.rs: -------------------------------------------------------------------------------- 1 | //! Provides the Sec-WebSocket-Extensions header. 2 | 3 | use crate::result::{WebSocketError, WebSocketResult}; 4 | use hyper::header::parsing::{fmt_comma_delimited, from_comma_delimited}; 5 | use hyper::header::{Header, HeaderFormat}; 6 | use std::fmt; 7 | use std::ops::Deref; 8 | use std::str::FromStr; 9 | 10 | const INVALID_EXTENSION: &str = "Invalid Sec-WebSocket-Extensions extension name"; 11 | 12 | // TODO: check if extension name is valid according to spec 13 | 14 | /// Represents a Sec-WebSocket-Extensions header 15 | #[derive(PartialEq, Clone, Debug)] 16 | pub struct WebSocketExtensions(pub Vec); 17 | 18 | impl Deref for WebSocketExtensions { 19 | type Target = Vec; 20 | 21 | fn deref(&self) -> &Vec { 22 | &self.0 23 | } 24 | } 25 | 26 | #[derive(PartialEq, Clone, Debug)] 27 | /// A WebSocket extension 28 | pub struct Extension { 29 | /// The name of this extension 30 | pub name: String, 31 | /// The parameters for this extension 32 | pub params: Vec, 33 | } 34 | 35 | impl Extension { 36 | /// Creates a new extension with the given name 37 | pub fn new(name: String) -> Extension { 38 | Extension { 39 | name, 40 | params: Vec::new(), 41 | } 42 | } 43 | } 44 | 45 | impl FromStr for Extension { 46 | type Err = WebSocketError; 47 | 48 | fn from_str(s: &str) -> WebSocketResult { 49 | let mut ext = s.split(';').map(str::trim); 50 | Ok(Extension { 51 | name: match ext.next() { 52 | Some(x) => x.to_string(), 53 | None => return Err(WebSocketError::ProtocolError(INVALID_EXTENSION)), 54 | }, 55 | params: ext 56 | .map(|x| { 57 | let mut pair = x.splitn(1, '=').map(|x| x.trim().to_string()); 58 | 59 | Parameter { 60 | name: pair.next().unwrap(), 61 | value: pair.next(), 62 | } 63 | }) 64 | .collect(), 65 | }) 66 | } 67 | } 68 | 69 | impl fmt::Display for Extension { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | write!(f, "{}", self.name)?; 72 | for param in &self.params { 73 | write!(f, "; {}", param)?; 74 | } 75 | Ok(()) 76 | } 77 | } 78 | 79 | #[derive(PartialEq, Clone, Debug)] 80 | /// A parameter for an Extension 81 | pub struct Parameter { 82 | /// The name of this parameter 83 | pub name: String, 84 | /// The value of this parameter, if any 85 | pub value: Option, 86 | } 87 | 88 | impl Parameter { 89 | /// Creates a new parameter with the given name and value 90 | pub fn new(name: String, value: Option) -> Parameter { 91 | Parameter { name, value } 92 | } 93 | } 94 | 95 | impl fmt::Display for Parameter { 96 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 97 | write!(f, "{}", self.name)?; 98 | if let Some(ref x) = self.value { 99 | write!(f, "={}", x)?; 100 | } 101 | Ok(()) 102 | } 103 | } 104 | 105 | impl Header for WebSocketExtensions { 106 | fn header_name() -> &'static str { 107 | "Sec-WebSocket-Extensions" 108 | } 109 | 110 | fn parse_header(raw: &[Vec]) -> hyper::Result { 111 | from_comma_delimited(raw).map(WebSocketExtensions) 112 | } 113 | } 114 | 115 | impl HeaderFormat for WebSocketExtensions { 116 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 117 | let WebSocketExtensions(ref value) = *self; 118 | fmt_comma_delimited(fmt, &value[..]) 119 | } 120 | } 121 | 122 | impl fmt::Display for WebSocketExtensions { 123 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 124 | self.fmt_header(fmt) 125 | } 126 | } 127 | 128 | #[cfg(all(feature = "nightly", test))] 129 | mod tests { 130 | use super::*; 131 | use hyper::header::Header; 132 | use test; 133 | 134 | #[test] 135 | fn test_header_extensions() { 136 | use crate::header::Headers; 137 | let value = vec![b"foo, bar; baz; qux=quux".to_vec()]; 138 | let extensions: WebSocketExtensions = Header::parse_header(&value[..]).unwrap(); 139 | 140 | let mut headers = Headers::new(); 141 | headers.set(extensions); 142 | 143 | assert_eq!( 144 | &headers.to_string()[..], 145 | "Sec-WebSocket-Extensions: foo, bar; baz; qux=quux\r\n" 146 | ); 147 | } 148 | 149 | #[bench] 150 | fn bench_header_extensions_parse(b: &mut test::Bencher) { 151 | let value = vec![b"foo, bar; baz; qux=quux".to_vec()]; 152 | b.iter(|| { 153 | let mut extensions: WebSocketExtensions = Header::parse_header(&value[..]).unwrap(); 154 | test::black_box(&mut extensions); 155 | }); 156 | } 157 | 158 | #[bench] 159 | fn bench_header_extensions_format(b: &mut test::Bencher) { 160 | let value = vec![b"foo, bar; baz; qux=quux".to_vec()]; 161 | let val: WebSocketExtensions = Header::parse_header(&value[..]).unwrap(); 162 | b.iter(|| { 163 | format!("{}", val); 164 | }); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/header/key.rs: -------------------------------------------------------------------------------- 1 | use crate::result::{WebSocketError, WebSocketResult}; 2 | use hyper::header::parsing::from_one_raw_str; 3 | use hyper::header::{Header, HeaderFormat}; 4 | use std::fmt::{self, Debug}; 5 | use std::str::FromStr; 6 | 7 | use websocket_base::header::WebSocketKey as WebSocketKeyLL; 8 | 9 | /// Represents a Sec-WebSocket-Key header. 10 | #[derive(PartialEq, Clone, Copy, Default)] 11 | pub struct WebSocketKey(pub WebSocketKeyLL); 12 | 13 | impl Debug for WebSocketKey { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | self.0.fmt(f) 16 | } 17 | } 18 | 19 | impl FromStr for WebSocketKey { 20 | type Err = WebSocketError; 21 | 22 | fn from_str(key: &str) -> WebSocketResult { 23 | Ok(WebSocketKey(WebSocketKeyLL::from_str(key)?)) 24 | } 25 | } 26 | 27 | impl WebSocketKey { 28 | /// Generate a new, random WebSocketKey 29 | pub fn new() -> WebSocketKey { 30 | WebSocketKey(WebSocketKeyLL::new()) 31 | } 32 | /// Return the Base64 encoding of this WebSocketKey 33 | pub fn serialize(&self) -> String { 34 | self.0.serialize() 35 | } 36 | 37 | /// Create WebSocketKey by explicitly specifying the key 38 | pub fn from_array(a: [u8; 16]) -> WebSocketKey { 39 | WebSocketKey(WebSocketKeyLL(a)) 40 | } 41 | } 42 | 43 | impl Header for WebSocketKey { 44 | fn header_name() -> &'static str { 45 | "Sec-WebSocket-Key" 46 | } 47 | 48 | fn parse_header(raw: &[Vec]) -> hyper::Result { 49 | from_one_raw_str(raw) 50 | } 51 | } 52 | 53 | impl HeaderFormat for WebSocketKey { 54 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 55 | write!(fmt, "{}", self.serialize()) 56 | } 57 | } 58 | 59 | #[cfg(all(feature = "nightly", test))] 60 | mod tests { 61 | use super::*; 62 | use hyper::header::Header; 63 | use test; 64 | 65 | #[test] 66 | fn test_header_key() { 67 | use crate::header::Headers; 68 | 69 | let extensions = WebSocketKey::from_array([65; 16]); 70 | let mut headers = Headers::new(); 71 | headers.set(extensions); 72 | 73 | assert_eq!( 74 | &headers.to_string()[..], 75 | "Sec-WebSocket-Key: QUFBQUFBQUFBQUFBQUFBQQ==\r\n" 76 | ); 77 | } 78 | 79 | #[test] 80 | fn test_header_from_str() { 81 | let key = WebSocketKey::from_str("YSByZWFsbCBnb29kIGtleQ=="); 82 | assert!(key.is_ok()); // 16 bytes 83 | 84 | let key = WebSocketKey::from_str("YSBzaG9ydCBrZXk="); 85 | assert!(key.is_err()); // < 16 bytes 86 | 87 | let key = WebSocketKey::from_str("YSB2ZXJ5IHZlcnkgbG9uZyBrZXk="); 88 | assert!(key.is_err()); // > 16 bytes 89 | } 90 | 91 | #[bench] 92 | fn bench_header_key_new(b: &mut test::Bencher) { 93 | b.iter(|| { 94 | let mut key = WebSocketKey::new(); 95 | test::black_box(&mut key); 96 | }); 97 | } 98 | 99 | #[bench] 100 | fn bench_header_key_parse(b: &mut test::Bencher) { 101 | let value = vec![b"QUFBQUFBQUFBQUFBQUFBQQ==".to_vec()]; 102 | b.iter(|| { 103 | let mut key: WebSocketKey = Header::parse_header(&value[..]).unwrap(); 104 | test::black_box(&mut key); 105 | }); 106 | } 107 | 108 | #[bench] 109 | fn bench_header_key_format(b: &mut test::Bencher) { 110 | let value = vec![b"QUFBQUFBQUFBQUFBQUFBQQ==".to_vec()]; 111 | let val: WebSocketKey = Header::parse_header(&value[..]).unwrap(); 112 | b.iter(|| { 113 | format!("{}", val.serialize()); 114 | }); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/header/mod.rs: -------------------------------------------------------------------------------- 1 | //! Structs representing headers relevant in a WebSocket context. 2 | //! 3 | //! These headers are commonly used in WebSocket requests and responses. 4 | //! The `Header` trait from the `hyper` crate is used. 5 | 6 | pub use self::accept::WebSocketAccept; 7 | pub use self::extensions::WebSocketExtensions; 8 | pub use self::key::WebSocketKey; 9 | pub use self::origin::Origin; 10 | pub use self::protocol::WebSocketProtocol; 11 | pub use self::version::WebSocketVersion; 12 | pub use hyper::header::*; 13 | pub use hyper::error::*; 14 | 15 | mod accept; 16 | pub mod extensions; 17 | mod key; 18 | mod origin; 19 | mod protocol; 20 | mod version; 21 | -------------------------------------------------------------------------------- /src/header/origin.rs: -------------------------------------------------------------------------------- 1 | use hyper::header::parsing::from_one_raw_str; 2 | use hyper::header::{Header, HeaderFormat}; 3 | use std::fmt; 4 | use std::ops::Deref; 5 | 6 | /// Represents an Origin header 7 | #[derive(PartialEq, Clone, Debug)] 8 | pub struct Origin(pub String); 9 | 10 | impl Deref for Origin { 11 | type Target = String; 12 | fn deref(&self) -> &String { 13 | &self.0 14 | } 15 | } 16 | 17 | impl Header for Origin { 18 | fn header_name() -> &'static str { 19 | "Origin" 20 | } 21 | 22 | fn parse_header(raw: &[Vec]) -> hyper::Result { 23 | from_one_raw_str(raw).map(Origin) 24 | } 25 | } 26 | 27 | impl HeaderFormat for Origin { 28 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 29 | let Origin(ref value) = *self; 30 | write!(fmt, "{}", value) 31 | } 32 | } 33 | 34 | impl fmt::Display for Origin { 35 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 36 | self.fmt_header(fmt) 37 | } 38 | } 39 | 40 | #[cfg(all(feature = "nightly", test))] 41 | mod tests { 42 | use super::*; 43 | use hyper::header::Header; 44 | use test; 45 | 46 | #[test] 47 | fn test_header_origin() { 48 | use crate::header::Headers; 49 | 50 | let origin = Origin("foo bar".to_string()); 51 | let mut headers = Headers::new(); 52 | headers.set(origin); 53 | 54 | assert_eq!(&headers.to_string()[..], "Origin: foo bar\r\n"); 55 | } 56 | 57 | #[bench] 58 | fn bench_header_origin_parse(b: &mut test::Bencher) { 59 | let value = vec![b"foobar".to_vec()]; 60 | b.iter(|| { 61 | let mut origin: Origin = Header::parse_header(&value[..]).unwrap(); 62 | test::black_box(&mut origin); 63 | }); 64 | } 65 | 66 | #[bench] 67 | fn bench_header_origin_format(b: &mut test::Bencher) { 68 | let value = vec![b"foobar".to_vec()]; 69 | let val: Origin = Header::parse_header(&value[..]).unwrap(); 70 | b.iter(|| { 71 | format!("{}", val); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/header/protocol.rs: -------------------------------------------------------------------------------- 1 | use hyper::header::parsing::{fmt_comma_delimited, from_comma_delimited}; 2 | use hyper::header::{Header, HeaderFormat}; 3 | use std::fmt; 4 | use std::ops::Deref; 5 | 6 | // TODO: only allow valid protocol names to be added 7 | 8 | /// Represents a Sec-WebSocket-Protocol header 9 | #[derive(PartialEq, Clone, Debug)] 10 | pub struct WebSocketProtocol(pub Vec); 11 | 12 | impl Deref for WebSocketProtocol { 13 | type Target = Vec; 14 | fn deref(&self) -> &Vec { 15 | &self.0 16 | } 17 | } 18 | 19 | impl Header for WebSocketProtocol { 20 | fn header_name() -> &'static str { 21 | "Sec-WebSocket-Protocol" 22 | } 23 | 24 | fn parse_header(raw: &[Vec]) -> hyper::Result { 25 | from_comma_delimited(raw).map(WebSocketProtocol) 26 | } 27 | } 28 | 29 | impl HeaderFormat for WebSocketProtocol { 30 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 31 | let WebSocketProtocol(ref value) = *self; 32 | fmt_comma_delimited(fmt, &value[..]) 33 | } 34 | } 35 | 36 | impl fmt::Display for WebSocketProtocol { 37 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 38 | self.fmt_header(fmt) 39 | } 40 | } 41 | 42 | #[cfg(all(feature = "nightly", test))] 43 | mod tests { 44 | use super::*; 45 | use hyper::header::Header; 46 | use test; 47 | 48 | #[test] 49 | fn test_header_protocol() { 50 | use crate::header::Headers; 51 | 52 | let protocol = WebSocketProtocol(vec!["foo".to_string(), "bar".to_string()]); 53 | let mut headers = Headers::new(); 54 | headers.set(protocol); 55 | 56 | assert_eq!( 57 | &headers.to_string()[..], 58 | "Sec-WebSocket-Protocol: foo, bar\r\n" 59 | ); 60 | } 61 | 62 | #[bench] 63 | fn bench_header_protocol_parse(b: &mut test::Bencher) { 64 | let value = vec![b"foo, bar".to_vec()]; 65 | b.iter(|| { 66 | let mut protocol: WebSocketProtocol = Header::parse_header(&value[..]).unwrap(); 67 | test::black_box(&mut protocol); 68 | }); 69 | } 70 | 71 | #[bench] 72 | fn bench_header_protocol_format(b: &mut test::Bencher) { 73 | let value = vec![b"foo, bar".to_vec()]; 74 | let val: WebSocketProtocol = Header::parse_header(&value[..]).unwrap(); 75 | b.iter(|| { 76 | format!("{}", val); 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/header/version.rs: -------------------------------------------------------------------------------- 1 | use hyper::header::parsing::from_one_raw_str; 2 | use hyper::header::{Header, HeaderFormat}; 3 | use std::fmt::{self, Debug}; 4 | 5 | /// Represents a Sec-WebSocket-Version header 6 | #[derive(PartialEq, Clone)] 7 | pub enum WebSocketVersion { 8 | /// The version of WebSocket defined in RFC6455 9 | WebSocket13, 10 | /// An unknown version of WebSocket 11 | Unknown(String), 12 | } 13 | 14 | impl fmt::Debug for WebSocketVersion { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | match *self { 17 | WebSocketVersion::WebSocket13 => write!(f, "13"), 18 | WebSocketVersion::Unknown(ref value) => write!(f, "{}", value), 19 | } 20 | } 21 | } 22 | 23 | impl Header for WebSocketVersion { 24 | fn header_name() -> &'static str { 25 | "Sec-WebSocket-Version" 26 | } 27 | 28 | fn parse_header(raw: &[Vec]) -> hyper::Result { 29 | from_one_raw_str(raw).map(|s: String| match &s[..] { 30 | "13" => WebSocketVersion::WebSocket13, 31 | _ => WebSocketVersion::Unknown(s), 32 | }) 33 | } 34 | } 35 | 36 | impl HeaderFormat for WebSocketVersion { 37 | fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 38 | self.fmt(fmt) 39 | } 40 | } 41 | 42 | impl fmt::Display for WebSocketVersion { 43 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 44 | self.fmt_header(fmt) 45 | } 46 | } 47 | 48 | #[cfg(all(feature = "nightly", test))] 49 | mod tests { 50 | use super::*; 51 | use hyper::header::Header; 52 | use test; 53 | 54 | #[test] 55 | fn test_websocket_version() { 56 | use crate::header::Headers; 57 | 58 | let version = WebSocketVersion::WebSocket13; 59 | let mut headers = Headers::new(); 60 | headers.set(version); 61 | 62 | assert_eq!(&headers.to_string()[..], "Sec-WebSocket-Version: 13\r\n"); 63 | } 64 | 65 | #[bench] 66 | fn bench_header_version_parse(b: &mut test::Bencher) { 67 | let value = vec![b"13".to_vec()]; 68 | b.iter(|| { 69 | let mut version: WebSocketVersion = Header::parse_header(&value[..]).unwrap(); 70 | test::black_box(&mut version); 71 | }); 72 | } 73 | 74 | #[bench] 75 | fn bench_header_version_format(b: &mut test::Bencher) { 76 | let value = vec![b"13".to_vec()]; 77 | let val: WebSocketVersion = Header::parse_header(&value[..]).unwrap(); 78 | b.iter(|| { 79 | format!("{}", val); 80 | }); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(all(test, feature = "nightly"), feature(test))] 2 | #![allow( 3 | clippy::write_with_newline, 4 | clippy::type_complexity, 5 | clippy::match_ref_pats, 6 | clippy::needless_doctest_main 7 | )] 8 | #![deny(unused_mut)] 9 | 10 | //! Rust-WebSocket is a WebSocket (RFC6455) library written in Rust. 11 | //! 12 | //! # Synchronous and Asynchronous 13 | //! This crate has both async and sync implementations of websockets, you are free to 14 | //! choose which one you would like to use by switching on the `async` or `sync` features 15 | //! for this crate. By default both are switched on since they do not conflict with each 16 | //! other. 17 | //! 18 | //! You'll find many modules with `::sync` and `::async` submodules that separate these 19 | //! behaviours. Since it get's tedious to add these on when appropriate a top-level 20 | //! convenience module called `websocket::sync` and `websocket::async` has been added that 21 | //! groups all the sync and async stuff, respectively. 22 | //! 23 | //! # Clients 24 | //! To make a client use the `ClientBuilder` struct, this builder has methods 25 | //! for creating both synchronous and asynchronous clients. 26 | //! 27 | //! # Servers 28 | //! WebSocket servers act similarly to the `TcpListener`, and listen for connections. 29 | //! See the `Server` struct documentation for more information. The `bind()` and 30 | //! `bind_secure()` functions will bind the server to the given `SocketAddr`. 31 | //! 32 | //! # Extending Rust-WebSocket 33 | //! The `ws` module contains the traits and functions used by Rust-WebSocket at a lower 34 | //! level. Their usage is explained in the module documentation. 35 | 36 | extern crate hyper; 37 | #[cfg(test)] 38 | extern crate tokio; 39 | 40 | extern crate unicase; 41 | pub extern crate url; 42 | 43 | pub extern crate websocket_base; 44 | 45 | #[cfg(all(feature = "nightly", test))] 46 | extern crate test; 47 | 48 | macro_rules! upsert_header { 49 | ($headers:expr; $header:ty; { Some($pat:pat) => $some_match:expr,None => $default:expr }) => {{ 50 | if $headers.has::<$header>() { 51 | if let Some($pat) = $headers.get_mut::<$header>() { 52 | $some_match 53 | } 54 | } else { 55 | $headers.set($default); 56 | } 57 | }}; 58 | } 59 | 60 | pub use websocket_base::dataframe; 61 | pub mod header; 62 | pub use websocket_base::message; 63 | pub mod result; 64 | pub use websocket_base::ws; 65 | pub mod receiver; 66 | pub mod sender; 67 | 68 | pub mod client; 69 | pub mod server; 70 | pub use websocket_base::stream; 71 | 72 | pub mod sync { 73 | pub use crate::sender; 74 | pub use crate::sender::Writer; 75 | 76 | pub use crate::receiver; 77 | pub use crate::receiver::Reader; 78 | 79 | pub use crate::stream::sync as stream; 80 | pub use crate::stream::sync::Stream; 81 | 82 | /// A collection of handy synchronous-only parts of the `server` module. 83 | pub mod server { 84 | pub use crate::server::sync::*; 85 | pub use crate::server::upgrade::sync as upgrade; 86 | pub use crate::server::upgrade::sync::IntoWs; 87 | pub use crate::server::upgrade::sync::Upgrade; 88 | } 89 | pub use crate::server::sync::Server; 90 | 91 | /// A collection of handy synchronous-only parts of the `client` module. 92 | pub mod client { 93 | pub use crate::client::builder::ClientBuilder; 94 | pub use crate::client::sync::*; 95 | } 96 | pub use crate::client::sync::Client; 97 | } 98 | 99 | pub use self::client::builder::ClientBuilder; 100 | pub use self::message::CloseData; 101 | pub use self::message::Message; 102 | pub use self::message::OwnedMessage; 103 | 104 | pub use self::result::WebSocketError; 105 | pub use self::result::WebSocketResult; 106 | -------------------------------------------------------------------------------- /src/receiver.rs: -------------------------------------------------------------------------------- 1 | //! The default implementation of a WebSocket Receiver. 2 | 3 | use std::io::Read; 4 | use std::io::Result as IoResult; 5 | 6 | use hyper::buffer::BufReader; 7 | 8 | use crate::dataframe::{DataFrame, Opcode}; 9 | use crate::message::OwnedMessage; 10 | use crate::result::{WebSocketError, WebSocketResult}; 11 | pub use crate::stream::sync::Shutdown; 12 | use crate::stream::sync::{AsTcpStream, Stream}; 13 | use crate::ws; 14 | use crate::ws::receiver::Receiver as ReceiverTrait; 15 | use crate::ws::receiver::{DataFrameIterator, MessageIterator}; 16 | 17 | /// This reader bundles an existing stream with a parsing algorithm. 18 | /// It is used by the client in its `.split()` function as the reading component. 19 | pub struct Reader 20 | where 21 | R: Read, 22 | { 23 | /// the stream to be read from 24 | pub stream: BufReader, 25 | /// the parser to parse bytes into messages 26 | pub receiver: Receiver, 27 | } 28 | 29 | impl Reader 30 | where 31 | R: Read, 32 | { 33 | /// Reads a single data frame from the remote endpoint. 34 | pub fn recv_dataframe(&mut self) -> WebSocketResult { 35 | self.receiver.recv_dataframe(&mut self.stream) 36 | } 37 | 38 | /// Returns an iterator over incoming data frames. 39 | pub fn incoming_dataframes(&mut self) -> DataFrameIterator> { 40 | self.receiver.incoming_dataframes(&mut self.stream) 41 | } 42 | 43 | /// Reads a single message from this receiver. 44 | pub fn recv_message(&mut self) -> WebSocketResult { 45 | self.receiver.recv_message(&mut self.stream) 46 | } 47 | 48 | /// An iterator over incoming messsages. 49 | /// This iterator will block until new messages arrive and will never halt. 50 | pub fn incoming_messages(&mut self) -> MessageIterator> { 51 | self.receiver.incoming_messages(&mut self.stream) 52 | } 53 | } 54 | 55 | impl Reader 56 | where 57 | S: AsTcpStream + Stream + Read, 58 | { 59 | /// Closes the receiver side of the connection, will cause all pending and future IO to 60 | /// return immediately with an appropriate value. 61 | pub fn shutdown(&self) -> IoResult<()> { 62 | self.stream.get_ref().as_tcp().shutdown(Shutdown::Read) 63 | } 64 | 65 | /// Shuts down both Sender and Receiver, will cause all pending and future IO to 66 | /// return immediately with an appropriate value. 67 | pub fn shutdown_all(&self) -> IoResult<()> { 68 | self.stream.get_ref().as_tcp().shutdown(Shutdown::Both) 69 | } 70 | } 71 | 72 | /// A Receiver that wraps a Reader and provides a default implementation using 73 | /// DataFrames and Messages. 74 | pub struct Receiver { 75 | buffer: Vec, 76 | } 77 | 78 | impl Receiver { 79 | /// Create a new Receiver using the specified Reader. 80 | pub fn new() -> Receiver { 81 | Receiver { 82 | buffer: Vec::new(), 83 | } 84 | } 85 | } 86 | impl Default for Receiver { 87 | fn default() -> Self { 88 | Self::new() 89 | } 90 | } 91 | impl ws::Receiver for Receiver { 92 | type F = DataFrame; 93 | 94 | type M = OwnedMessage; 95 | 96 | /// Reads a single data frame from the remote endpoint. 97 | fn recv_dataframe(&mut self, reader: &mut R) -> WebSocketResult 98 | where 99 | R: Read, 100 | { 101 | DataFrame::read_dataframe(reader) 102 | } 103 | 104 | /// Returns the data frames that constitute one message. 105 | fn recv_message_dataframes(&mut self, reader: &mut R) -> WebSocketResult> 106 | where 107 | R: Read, 108 | { 109 | let mut finished = if self.buffer.is_empty() { 110 | let first = self.recv_dataframe(reader)?; 111 | 112 | if first.opcode == Opcode::Continuation { 113 | return Err(WebSocketError::ProtocolError( 114 | "Unexpected continuation data frame opcode", 115 | )); 116 | } 117 | 118 | let finished = first.finished; 119 | self.buffer.push(first); 120 | finished 121 | } else { 122 | false 123 | }; 124 | 125 | while !finished { 126 | let next = self.recv_dataframe(reader)?; 127 | finished = next.finished; 128 | 129 | match next.opcode as u8 { 130 | // Continuation opcode 131 | 0 => self.buffer.push(next), 132 | // Control frame 133 | 8..=15 => { 134 | return Ok(vec![next]); 135 | } 136 | // Others 137 | _ => { 138 | return Err(WebSocketError::ProtocolError( 139 | "Unexpected data frame opcode", 140 | )); 141 | } 142 | } 143 | } 144 | 145 | Ok(::std::mem::replace(&mut self.buffer, Vec::new())) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/result.rs: -------------------------------------------------------------------------------- 1 | //! The result type used within Rust-WebSocket 2 | 3 | use crate::server::upgrade::HyperIntoWsError; 4 | pub use hyper::status::StatusCode; 5 | use hyper::Error as HttpError; 6 | use std::convert::From; 7 | use std::error::Error; 8 | use std::fmt; 9 | use std::io; 10 | 11 | use url::ParseError; 12 | 13 | /// The type used for WebSocket results 14 | pub type WebSocketResult = Result; 15 | 16 | 17 | pub use websocket_base::result::WebSocketError; 18 | 19 | /// Represents a WebSocket error while connecting 20 | #[derive(Debug)] 21 | pub enum WebSocketOtherError { 22 | /// A WebSocket protocol error 23 | ProtocolError(&'static str), 24 | /// Invalid WebSocket request error 25 | RequestError(&'static str), 26 | /// Invalid WebSocket response error 27 | ResponseError(&'static str), 28 | /// Received unexpected status code 29 | StatusCodeError(StatusCode), 30 | /// An HTTP parsing error 31 | HttpError(HttpError), 32 | /// A URL parsing error 33 | UrlError(ParseError), 34 | /// An input/output error 35 | IoError(io::Error), 36 | /// A WebSocket URL error 37 | WebSocketUrlError(WSUrlErrorKind), 38 | } 39 | 40 | impl fmt::Display for WebSocketOtherError { 41 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 42 | match self { 43 | WebSocketOtherError::RequestError(e) => write!(fmt, "WebSocket request error: {}", e)?, 44 | WebSocketOtherError::ResponseError(e) => { 45 | write!(fmt, "WebSocket response error: {}", e)? 46 | } 47 | WebSocketOtherError::StatusCodeError(e) => write!( 48 | fmt, 49 | "WebSocketError: Received unexpected status code ({})", 50 | e 51 | )?, 52 | WebSocketOtherError::HttpError(e) => write!(fmt, "WebSocket HTTP error: {}", e)?, 53 | WebSocketOtherError::UrlError(e) => write!(fmt, "WebSocket URL parse error: {}", e)?, 54 | WebSocketOtherError::IoError(e) => write!(fmt, "WebSocket I/O error: {}", e)?, 55 | WebSocketOtherError::WebSocketUrlError(e) => e.fmt(fmt)?, 56 | _ => write!(fmt, "WebSocketError: {}", self.description())?, 57 | } 58 | Ok(()) 59 | } 60 | } 61 | 62 | impl Error for WebSocketOtherError { 63 | fn description(&self) -> &str { 64 | match *self { 65 | WebSocketOtherError::RequestError(_) => "WebSocket request error", 66 | WebSocketOtherError::ResponseError(_) => "WebSocket response error", 67 | WebSocketOtherError::HttpError(_) => "HTTP failure", 68 | WebSocketOtherError::UrlError(_) => "URL failure", 69 | WebSocketOtherError::WebSocketUrlError(_) => "WebSocket URL failure", 70 | WebSocketOtherError::IoError(ref e) => e.description(), 71 | WebSocketOtherError::ProtocolError(e) => e, 72 | WebSocketOtherError::StatusCodeError(_) => "Received unexpected status code", 73 | } 74 | } 75 | 76 | fn cause(&self) -> Option<&dyn Error> { 77 | match *self { 78 | WebSocketOtherError::HttpError(ref error) => Some(error), 79 | WebSocketOtherError::UrlError(ref error) => Some(error), 80 | WebSocketOtherError::WebSocketUrlError(ref error) => Some(error), 81 | WebSocketOtherError::IoError(ref e) => Some(e), 82 | _ => None, 83 | } 84 | } 85 | } 86 | 87 | impl From for WebSocketOtherError { 88 | fn from(err: HttpError) -> WebSocketOtherError { 89 | WebSocketOtherError::HttpError(err) 90 | } 91 | } 92 | 93 | impl From for WebSocketOtherError { 94 | fn from(err: ParseError) -> WebSocketOtherError { 95 | WebSocketOtherError::UrlError(err) 96 | } 97 | } 98 | 99 | impl From for WebSocketOtherError { 100 | fn from(err: WSUrlErrorKind) -> WebSocketOtherError { 101 | WebSocketOtherError::WebSocketUrlError(err) 102 | } 103 | } 104 | 105 | impl From for WebSocketOtherError { 106 | fn from(err: HyperIntoWsError) -> WebSocketOtherError { 107 | use self::HyperIntoWsError::*; 108 | use self::WebSocketOtherError::*; 109 | match err { 110 | Io(io) => IoError(io), 111 | Parsing(err) => HttpError(err), 112 | MethodNotGet => ProtocolError("Request method must be GET"), 113 | UnsupportedHttpVersion => ProtocolError("Unsupported request HTTP version"), 114 | UnsupportedWebsocketVersion => ProtocolError("Unsupported WebSocket version"), 115 | NoSecWsKeyHeader => ProtocolError("Missing Sec-WebSocket-Key header"), 116 | NoWsUpgradeHeader => ProtocolError("Invalid Upgrade WebSocket header"), 117 | NoUpgradeHeader => ProtocolError("Missing Upgrade WebSocket header"), 118 | NoWsConnectionHeader => ProtocolError("Invalid Connection WebSocket header"), 119 | NoConnectionHeader => ProtocolError("Missing Connection WebSocket header"), 120 | } 121 | } 122 | } 123 | 124 | /// Represents a WebSocket URL error 125 | #[derive(Debug)] 126 | pub enum WSUrlErrorKind { 127 | /// Fragments are not valid in a WebSocket URL 128 | CannotSetFragment, 129 | /// The scheme provided is invalid for a WebSocket 130 | InvalidScheme, 131 | /// There is no hostname or IP address to connect to 132 | NoHostName, 133 | } 134 | 135 | impl fmt::Display for WSUrlErrorKind { 136 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 137 | fmt.write_str("WebSocket Url Error: ")?; 138 | fmt.write_str(self.description())?; 139 | Ok(()) 140 | } 141 | } 142 | 143 | impl Error for WSUrlErrorKind { 144 | fn description(&self) -> &str { 145 | match *self { 146 | WSUrlErrorKind::CannotSetFragment => "WebSocket URL cannot set fragment", 147 | WSUrlErrorKind::InvalidScheme => "WebSocket URL invalid scheme", 148 | WSUrlErrorKind::NoHostName => "WebSocket URL no host name provided", 149 | } 150 | } 151 | } 152 | 153 | impl From for WebSocketError { 154 | fn from(e: WebSocketOtherError) -> WebSocketError { 155 | WebSocketError::Other(Box::new(e)) 156 | } 157 | } 158 | 159 | pub(crate) fn towse(e: E) -> WebSocketError 160 | where 161 | E: Into, 162 | { 163 | let e: WebSocketOtherError = e.into(); 164 | e.into() 165 | } 166 | -------------------------------------------------------------------------------- /src/sender.rs: -------------------------------------------------------------------------------- 1 | //! The default implementation of a WebSocket Sender. 2 | 3 | use crate::result::WebSocketResult; 4 | use crate::stream::sync::AsTcpStream; 5 | pub use crate::stream::sync::Shutdown; 6 | use crate::ws; 7 | use crate::ws::dataframe::DataFrame; 8 | use crate::ws::sender::Sender as SenderTrait; 9 | use std::io::Result as IoResult; 10 | use std::io::Write; 11 | 12 | /// A writer that bundles a stream with a serializer to send the messages. 13 | /// This is used in the client's `.split()` function as the writing component. 14 | /// 15 | /// It can also be useful to use a websocket connection without a handshake. 16 | pub struct Writer { 17 | /// The stream that websocket messages will be written to 18 | pub stream: W, 19 | /// The serializer that will be used to serialize the messages 20 | pub sender: Sender, 21 | } 22 | 23 | impl Writer 24 | where 25 | W: Write, 26 | { 27 | /// Sends a single data frame to the remote endpoint. 28 | pub fn send_dataframe(&mut self, dataframe: &D) -> WebSocketResult<()> 29 | where 30 | D: DataFrame, 31 | W: Write, 32 | { 33 | self.sender.send_dataframe(&mut self.stream, dataframe) 34 | } 35 | 36 | /// Sends a single message to the remote endpoint. 37 | pub fn send_message(&mut self, message: &M) -> WebSocketResult<()> 38 | where 39 | M: ws::Message, 40 | { 41 | self.sender.send_message(&mut self.stream, message) 42 | } 43 | } 44 | 45 | impl Writer 46 | where 47 | S: AsTcpStream + Write, 48 | { 49 | /// Closes the sender side of the connection, will cause all pending and future IO to 50 | /// return immediately with an appropriate value. 51 | pub fn shutdown(&self) -> IoResult<()> { 52 | self.stream.as_tcp().shutdown(Shutdown::Write) 53 | } 54 | 55 | /// Shuts down both Sender and Receiver, will cause all pending and future IO to 56 | /// return immediately with an appropriate value. 57 | pub fn shutdown_all(&self) -> IoResult<()> { 58 | self.stream.as_tcp().shutdown(Shutdown::Both) 59 | } 60 | } 61 | 62 | /// A Sender that wraps a Writer and provides a default implementation using 63 | /// DataFrames and Messages. 64 | pub struct Sender { 65 | mask: bool, 66 | } 67 | 68 | impl Sender { 69 | /// Create a new WebSocketSender using the specified Writer. 70 | pub fn new(mask: bool) -> Sender { 71 | Sender { mask } 72 | } 73 | } 74 | 75 | impl ws::Sender for Sender { 76 | fn is_masked(&self) -> bool { 77 | self.mask 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/server/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provides an implementation of a WebSocket server 2 | 3 | use self::upgrade::{HyperIntoWsError, Request}; 4 | use crate::stream::Stream; 5 | use std::fmt::{Debug, Formatter, Result as FmtResult}; 6 | 7 | pub mod upgrade; 8 | 9 | pub mod sync; 10 | 11 | /// Marker struct for a struct not being secure 12 | #[derive(Clone)] 13 | pub struct NoTlsAcceptor; 14 | /// Trait that is implemented over NoSslAcceptor and SslAcceptor that 15 | /// serves as a generic bound to make a struct with. 16 | /// Used in the Server to specify impls based on whether the server 17 | /// is running over SSL or not. 18 | pub trait OptionalTlsAcceptor {} 19 | impl OptionalTlsAcceptor for NoTlsAcceptor {} 20 | 21 | /// When a sever tries to accept a connection many things can go wrong. 22 | /// 23 | /// This struct is all the information that is recovered from a failed 24 | /// websocket handshake, in case one wants to use the connection for something 25 | /// else (such as HTTP). 26 | pub struct InvalidConnection 27 | where 28 | S: Stream, 29 | { 30 | /// if the stream was successfully setup it will be included here 31 | /// on a failed connection. 32 | pub stream: Option, 33 | /// the parsed request. **This is a normal HTTP request** meaning you can 34 | /// simply run this server and handle both HTTP and Websocket connections. 35 | /// If you already have a server you want to use, checkout the 36 | /// `server::upgrade` module to integrate this crate with your server. 37 | pub parsed: Option, 38 | /// the buffered data that was already taken from the stream 39 | pub buffer: Option, 40 | /// the cause of the failed websocket connection setup 41 | pub error: HyperIntoWsError, 42 | } 43 | 44 | impl Debug for InvalidConnection 45 | where 46 | S: Stream, 47 | { 48 | fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult { 49 | fmt.debug_struct("InvalidConnection") 50 | .field("stream", &String::from("...")) 51 | .field("parsed", &String::from("...")) 52 | .field("buffer", &String::from("...")) 53 | .field("error", &self.error) 54 | .finish() 55 | } 56 | } 57 | 58 | /// Represents a WebSocket server which can work with either normal 59 | /// (non-secure) connections, or secure WebSocket connections. 60 | /// 61 | /// This is a convenient way to implement WebSocket servers, however 62 | /// it is possible to use any sendable Reader and Writer to obtain 63 | /// a WebSocketClient, so if needed, an alternative server implementation can be used. 64 | /// 65 | /// # Synchronous Servers 66 | /// Synchronous implementations of a websocket server are available below, each method is 67 | /// documented so the reader knows whether is it synchronous or asynchronous. 68 | /// 69 | /// To use the synchronous implementation, you must have the `sync` feature enabled 70 | /// (it is enabled by default). 71 | /// To use the synchronous SSL implementation, you must have the `sync-ssl` feature enabled 72 | /// (it is enabled by default). 73 | /// 74 | /// # Asynchronous Servers 75 | /// Asynchronous implementations of a websocket server are available below, each method is 76 | /// documented so the reader knows whether is it synchronous or asynchronous. 77 | /// Simply look out for the implementation of `Server` whose methods only return `Future`s 78 | /// (it is also written in the docs if the method is async). 79 | /// 80 | /// To use the asynchronous implementation, you must have the `async` feature enabled 81 | /// (it is enabled by default). 82 | /// To use the asynchronous SSL implementation, you must have the `async-ssl` feature enabled 83 | /// (it is enabled by default). 84 | /// 85 | /// # A Hyper Server 86 | /// This crates comes with hyper integration out of the box, you can create a hyper 87 | /// server and serve websocket and HTTP **on the same port!** 88 | /// check out the docs over at `websocket::server::upgrade::sync::HyperRequest` for an example. 89 | /// 90 | /// # A Custom Server 91 | /// So you don't want to use any of our server implementations? That's O.K. 92 | /// All it takes is implementing the `IntoWs` trait for your server's streams, 93 | /// then calling `.into_ws()` on them. 94 | /// check out the docs over at `websocket::server::upgrade::sync` for more. 95 | 96 | pub struct WsServer 97 | where 98 | S: OptionalTlsAcceptor, 99 | { 100 | listener: L, 101 | /// The SSL acceptor given to the server 102 | pub ssl_acceptor: S, 103 | } 104 | -------------------------------------------------------------------------------- /src/server/sync.rs: -------------------------------------------------------------------------------- 1 | //! Provides an implementation of a WebSocket server 2 | use crate::server::upgrade::sync::{Buffer, IntoWs, Upgrade}; 3 | pub use crate::server::upgrade::{HyperIntoWsError, Request}; 4 | use crate::server::{InvalidConnection, NoTlsAcceptor, OptionalTlsAcceptor, WsServer}; 5 | 6 | use std::convert::Into; 7 | use std::io; 8 | use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs}; 9 | 10 | /// Either the stream was established and it sent a websocket handshake 11 | /// which represents the `Ok` variant, or there was an error (this is the 12 | /// `Err` variant). 13 | pub type AcceptResult = Result, InvalidConnection>; 14 | 15 | /// Represents a WebSocket server which can work with either normal 16 | /// (non-secure) connections, or secure WebSocket connections. 17 | /// 18 | /// This is a convenient way to implement WebSocket servers, however 19 | /// it is possible to use any sendable Reader and Writer to obtain 20 | /// a WebSocketClient, so if needed, an alternative server implementation can be used. 21 | pub type Server = WsServer; 22 | 23 | /// Synchronous methods for creating a server and accepting incoming connections. 24 | impl WsServer 25 | where 26 | S: OptionalTlsAcceptor, 27 | { 28 | /// Get the socket address of this server 29 | pub fn local_addr(&self) -> io::Result { 30 | self.listener.local_addr() 31 | } 32 | 33 | /// Changes whether the Server is in nonblocking mode. 34 | /// NOTE: It is strongly encouraged to use the `websocket::async` module instead 35 | /// of this. It provides high level APIs for creating asynchronous servers. 36 | /// 37 | /// If it is in nonblocking mode, accept() will return an error instead of 38 | /// blocking when there are no incoming connections. 39 | /// 40 | ///```no_run 41 | /// # extern crate websocket; 42 | /// # use websocket::sync::Server; 43 | /// # fn main() { 44 | /// // Suppose we have to work in a single thread, but want to 45 | /// // accomplish two unrelated things: 46 | /// // (1) Once in a while we want to check if anybody tried to connect to 47 | /// // our websocket server, and if so, handle the TcpStream. 48 | /// // (2) In between we need to something else, possibly unrelated to networking. 49 | /// 50 | /// let mut server = Server::bind("127.0.0.1:0").unwrap(); 51 | /// 52 | /// // Set the server to non-blocking. 53 | /// server.set_nonblocking(true); 54 | /// 55 | /// for i in 1..3 { 56 | /// let result = match server.accept() { 57 | /// Ok(wsupgrade) => { 58 | /// // Do something with the established TcpStream. 59 | /// } 60 | /// _ => { 61 | /// // Nobody tried to connect, move on. 62 | /// } 63 | /// }; 64 | /// // Perform another task. Because we have a non-blocking server, 65 | /// // this will execute independent of whether someone tried to 66 | /// // establish a connection. 67 | /// let two = 1+1; 68 | /// } 69 | /// # } 70 | ///``` 71 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { 72 | self.listener.set_nonblocking(nonblocking) 73 | } 74 | } 75 | 76 | impl WsServer { 77 | /// Bind this Server to this socket 78 | /// 79 | /// # Non-secure Servers 80 | /// 81 | /// ```no_run 82 | /// extern crate websocket; 83 | /// # fn main() { 84 | /// use std::thread; 85 | /// use websocket::Message; 86 | /// use websocket::sync::Server; 87 | /// 88 | /// let server = Server::bind("127.0.0.1:1234").unwrap(); 89 | /// 90 | /// for connection in server.filter_map(Result::ok) { 91 | /// // Spawn a new thread for each connection. 92 | /// thread::spawn(move || { 93 | /// let mut client = connection.accept().unwrap(); 94 | /// 95 | /// let message = Message::text("Hello, client!"); 96 | /// let _ = client.send_message(&message); 97 | /// 98 | /// // ... 99 | /// }); 100 | /// } 101 | /// # } 102 | /// ``` 103 | pub fn bind(addr: A) -> io::Result { 104 | Ok(Server { 105 | listener: TcpListener::bind(&addr)?, 106 | ssl_acceptor: NoTlsAcceptor, 107 | }) 108 | } 109 | 110 | /// Wait for and accept an incoming WebSocket connection, returning a WebSocketRequest 111 | pub fn accept(&mut self) -> AcceptResult { 112 | let stream = match self.listener.accept() { 113 | Ok(s) => s.0, 114 | Err(e) => { 115 | return Err(InvalidConnection { 116 | stream: None, 117 | parsed: None, 118 | buffer: None, 119 | error: e.into(), 120 | }); 121 | } 122 | }; 123 | 124 | match stream.into_ws() { 125 | Ok(u) => Ok(u), 126 | Err((s, r, b, e)) => Err(InvalidConnection { 127 | stream: Some(s), 128 | parsed: r, 129 | buffer: b, 130 | error: e, 131 | }), 132 | } 133 | } 134 | 135 | /// Create a new independently owned handle to the underlying socket. 136 | pub fn try_clone(&self) -> io::Result { 137 | let inner = self.listener.try_clone()?; 138 | Ok(Server { 139 | listener: inner, 140 | ssl_acceptor: self.ssl_acceptor.clone(), 141 | }) 142 | } 143 | } 144 | 145 | impl Iterator for WsServer { 146 | type Item = AcceptResult; 147 | 148 | fn next(&mut self) -> Option<::Item> { 149 | Some(self.accept()) 150 | } 151 | } 152 | 153 | mod tests { 154 | #[test] 155 | // test the set_nonblocking() method for Server. 156 | // Some of this is copied from 157 | // https://doc.rust-lang.org/src/std/net/tcp.rs.html#1413 158 | fn set_nonblocking() { 159 | use super::*; 160 | 161 | // Test unsecure server 162 | 163 | let mut server = Server::bind("127.0.0.1:0").unwrap(); 164 | 165 | // Note that if set_nonblocking() doesn't work, but the following 166 | // fails to panic for some reason, then the .accept() method below 167 | // will block indefinitely. 168 | server.set_nonblocking(true).unwrap(); 169 | 170 | let result = server.accept(); 171 | match result { 172 | // nobody tried to establish a connection, so we expect an error 173 | Ok(_) => panic!("expected error"), 174 | Err(e) => match e.error { 175 | HyperIntoWsError::Io(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} 176 | _ => panic!("unexpected error {}"), 177 | }, 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/server/upgrade/mod.rs: -------------------------------------------------------------------------------- 1 | //! Allows you to take an existing request or stream of data and convert it into a 2 | //! WebSocket client. 3 | use crate::header::extensions::Extension; 4 | use crate::header::{ 5 | Origin, WebSocketAccept, WebSocketExtensions, WebSocketKey, WebSocketProtocol, WebSocketVersion, 6 | }; 7 | use crate::stream::Stream; 8 | use std::error::Error; 9 | use std::fmt::{self, Display, Formatter}; 10 | use std::io; 11 | 12 | use hyper::header::{Connection, ConnectionOption, Headers, Protocol, ProtocolName, Upgrade}; 13 | use hyper::http::h1::Incoming; 14 | use hyper::method::Method; 15 | use hyper::status::StatusCode; 16 | use hyper::uri::RequestUri; 17 | use unicase::UniCase; 18 | 19 | use hyper::version::HttpVersion; 20 | 21 | pub mod sync; 22 | 23 | /// A typical request from hyper 24 | pub type Request = Incoming<(Method, RequestUri)>; 25 | 26 | /// Intermediate representation of a half created websocket session. 27 | /// Should be used to examine the client's handshake 28 | /// accept the protocols requested, route the path, etc. 29 | /// 30 | /// Users should then call `accept` or `reject` to complete the handshake 31 | /// and start a session. 32 | /// Note: if the stream in use is `AsyncRead + AsyncWrite`, then asynchronous 33 | /// functions will be available when completing the handshake. 34 | /// Otherwise if the stream is simply `Read + Write` blocking functions will be 35 | /// available to complete the handshake. 36 | pub struct WsUpgrade 37 | where 38 | S: Stream, 39 | { 40 | /// The headers that will be used in the handshake response. 41 | pub headers: Headers, 42 | /// The stream that will be used to read from / write to. 43 | pub stream: S, 44 | /// The handshake request, filled with useful metadata. 45 | pub request: Request, 46 | /// Some buffered data from the stream, if it exists. 47 | pub buffer: B, 48 | } 49 | 50 | impl WsUpgrade 51 | where 52 | S: Stream, 53 | { 54 | /// Select a protocol to use in the handshake response. 55 | pub fn use_protocol

(mut self, protocol: P) -> Self 56 | where 57 | P: Into, 58 | { 59 | upsert_header!(self.headers; WebSocketProtocol; { 60 | Some(protos) => protos.0.push(protocol.into()), 61 | None => WebSocketProtocol(vec![protocol.into()]) 62 | }); 63 | self 64 | } 65 | 66 | /// Select an extension to use in the handshake response. 67 | pub fn use_extension(mut self, extension: Extension) -> Self { 68 | upsert_header!(self.headers; WebSocketExtensions; { 69 | Some(protos) => protos.0.push(extension), 70 | None => WebSocketExtensions(vec![extension]) 71 | }); 72 | self 73 | } 74 | 75 | /// Select multiple extensions to use in the connection 76 | pub fn use_extensions(mut self, extensions: I) -> Self 77 | where 78 | I: IntoIterator, 79 | { 80 | let mut extensions: Vec = extensions.into_iter().collect(); 81 | upsert_header!(self.headers; WebSocketExtensions; { 82 | Some(protos) => protos.0.append(&mut extensions), 83 | None => WebSocketExtensions(extensions) 84 | }); 85 | self 86 | } 87 | 88 | /// Drop the connection without saying anything. 89 | pub fn drop(self) { 90 | ::std::mem::drop(self); 91 | } 92 | 93 | /// A list of protocols requested from the client. 94 | pub fn protocols(&self) -> &[String] { 95 | self.request 96 | .headers 97 | .get::() 98 | .map(|p| p.0.as_slice()) 99 | .unwrap_or(&[]) 100 | } 101 | 102 | /// A list of extensions requested from the client. 103 | pub fn extensions(&self) -> &[Extension] { 104 | self.request 105 | .headers 106 | .get::() 107 | .map(|e| e.0.as_slice()) 108 | .unwrap_or(&[]) 109 | } 110 | 111 | /// The client's websocket accept key. 112 | pub fn key(&self) -> Option<&[u8; 16]> { 113 | self.request.headers.get::().map(|k| &(k.0).0) 114 | } 115 | 116 | /// The client's websocket version. 117 | pub fn version(&self) -> Option<&WebSocketVersion> { 118 | self.request.headers.get::() 119 | } 120 | 121 | /// The original request URI. 122 | pub fn uri(&self) -> String { 123 | format!("{}", self.request.subject.1) 124 | } 125 | 126 | /// Origin of the client 127 | pub fn origin(&self) -> Option<&str> { 128 | self.request.headers.get::().map(|o| &o.0 as &str) 129 | } 130 | 131 | fn send(&mut self, status: StatusCode) -> io::Result<()> { 132 | let data = format!( 133 | "{} {}\r\n{}\r\n", 134 | self.request.version, status, self.headers 135 | ); 136 | self.stream.write_all(data.as_bytes())?; 137 | Ok(()) 138 | } 139 | 140 | #[doc(hidden)] 141 | pub fn prepare_headers(&mut self, custom: Option<&Headers>) -> StatusCode { 142 | if let Some(headers) = custom { 143 | self.headers.extend(headers.iter()); 144 | } 145 | // NOTE: we know there is a key because this is a valid request 146 | // i.e. to construct this you must go through the validate function 147 | let key = self.request.headers.get::().unwrap(); 148 | self.headers.set(WebSocketAccept::new(key)); 149 | self.headers 150 | .set(Connection(vec![ConnectionOption::ConnectionHeader( 151 | UniCase("Upgrade".to_string()), 152 | )])); 153 | self.headers 154 | .set(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])); 155 | 156 | StatusCode::SwitchingProtocols 157 | } 158 | } 159 | 160 | /// Errors that can occur when one tries to upgrade a connection to a 161 | /// websocket connection. 162 | #[derive(Debug)] 163 | pub enum HyperIntoWsError { 164 | /// The HTTP method in a valid websocket upgrade request must be GET 165 | MethodNotGet, 166 | /// Currently HTTP 2 is not supported 167 | UnsupportedHttpVersion, 168 | /// Currently only WebSocket13 is supported (RFC6455) 169 | UnsupportedWebsocketVersion, 170 | /// A websocket upgrade request must contain a key 171 | NoSecWsKeyHeader, 172 | /// A websocket upgrade request must ask to upgrade to a `websocket` 173 | NoWsUpgradeHeader, 174 | /// A websocket upgrade request must contain an `Upgrade` header 175 | NoUpgradeHeader, 176 | /// A websocket upgrade request's `Connection` header must be `Upgrade` 177 | NoWsConnectionHeader, 178 | /// A websocket upgrade request must contain a `Connection` header 179 | NoConnectionHeader, 180 | /// IO error from reading the underlying socket 181 | Io(io::Error), 182 | /// Error while parsing an incoming request 183 | Parsing(::hyper::Error), 184 | } 185 | 186 | impl Display for HyperIntoWsError { 187 | fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { 188 | fmt.write_str(self.description()) 189 | } 190 | } 191 | 192 | impl Error for HyperIntoWsError { 193 | fn description(&self) -> &str { 194 | use self::HyperIntoWsError::*; 195 | match *self { 196 | MethodNotGet => "Request method must be GET", 197 | UnsupportedHttpVersion => "Unsupported request HTTP version", 198 | UnsupportedWebsocketVersion => "Unsupported WebSocket version", 199 | NoSecWsKeyHeader => "Missing Sec-WebSocket-Key header", 200 | NoWsUpgradeHeader => "Invalid Upgrade WebSocket header", 201 | NoUpgradeHeader => "Missing Upgrade WebSocket header", 202 | NoWsConnectionHeader => "Invalid Connection WebSocket header", 203 | NoConnectionHeader => "Missing Connection WebSocket header", 204 | Io(ref e) => e.description(), 205 | Parsing(ref e) => e.description(), 206 | } 207 | } 208 | 209 | fn cause(&self) -> Option<&dyn Error> { 210 | match *self { 211 | HyperIntoWsError::Io(ref e) => Some(e), 212 | HyperIntoWsError::Parsing(ref e) => Some(e), 213 | _ => None, 214 | } 215 | } 216 | } 217 | 218 | impl From for HyperIntoWsError { 219 | fn from(err: io::Error) -> Self { 220 | HyperIntoWsError::Io(err) 221 | } 222 | } 223 | 224 | impl From<::hyper::Error> for HyperIntoWsError { 225 | fn from(err: ::hyper::Error) -> Self { 226 | HyperIntoWsError::Parsing(err) 227 | } 228 | } 229 | 230 | /// Check whether an incoming request is a valid WebSocket upgrade attempt. 231 | pub fn validate( 232 | method: &Method, 233 | version: HttpVersion, 234 | headers: &Headers, 235 | ) -> Result<(), HyperIntoWsError> { 236 | if *method != Method::Get { 237 | return Err(HyperIntoWsError::MethodNotGet); 238 | } 239 | 240 | if version == HttpVersion::Http09 || version == HttpVersion::Http10 { 241 | return Err(HyperIntoWsError::UnsupportedHttpVersion); 242 | } 243 | 244 | if let Some(version) = headers.get::() { 245 | if version != &WebSocketVersion::WebSocket13 { 246 | return Err(HyperIntoWsError::UnsupportedWebsocketVersion); 247 | } 248 | } 249 | 250 | if headers.get::().is_none() { 251 | return Err(HyperIntoWsError::NoSecWsKeyHeader); 252 | } 253 | 254 | match headers.get() { 255 | Some(&Upgrade(ref upgrade)) => { 256 | if upgrade.iter().all(|u| u.name != ProtocolName::WebSocket) { 257 | return Err(HyperIntoWsError::NoWsUpgradeHeader); 258 | } 259 | } 260 | None => return Err(HyperIntoWsError::NoUpgradeHeader), 261 | }; 262 | 263 | fn check_connection_header(headers: &[ConnectionOption]) -> bool { 264 | for header in headers { 265 | if let ConnectionOption::ConnectionHeader(ref h) = *header { 266 | if UniCase(h as &str) == UniCase("upgrade") { 267 | return true; 268 | } 269 | } 270 | } 271 | false 272 | } 273 | 274 | match headers.get() { 275 | Some(&Connection(ref connection)) => { 276 | if !check_connection_header(connection) { 277 | return Err(HyperIntoWsError::NoWsConnectionHeader); 278 | } 279 | } 280 | None => return Err(HyperIntoWsError::NoConnectionHeader), 281 | }; 282 | 283 | Ok(()) 284 | } 285 | -------------------------------------------------------------------------------- /src/server/upgrade/sync.rs: -------------------------------------------------------------------------------- 1 | //! Allows you to take an existing request or stream of data and convert it into a 2 | //! WebSocket client. 3 | use crate::client::sync::Client; 4 | use crate::server::upgrade::{validate, HyperIntoWsError, Request, WsUpgrade}; 5 | use crate::stream::sync::{AsTcpStream, Stream}; 6 | use std::io; 7 | use std::net::TcpStream; 8 | 9 | use hyper::buffer::BufReader; 10 | use hyper::header::Headers; 11 | use hyper::http::h1::parse_request; 12 | use hyper::http::h1::Incoming; 13 | use hyper::net::NetworkStream; 14 | use hyper::status::StatusCode; 15 | 16 | /// This crate uses buffered readers to read in the handshake quickly, in order to 17 | /// interface with other use cases that don't use buffered readers the buffered readers 18 | /// is deconstructed when it is returned to the user and given as the underlying 19 | /// reader and the buffer. 20 | /// 21 | /// This struct represents bytes that have already been read in from the stream. 22 | /// A slice of valid data in this buffer can be obtained by: `&buf[pos..cap]`. 23 | #[derive(Debug)] 24 | pub struct Buffer { 25 | /// the contents of the buffered stream data 26 | pub buf: Vec, 27 | /// the current position of cursor in the buffer 28 | /// Any data before `pos` has already been read and parsed. 29 | pub pos: usize, 30 | /// the last location of valid data 31 | /// Any data after `cap` is not valid. 32 | pub cap: usize, 33 | } 34 | 35 | /// If you have your requests separate from your stream you can use this struct 36 | /// to upgrade the connection based on the request given 37 | /// (the request should be a handshake). 38 | pub struct RequestStreamPair(pub S, pub Request); 39 | 40 | /// The synchronous specialization of `WsUpgrade`. 41 | /// See the `WsUpgrade` docs for usage and the extra synchronous methods 42 | /// given by this specialization. 43 | pub type Upgrade = WsUpgrade>; 44 | 45 | /// These methods are the synchronous ways of accepting and rejecting a websocket 46 | /// handshake. 47 | impl WsUpgrade> 48 | where 49 | S: Stream, 50 | { 51 | /// Accept the handshake request and send a response, 52 | /// if nothing goes wrong a client will be created. 53 | pub fn accept(self) -> Result, (S, io::Error)> { 54 | self.internal_accept(None) 55 | } 56 | 57 | /// Accept the handshake request and send a response while 58 | /// adding on a few headers. These headers are added before the required 59 | /// headers are, so some might be overwritten. 60 | pub fn accept_with(self, custom_headers: &Headers) -> Result, (S, io::Error)> { 61 | self.internal_accept(Some(custom_headers)) 62 | } 63 | 64 | fn internal_accept(mut self, headers: Option<&Headers>) -> Result, (S, io::Error)> { 65 | let status = self.prepare_headers(headers); 66 | 67 | if let Err(e) = self.send(status) { 68 | return Err((self.stream, e)); 69 | } 70 | 71 | let stream = match self.buffer { 72 | Some(Buffer { buf, pos, cap }) => BufReader::from_parts(self.stream, buf, pos, cap), 73 | None => BufReader::new(self.stream), 74 | }; 75 | 76 | Ok(Client::unchecked(stream, self.headers, false)) 77 | } 78 | 79 | /// Reject the client's request to make a websocket connection. 80 | pub fn reject(self) -> Result { 81 | self.internal_reject(None) 82 | } 83 | 84 | /// Reject the client's request to make a websocket connection 85 | /// and send extra headers. 86 | pub fn reject_with(self, headers: &Headers) -> Result { 87 | self.internal_reject(Some(headers)) 88 | } 89 | 90 | fn internal_reject(mut self, headers: Option<&Headers>) -> Result { 91 | if let Some(custom) = headers { 92 | self.headers.extend(custom.iter()); 93 | } 94 | match self.send(StatusCode::BadRequest) { 95 | Ok(()) => Ok(self.stream), 96 | Err(e) => Err((self.stream, e)), 97 | } 98 | } 99 | } 100 | 101 | impl WsUpgrade 102 | where 103 | S: Stream + AsTcpStream, 104 | { 105 | /// Get a handle to the underlying TCP stream, useful to be able to set 106 | /// TCP options, etc. 107 | pub fn tcp_stream(&self) -> &TcpStream { 108 | self.stream.as_tcp() 109 | } 110 | } 111 | 112 | /// Trait to take a stream or similar and attempt to recover the start of a 113 | /// websocket handshake from it. 114 | /// Should be used when a stream might contain a request for a websocket session. 115 | /// 116 | /// If an upgrade request can be parsed, one can accept or deny the handshake with 117 | /// the `WsUpgrade` struct. 118 | /// Otherwise the original stream is returned along with an error. 119 | /// 120 | /// Note: the stream is owned because the websocket client expects to own its stream. 121 | /// 122 | /// This is already implemented for all Streams, which means all types with Read + Write. 123 | /// 124 | /// # Example 125 | /// 126 | /// ```rust,no_run 127 | /// use std::net::TcpListener; 128 | /// use std::net::TcpStream; 129 | /// use websocket::sync::server::upgrade::IntoWs; 130 | /// use websocket::sync::Client; 131 | /// 132 | /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); 133 | /// 134 | /// for stream in listener.incoming().filter_map(Result::ok) { 135 | /// let mut client: Client = match stream.into_ws() { 136 | /// Ok(upgrade) => { 137 | /// match upgrade.accept() { 138 | /// Ok(client) => client, 139 | /// Err(_) => panic!(), 140 | /// } 141 | /// }, 142 | /// Err(_) => panic!(), 143 | /// }; 144 | /// } 145 | /// ``` 146 | pub trait IntoWs { 147 | /// The type of stream this upgrade process is working with (TcpStream, etc.) 148 | type Stream: Stream; 149 | /// An error value in case the stream is not asking for a websocket connection 150 | /// or something went wrong. It is common to also include the stream here. 151 | type Error; 152 | /// Attempt to parse the start of a Websocket handshake, later with the returned 153 | /// `WsUpgrade` struct, call `accept` to start a websocket client, and `reject` to 154 | /// send a handshake rejection response. 155 | fn into_ws(self) -> Result, Self::Error>; 156 | } 157 | 158 | impl IntoWs for S 159 | where 160 | S: Stream, 161 | { 162 | type Stream = S; 163 | type Error = (S, Option, Option, HyperIntoWsError); 164 | 165 | fn into_ws(self) -> Result, Self::Error> { 166 | let mut reader = BufReader::new(self); 167 | let request = parse_request(&mut reader); 168 | 169 | let (stream, buf, pos, cap) = reader.into_parts(); 170 | let buffer = Some(Buffer { buf, pos, cap }); 171 | 172 | let request = match request { 173 | Ok(r) => r, 174 | Err(e) => return Err((stream, None, buffer, e.into())), 175 | }; 176 | 177 | match validate(&request.subject.0, request.version, &request.headers) { 178 | Ok(_) => Ok(WsUpgrade { 179 | headers: Headers::new(), 180 | stream, 181 | request, 182 | buffer, 183 | }), 184 | Err(e) => Err((stream, Some(request), buffer, e)), 185 | } 186 | } 187 | } 188 | 189 | impl IntoWs for RequestStreamPair 190 | where 191 | S: Stream, 192 | { 193 | type Stream = S; 194 | type Error = (S, Request, HyperIntoWsError); 195 | 196 | fn into_ws(self) -> Result, Self::Error> { 197 | match validate(&self.1.subject.0, self.1.version, &self.1.headers) { 198 | Ok(_) => Ok(WsUpgrade { 199 | headers: Headers::new(), 200 | stream: self.0, 201 | request: self.1, 202 | buffer: None, 203 | }), 204 | Err(e) => Err((self.0, self.1, e)), 205 | } 206 | } 207 | } 208 | 209 | /// Upgrade a hyper connection to a websocket one. 210 | /// 211 | /// A hyper request is implicitly defined as a stream from other `impl`s of Stream. 212 | /// Until trait impl specialization comes along, we use this struct to differentiate 213 | /// a hyper request (which already has parsed headers) from a normal stream. 214 | /// 215 | /// Using this method, one can start a hyper server and check if each request 216 | /// is a websocket upgrade request, if so you can use websockets and hyper on the 217 | /// same port! 218 | /// 219 | /// ```rust,no_run 220 | /// # extern crate hyper; 221 | /// # extern crate websocket; 222 | /// # fn main() { 223 | /// use hyper::server::{Server, Request, Response}; 224 | /// use websocket::Message; 225 | /// use websocket::sync::server::upgrade::IntoWs; 226 | /// use websocket::sync::server::upgrade::HyperRequest; 227 | /// 228 | /// Server::http("0.0.0.0:80").unwrap().handle(move |req: Request, res: Response| { 229 | /// match HyperRequest(req).into_ws() { 230 | /// Ok(upgrade) => { 231 | /// // `accept` sends a successful handshake, no need to worry about res 232 | /// let mut client = match upgrade.accept() { 233 | /// Ok(c) => c, 234 | /// Err(_) => panic!(), 235 | /// }; 236 | /// 237 | /// client.send_message(&Message::text("its free real estate")); 238 | /// }, 239 | /// 240 | /// Err((request, err)) => { 241 | /// // continue using the request as normal, "echo uri" 242 | /// res.send(b"Try connecting over ws instead.").unwrap(); 243 | /// }, 244 | /// }; 245 | /// }) 246 | /// .unwrap(); 247 | /// # } 248 | /// ``` 249 | pub struct HyperRequest<'a, 'b: 'a>(pub ::hyper::server::Request<'a, 'b>); 250 | 251 | impl<'a, 'b> IntoWs for HyperRequest<'a, 'b> { 252 | type Stream = &'a mut &'b mut dyn NetworkStream; 253 | type Error = (::hyper::server::Request<'a, 'b>, HyperIntoWsError); 254 | 255 | fn into_ws(self) -> Result, Self::Error> { 256 | if let Err(e) = validate(&self.0.method, self.0.version, &self.0.headers) { 257 | return Err((self.0, e)); 258 | } 259 | 260 | let (_, method, headers, uri, version, reader) = self.0.deconstruct(); 261 | 262 | let reader = reader.into_inner(); 263 | let (buf, pos, cap) = reader.take_buf(); 264 | let stream = reader.get_mut(); 265 | 266 | Ok(Upgrade { 267 | headers: Headers::new(), 268 | stream, 269 | buffer: Some(Buffer { buf, pos, cap }), 270 | request: Incoming { 271 | version, 272 | headers, 273 | subject: (method, uri), 274 | }, 275 | }) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /websocket-base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "websocket-base" 3 | version = "0.26.2" 4 | authors = ["cyderize ", "Michael Eden "] 5 | edition = "2018" 6 | 7 | description = "A WebSocket (RFC6455) library for Rust: low-level component. It contains HTTP-independent aspect of WebSockets" 8 | 9 | documentation = "https://docs.rs/websocket-base/" 10 | repository = "https://github.com/websockets-rs/rust-websocket" 11 | 12 | keywords = ["websocket", "websockets", "rfc6455", "async", "tokio"] 13 | categories = ["asynchronous", "network-programming", "web-programming", "web-programming::websocket"] 14 | 15 | license = "MIT" 16 | 17 | 18 | [dependencies] 19 | byteorder = "1.0" 20 | rand = "0.7.3" 21 | bitflags = "1.0.4" 22 | base64 = "0.13.0" 23 | sha-1 = "0.9" 24 | bytes = { version = "0.4", optional = true } 25 | futures = { version = "0.1", optional = true } 26 | native-tls = { version = "0.2.1", optional = true } 27 | tokio-codec = { version = "0.1", optional = true } 28 | tokio-io = { version = "0.1", optional = true } 29 | tokio-tls = { version = "0.2.0", optional = true } 30 | tokio-tcp = { version = "0.1", optional = true } 31 | 32 | [dev-dependencies.tokio] 33 | version = "0.1" 34 | default-features = false 35 | features = ["codec", "tcp", "rt-full"] 36 | 37 | [features] 38 | default = ["sync", "async", "sync-ssl", "async-ssl"] 39 | sync = [] 40 | sync-ssl = ["native-tls", "sync"] 41 | async = ["tokio-codec", "tokio-io", "tokio-tcp", "bytes", "futures"] 42 | async-ssl = ["native-tls", "tokio-tls", "async"] 43 | -------------------------------------------------------------------------------- /websocket-base/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Rust Websockets Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /websocket-base/src/codec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ws; 2 | -------------------------------------------------------------------------------- /websocket-base/src/codec/ws.rs: -------------------------------------------------------------------------------- 1 | //! Send websocket messages and dataframes asynchronously. 2 | //! 3 | //! This module provides codecs that can be be used with `tokio` to create 4 | //! asynchronous streams that can serialize/deserialize websocket messages 5 | //! (and dataframes for users that want low level control). 6 | //! 7 | //! For websocket messages, see the documentation for `MessageCodec`, for 8 | //! dataframes see the documentation for `DataFrameCodec` 9 | 10 | extern crate bytes; 11 | extern crate tokio_codec; 12 | 13 | use std::borrow::Borrow; 14 | use std::io::Cursor; 15 | use std::marker::PhantomData; 16 | use std::mem; 17 | 18 | use self::bytes::BufMut; 19 | use self::bytes::BytesMut; 20 | use self::tokio_codec::Decoder; 21 | use self::tokio_codec::Encoder; 22 | 23 | use crate::dataframe::DataFrame; 24 | use crate::message::OwnedMessage; 25 | use crate::result::WebSocketError; 26 | use crate::ws::dataframe::DataFrame as DataFrameTrait; 27 | use crate::ws::message::Message as MessageTrait; 28 | use crate::ws::util::header::read_header; 29 | 30 | /// Even though a websocket connection may look perfectly symmetrical 31 | /// in reality there are small differences between clients and servers. 32 | /// This type is passed to the codecs to inform them of what role they are in 33 | /// (i.e. that of a Client or Server). 34 | /// 35 | /// For those familiar with the protocol, this decides whether the data should be 36 | /// masked or not. 37 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 38 | pub enum Context { 39 | /// Set the codec to act in `Server` mode, used when 40 | /// implementing a websocket server. 41 | Server, 42 | /// Set the codec to act in `Client` mode, used when 43 | /// implementing a websocket client. 44 | Client, 45 | } 46 | 47 | /************** 48 | * Dataframes * 49 | **************/ 50 | 51 | /// A codec for decoding and encoding websocket dataframes. 52 | /// 53 | /// This codec decodes dataframes into the crates default implementation 54 | /// of `Dataframe` but can encode and send any struct that implements the 55 | /// `ws::Dataframe` trait. The type of struct to encode is given by the `D` 56 | /// type parameter in the struct. 57 | /// 58 | /// Using dataframes directly is meant for users who want low-level access to the 59 | /// connection. If you don't want to do anything low-level please use the 60 | /// `MessageCodec` codec instead, or better yet use the `ClientBuilder` to make 61 | /// clients and the `Server` to make servers. 62 | pub struct DataFrameCodec { 63 | is_server: bool, 64 | frame_type: PhantomData, 65 | } 66 | 67 | impl DataFrameCodec { 68 | /// Create a new `DataFrameCodec` struct using the crate's implementation 69 | /// of dataframes for reading and writing dataframes. 70 | /// 71 | /// Use this method if you don't want to provide a custom implementation 72 | /// for your dataframes. 73 | pub fn default(context: Context) -> Self { 74 | DataFrameCodec::new(context) 75 | } 76 | } 77 | 78 | impl DataFrameCodec { 79 | /// Create a new `DataFrameCodec` struct using any implementation of 80 | /// `ws::Dataframe` you want. This is useful if you want to manipulate 81 | /// the websocket layer very specifically. 82 | /// 83 | /// If you only want to be able to send and receive the crate's 84 | /// `DataFrame` struct use `.default(Context)` instead. 85 | pub fn new(context: Context) -> DataFrameCodec { 86 | DataFrameCodec { 87 | is_server: context == Context::Server, 88 | frame_type: PhantomData, 89 | } 90 | } 91 | } 92 | 93 | impl Decoder for DataFrameCodec { 94 | type Item = DataFrame; 95 | type Error = WebSocketError; 96 | 97 | // TODO: do not retry to read the header on each new data (keep a buffer) 98 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 99 | let (header, bytes_read) = { 100 | // we'll make a fake reader and keep track of the bytes read 101 | let mut reader = Cursor::new(src.as_ref()); 102 | 103 | // read header to get the size, bail if not enough 104 | let header = match read_header(&mut reader) { 105 | Ok(head) => head, 106 | Err(WebSocketError::NoDataAvailable) => return Ok(None), 107 | Err(e) => return Err(e), 108 | }; 109 | 110 | (header, reader.position()) 111 | }; 112 | 113 | // check if we have enough bytes to continue 114 | if header.len + bytes_read > src.len() as u64 { 115 | return Ok(None); 116 | } 117 | 118 | // TODO: using usize is not the right thing here (can be larger) 119 | let _ = src.split_to(bytes_read as usize); 120 | let body = src.split_to(header.len as usize).to_vec(); 121 | 122 | // construct a dataframe 123 | Ok(Some(DataFrame::read_dataframe_body( 124 | header, 125 | body, 126 | self.is_server, 127 | )?)) 128 | } 129 | } 130 | 131 | impl Encoder for DataFrameCodec 132 | where 133 | D: Borrow, 134 | { 135 | type Item = D; 136 | type Error = WebSocketError; 137 | 138 | fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { 139 | let masked = !self.is_server; 140 | let frame_size = item.borrow().frame_size(masked); 141 | if frame_size > dst.remaining_mut() { 142 | dst.reserve(frame_size); 143 | } 144 | item.borrow().write_to(&mut dst.writer(), masked) 145 | } 146 | } 147 | 148 | /************ 149 | * Messages * 150 | ************/ 151 | 152 | /// A codec for asynchronously decoding and encoding websocket messages. 153 | /// 154 | /// This codec decodes messages into the `OwnedMessage` struct, so using this 155 | /// the user will receive messages as `OwnedMessage`s. However it can encode 156 | /// any type of message that implements the `ws::Message` trait (that type is 157 | /// decided by the `M` type parameter) like `OwnedMessage` and `Message`. 158 | /// 159 | /// Warning: if you don't know what your doing or want a simple websocket connection 160 | /// please use the `ClientBuilder` or the `Server` structs. You should only use this 161 | /// after a websocket handshake has already been completed on the stream you are 162 | /// using. 163 | /// 164 | ///# Example (for the high-level `websocket` crate) 165 | /// 166 | ///```rust,ignore 167 | ///# extern crate tokio; 168 | ///# extern crate websocket; 169 | ///# extern crate hyper; 170 | ///# use std::io::{self, Cursor}; 171 | ///use websocket::async::{MessageCodec, MsgCodecCtx}; 172 | ///# use websocket::{Message, OwnedMessage}; 173 | ///# use websocket::ws::Message as MessageTrait; 174 | ///# use websocket::stream::ReadWritePair; 175 | ///# use websocket::async::futures::{Future, Sink, Stream}; 176 | ///# use hyper::http::h1::Incoming; 177 | ///# use hyper::version::HttpVersion; 178 | ///# use hyper::header::Headers; 179 | ///# use hyper::method::Method; 180 | ///# use hyper::uri::RequestUri; 181 | ///# use hyper::status::StatusCode; 182 | ///# use tokio::codec::Decoder; 183 | ///# fn main() { 184 | /// 185 | ///let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 186 | ///let mut input = Vec::new(); 187 | ///Message::text("50 schmeckels").serialize(&mut input, false); 188 | /// 189 | ///let f = MessageCodec::default(MsgCodecCtx::Client) 190 | /// .framed(ReadWritePair(Cursor::new(input), Cursor::new(vec![]))) 191 | /// .into_future() 192 | /// .map_err(|e| e.0) 193 | /// .map(|(m, _)| { 194 | /// assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string()))); 195 | /// }); 196 | /// 197 | ///runtime.block_on(f).unwrap(); 198 | ///# } 199 | pub struct MessageCodec 200 | where 201 | M: MessageTrait, 202 | { 203 | buffer: Vec, 204 | dataframe_codec: DataFrameCodec, 205 | message_type: PhantomData, 206 | } 207 | 208 | impl MessageCodec { 209 | /// Create a new `MessageCodec` with a role of `context` (either `Client` 210 | /// or `Server`) to read and write messages asynchronously. 211 | /// 212 | /// This will create the crate's default codec which sends and receives 213 | /// `OwnedMessage` structs. The message data has to be sent to an intermediate 214 | /// buffer anyway so sending owned data is preferable. 215 | /// 216 | /// If you have your own implementation of websocket messages, you can 217 | /// use the `new` method to create a codec for that implementation. 218 | pub fn default(context: Context) -> Self { 219 | Self::new(context) 220 | } 221 | } 222 | 223 | impl MessageCodec 224 | where 225 | M: MessageTrait, 226 | { 227 | /// Creates a codec that can encode a custom implementation of a websocket 228 | /// message. 229 | /// 230 | /// If you just want to use a normal codec without a specific implementation 231 | /// of a websocket message, take a look at `MessageCodec::default`. 232 | pub fn new(context: Context) -> MessageCodec { 233 | MessageCodec { 234 | buffer: Vec::new(), 235 | dataframe_codec: DataFrameCodec::new(context), 236 | message_type: PhantomData, 237 | } 238 | } 239 | } 240 | 241 | impl Decoder for MessageCodec 242 | where 243 | M: MessageTrait, 244 | { 245 | type Item = OwnedMessage; 246 | type Error = WebSocketError; 247 | 248 | fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { 249 | while let Some(frame) = self.dataframe_codec.decode(src)? { 250 | let is_first = self.buffer.is_empty(); 251 | let finished = frame.finished; 252 | 253 | match frame.opcode as u8 { 254 | // continuation code 255 | 0 if is_first => { 256 | return Err(WebSocketError::ProtocolError( 257 | "Unexpected continuation data frame opcode", 258 | )); 259 | } 260 | // control frame 261 | 8..=15 => { 262 | return Ok(Some(OwnedMessage::from_dataframes(vec![frame])?)); 263 | } 264 | // data frame 265 | 1..=7 if !is_first => { 266 | return Err(WebSocketError::ProtocolError( 267 | "Unexpected data frame opcode", 268 | )); 269 | } 270 | // its good 271 | _ => { 272 | self.buffer.push(frame); 273 | } 274 | }; 275 | 276 | if finished { 277 | let buffer = mem::replace(&mut self.buffer, Vec::new()); 278 | return Ok(Some(OwnedMessage::from_dataframes(buffer)?)); 279 | } 280 | } 281 | 282 | Ok(None) 283 | } 284 | } 285 | 286 | impl Encoder for MessageCodec 287 | where 288 | M: MessageTrait, 289 | { 290 | type Item = M; 291 | type Error = WebSocketError; 292 | 293 | fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { 294 | let masked = !self.dataframe_codec.is_server; 295 | let frame_size = item.message_size(masked); 296 | if frame_size > dst.remaining_mut() { 297 | dst.reserve(frame_size); 298 | } 299 | item.serialize(&mut dst.writer(), masked) 300 | } 301 | } 302 | 303 | #[cfg(test)] 304 | mod tests { 305 | extern crate tokio; 306 | use super::*; 307 | use crate::message::CloseData; 308 | use crate::message::Message; 309 | use crate::stream::ReadWritePair; 310 | use futures::{Future, Sink, Stream}; 311 | use std::io::Cursor; 312 | 313 | #[test] 314 | fn owned_message_predicts_size() { 315 | let messages = vec![ 316 | OwnedMessage::Text("nilbog".to_string()), 317 | OwnedMessage::Binary(vec![1, 2, 3, 4]), 318 | OwnedMessage::Binary(vec![42; 256]), 319 | OwnedMessage::Binary(vec![42; 65535]), 320 | OwnedMessage::Binary(vec![42; 65555]), 321 | OwnedMessage::Ping("beep".to_string().into_bytes()), 322 | OwnedMessage::Pong("boop".to_string().into_bytes()), 323 | OwnedMessage::Close(None), 324 | OwnedMessage::Close(Some(CloseData { 325 | status_code: 64, 326 | reason: "because".to_string(), 327 | })), 328 | ]; 329 | 330 | for message in messages.into_iter() { 331 | let masked_predicted = message.message_size(true); 332 | let mut masked_buf = Vec::new(); 333 | message.serialize(&mut masked_buf, true).unwrap(); 334 | assert_eq!(masked_buf.len(), masked_predicted); 335 | 336 | let unmasked_predicted = message.message_size(false); 337 | let mut unmasked_buf = Vec::new(); 338 | message.serialize(&mut unmasked_buf, false).unwrap(); 339 | assert_eq!(unmasked_buf.len(), unmasked_predicted); 340 | } 341 | } 342 | 343 | #[test] 344 | fn cow_message_predicts_size() { 345 | let messages = vec![ 346 | Message::binary(vec![1, 2, 3, 4]), 347 | Message::binary(vec![42; 256]), 348 | Message::binary(vec![42; 65535]), 349 | Message::binary(vec![42; 65555]), 350 | Message::text("nilbog".to_string()), 351 | Message::ping("beep".to_string().into_bytes()), 352 | Message::pong("boop".to_string().into_bytes()), 353 | Message::close(), 354 | Message::close_because(64, "because"), 355 | ]; 356 | 357 | for message in messages.iter() { 358 | let masked_predicted = message.message_size(true); 359 | let mut masked_buf = Vec::new(); 360 | message.serialize(&mut masked_buf, true).unwrap(); 361 | assert_eq!(masked_buf.len(), masked_predicted); 362 | 363 | let unmasked_predicted = message.message_size(false); 364 | let mut unmasked_buf = Vec::new(); 365 | message.serialize(&mut unmasked_buf, false).unwrap(); 366 | assert_eq!(unmasked_buf.len(), unmasked_predicted); 367 | } 368 | } 369 | 370 | #[test] 371 | fn message_codec_client_send_receive() { 372 | let mut input = Vec::new(); 373 | Message::text("50 schmeckels") 374 | .serialize(&mut input, false) 375 | .unwrap(); 376 | 377 | let f = MessageCodec::new(Context::Client) 378 | .framed(ReadWritePair(Cursor::new(input), Cursor::new(vec![]))) 379 | .into_future() 380 | .map_err(|e| e.0) 381 | .map(|(m, s)| { 382 | assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string()))); 383 | s 384 | }) 385 | .and_then(|s| s.send(Message::text("ethan bradberry"))) 386 | .and_then(|s| { 387 | let mut stream = s.into_parts().io; 388 | stream.1.set_position(0); 389 | println!("buffer: {:?}", stream.1); 390 | MessageCodec::default(Context::Server) 391 | .framed(ReadWritePair(stream.1, stream.0)) 392 | .into_future() 393 | .map_err(|e| e.0) 394 | .map(|(message, _)| { 395 | assert_eq!(message, Some(Message::text("ethan bradberry").into())) 396 | }) 397 | }); 398 | 399 | tokio::runtime::Builder::new() 400 | .build() 401 | .unwrap() 402 | .block_on(f) 403 | .unwrap(); 404 | } 405 | 406 | #[test] 407 | fn message_codec_server_send_receive() { 408 | let mut runtime = tokio::runtime::Builder::new().build().unwrap(); 409 | let mut input = Vec::new(); 410 | Message::text("50 schmeckels") 411 | .serialize(&mut input, true) 412 | .unwrap(); 413 | 414 | let f = MessageCodec::new(Context::Server) 415 | .framed(ReadWritePair(Cursor::new(input), Cursor::new(vec![]))) 416 | .into_future() 417 | .map_err(|e| e.0) 418 | .map(|(m, s)| { 419 | assert_eq!(m, Some(OwnedMessage::Text("50 schmeckels".to_string()))); 420 | s 421 | }) 422 | .and_then(|s| s.send(Message::text("ethan bradberry"))) 423 | .map(|s| { 424 | let mut written = vec![]; 425 | Message::text("ethan bradberry") 426 | .serialize(&mut written, false) 427 | .unwrap(); 428 | assert_eq!(written, s.into_parts().io.1.into_inner()); 429 | }); 430 | 431 | runtime.block_on(f).unwrap(); 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /websocket-base/src/dataframe.rs: -------------------------------------------------------------------------------- 1 | //! Module containing the default implementation of data frames. 2 | use crate::result::WebSocketResult; 3 | use crate::ws::dataframe::DataFrame as DataFrameable; 4 | use crate::ws::util::header as dfh; 5 | use crate::ws::util::header::DataFrameHeader; 6 | use crate::ws::util::mask; 7 | use std::io::{self, Read, Write}; 8 | 9 | /// Represents a WebSocket data frame. 10 | /// 11 | /// The data held in a DataFrame is never masked. 12 | /// Masking/unmasking is done when sending and receiving the data frame, 13 | /// 14 | /// This DataFrame, unlike the standard Message implementation (which also 15 | /// implements the DataFrame trait), owns its entire payload. This means that calls to `payload` 16 | /// don't allocate extra memory (again unlike the default Message implementation). 17 | #[derive(Debug, Clone, PartialEq)] 18 | pub struct DataFrame { 19 | /// Whether or no this constitutes the end of a message 20 | pub finished: bool, 21 | /// The reserved portion of the data frame (RFC6455 5.2) 22 | pub reserved: [bool; 3], 23 | /// The opcode associated with this data frame 24 | pub opcode: Opcode, 25 | /// The payload associated with this data frame 26 | pub data: Vec, 27 | } 28 | 29 | impl DataFrame { 30 | /// Creates a new DataFrame. 31 | pub fn new(finished: bool, opcode: Opcode, data: Vec) -> DataFrame { 32 | DataFrame { 33 | finished, 34 | reserved: [false; 3], 35 | opcode, 36 | data, 37 | } 38 | } 39 | 40 | /// Take the body and header of a dataframe and combine it into a single 41 | /// Dataframe struct. A websocket message can be made up of many individual 42 | /// dataframes, use the methods from the Message or OwnedMessage structs to 43 | /// take many of these and create a websocket message. 44 | pub fn read_dataframe_body(header: DataFrameHeader, body: Vec) -> WebSocketResult { 45 | let finished = header.flags.contains(dfh::DataFrameFlags::FIN); 46 | 47 | let reserved = [ 48 | header.flags.contains(dfh::DataFrameFlags::RSV1), 49 | header.flags.contains(dfh::DataFrameFlags::RSV2), 50 | header.flags.contains(dfh::DataFrameFlags::RSV3), 51 | ]; 52 | 53 | let opcode = Opcode::new(header.opcode).expect("Invalid header opcode!"); 54 | 55 | let data = match header.mask { 56 | Some(mask) => mask::mask_data(mask, &body), 57 | None => body, 58 | }; 59 | 60 | Ok(DataFrame { 61 | finished, 62 | reserved, 63 | opcode, 64 | data, 65 | }) 66 | } 67 | 68 | /// Reads a DataFrame from a Reader. 69 | pub fn read_dataframe(reader: &mut R) -> WebSocketResult 70 | where 71 | R: Read, 72 | { 73 | let header = dfh::read_header(reader)?; 74 | 75 | let mut data: Vec = Vec::with_capacity(header.len as usize); 76 | let read = reader.take(header.len).read_to_end(&mut data)?; 77 | if (read as u64) < header.len { 78 | return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "incomplete payload").into()); 79 | } 80 | 81 | DataFrame::read_dataframe_body(header, data) 82 | } 83 | } 84 | 85 | impl DataFrameable for DataFrame { 86 | #[inline(always)] 87 | fn is_last(&self) -> bool { 88 | self.finished 89 | } 90 | 91 | #[inline(always)] 92 | fn opcode(&self) -> u8 { 93 | self.opcode as u8 94 | } 95 | 96 | #[inline(always)] 97 | fn reserved(&self) -> &[bool; 3] { 98 | &self.reserved 99 | } 100 | 101 | #[inline(always)] 102 | fn size(&self) -> usize { 103 | self.data.len() 104 | } 105 | 106 | #[inline(always)] 107 | fn write_payload(&self, socket: &mut dyn Write) -> WebSocketResult<()> { 108 | socket.write_all(self.data.as_slice())?; 109 | Ok(()) 110 | } 111 | 112 | #[inline(always)] 113 | fn take_payload(self) -> Vec { 114 | self.data 115 | } 116 | } 117 | 118 | /// Represents a WebSocket data frame opcode 119 | #[derive(Clone, Debug, Copy, PartialEq)] 120 | pub enum Opcode { 121 | /// A continuation data frame 122 | Continuation, 123 | /// A UTF-8 text data frame 124 | Text, 125 | /// A binary data frame 126 | Binary, 127 | /// An undefined non-control data frame 128 | NonControl1, 129 | /// An undefined non-control data frame 130 | NonControl2, 131 | /// An undefined non-control data frame 132 | NonControl3, 133 | /// An undefined non-control data frame 134 | NonControl4, 135 | /// An undefined non-control data frame 136 | NonControl5, 137 | /// A close data frame 138 | Close, 139 | /// A ping data frame 140 | Ping, 141 | /// A pong data frame 142 | Pong, 143 | /// An undefined control data frame 144 | Control1, 145 | /// An undefined control data frame 146 | Control2, 147 | /// An undefined control data frame 148 | Control3, 149 | /// An undefined control data frame 150 | Control4, 151 | /// An undefined control data frame 152 | Control5, 153 | } 154 | 155 | impl Opcode { 156 | /// Attempts to form an Opcode from a nibble. 157 | /// 158 | /// Returns the Opcode, or None if the opcode is out of range. 159 | #[warn(clippy::new_ret_no_self)] 160 | pub fn new(op: u8) -> Option { 161 | Some(match op { 162 | 0 => Opcode::Continuation, 163 | 1 => Opcode::Text, 164 | 2 => Opcode::Binary, 165 | 3 => Opcode::NonControl1, 166 | 4 => Opcode::NonControl2, 167 | 5 => Opcode::NonControl3, 168 | 6 => Opcode::NonControl4, 169 | 7 => Opcode::NonControl5, 170 | 8 => Opcode::Close, 171 | 9 => Opcode::Ping, 172 | 10 => Opcode::Pong, 173 | 11 => Opcode::Control1, 174 | 12 => Opcode::Control2, 175 | 13 => Opcode::Control3, 176 | 14 => Opcode::Control4, 177 | 15 => Opcode::Control5, 178 | _ => return None, 179 | }) 180 | } 181 | } 182 | 183 | #[cfg(all(feature = "nightly", test))] 184 | mod tests { 185 | use super::*; 186 | use test::Bencher; 187 | use ws::dataframe::DataFrame as DataFrameable; 188 | 189 | #[test] 190 | fn test_read_dataframe() { 191 | let data = b"The quick brown fox jumps over the lazy dog"; 192 | let mut dataframe = vec![0x81, 0x2B]; 193 | for i in data.iter() { 194 | dataframe.push(*i); 195 | } 196 | let obtained = DataFrame::read_dataframe(&mut &dataframe[..], false).unwrap(); 197 | let expected = DataFrame { 198 | finished: true, 199 | reserved: [false; 3], 200 | opcode: Opcode::Text, 201 | data: data.to_vec(), 202 | }; 203 | assert_eq!(obtained, expected); 204 | } 205 | 206 | #[test] 207 | fn read_incomplete_payloads() { 208 | let mut data = vec![0x8au8, 0x08, 0x19, 0xac, 0xab, 0x8a, 0x52, 0x4e, 0x05, 0x00]; 209 | let payload = vec![25, 172, 171, 138, 82, 78, 5, 0]; 210 | let short_header = DataFrame::read_dataframe(&mut &data[..1], false); 211 | let short_payload = DataFrame::read_dataframe(&mut &data[..6], false); 212 | let full_payload = DataFrame::read_dataframe(&mut &data[..], false); 213 | data.push(0xff); 214 | let more_payload = DataFrame::read_dataframe(&mut &data[..], false); 215 | 216 | match (short_header.unwrap_err(), short_payload.unwrap_err()) { 217 | (WebSocketError::NoDataAvailable, WebSocketError::NoDataAvailable) => (), 218 | _ => assert!(false), 219 | }; 220 | assert_eq!(full_payload.unwrap().data, payload); 221 | assert_eq!(more_payload.unwrap().data, payload); 222 | } 223 | 224 | #[bench] 225 | fn bench_read_dataframe(b: &mut Bencher) { 226 | let data = b"The quick brown fox jumps over the lazy dog"; 227 | let mut dataframe = vec![0x81, 0x2B]; 228 | for i in data.iter() { 229 | dataframe.push(*i); 230 | } 231 | b.iter(|| { 232 | DataFrame::read_dataframe(&mut &dataframe[..], false).unwrap(); 233 | }); 234 | } 235 | 236 | #[test] 237 | fn test_write_dataframe() { 238 | let data = b"The quick brown fox jumps over the lazy dog"; 239 | let mut expected = vec![0x81, 0x2B]; 240 | for i in data.iter() { 241 | expected.push(*i); 242 | } 243 | let dataframe = DataFrame { 244 | finished: true, 245 | reserved: [false; 3], 246 | opcode: Opcode::Text, 247 | data: data.to_vec(), 248 | }; 249 | let mut obtained = Vec::new(); 250 | dataframe.write_to(&mut obtained, false).unwrap(); 251 | 252 | assert_eq!(&obtained[..], &expected[..]); 253 | } 254 | 255 | #[bench] 256 | fn bench_write_dataframe(b: &mut Bencher) { 257 | let data = b"The quick brown fox jumps over the lazy dog"; 258 | let dataframe = DataFrame { 259 | finished: true, 260 | reserved: [false; 3], 261 | opcode: Opcode::Text, 262 | data: data.to_vec(), 263 | }; 264 | let mut writer = Vec::with_capacity(45); 265 | b.iter(|| { 266 | dataframe.write_to(&mut writer, false).unwrap(); 267 | }); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /websocket-base/src/header.rs: -------------------------------------------------------------------------------- 1 | //! This file contains some framework-agnostic aspects of WebSocket HTTP headers. 2 | 3 | extern crate rand; 4 | 5 | /// WebSocket header names 6 | pub mod names { 7 | pub const PROTOCOL: &str = "Sec-WebSocket-Protocol"; 8 | pub const ACCEPT: &str = "Sec-WebSocket-Accept"; 9 | pub const EXTENSIONS: &str = "Sec-WebSocket-Extensions"; 10 | pub const KEY: &str = "Sec-WebSocket-Key"; 11 | } 12 | 13 | extern crate base64; 14 | extern crate sha1; 15 | use sha1::{Digest, Sha1}; 16 | 17 | use crate::result::{WebSocketError, WebSocketResult}; 18 | use std::fmt::{self, Debug}; 19 | 20 | use std::str::FromStr; 21 | 22 | /// Represents a Sec-WebSocket-Key header. 23 | #[derive(PartialEq, Clone, Copy, Default)] 24 | pub struct WebSocketKey(pub [u8; 16]); 25 | 26 | impl Debug for WebSocketKey { 27 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 28 | write!(f, "WebSocketKey({})", self.serialize()) 29 | } 30 | } 31 | 32 | impl FromStr for WebSocketKey { 33 | type Err = WebSocketError; 34 | 35 | fn from_str(key: &str) -> WebSocketResult { 36 | match base64::decode(key) { 37 | Ok(vec) => { 38 | if vec.len() != 16 { 39 | return Err(WebSocketError::ProtocolError( 40 | "Sec-WebSocket-Key must be 16 bytes", 41 | )); 42 | } 43 | let mut array = [0u8; 16]; 44 | array[..16].clone_from_slice(&vec[..16]); 45 | Ok(WebSocketKey(array)) 46 | } 47 | Err(_) => Err(WebSocketError::ProtocolError( 48 | "Invalid Sec-WebSocket-Accept", 49 | )), 50 | } 51 | } 52 | } 53 | 54 | impl WebSocketKey { 55 | /// Generate a new, random WebSocketKey 56 | pub fn new() -> WebSocketKey { 57 | let key = rand::random(); 58 | WebSocketKey(key) 59 | } 60 | /// Return the Base64 encoding of this WebSocketKey 61 | pub fn serialize(&self) -> String { 62 | let WebSocketKey(key) = *self; 63 | base64::encode(&key) 64 | } 65 | } 66 | 67 | static MAGIC_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 68 | 69 | /// Represents a Sec-WebSocket-Accept header 70 | #[derive(PartialEq, Clone, Copy)] 71 | pub struct WebSocketAccept([u8; 20]); 72 | 73 | impl Debug for WebSocketAccept { 74 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 75 | write!(f, "WebSocketAccept({})", self.serialize()) 76 | } 77 | } 78 | 79 | impl FromStr for WebSocketAccept { 80 | type Err = WebSocketError; 81 | 82 | fn from_str(accept: &str) -> WebSocketResult { 83 | match base64::decode(accept) { 84 | Ok(vec) => { 85 | if vec.len() != 20 { 86 | return Err(WebSocketError::ProtocolError( 87 | "Sec-WebSocket-Accept must be 20 bytes", 88 | )); 89 | } 90 | let mut array = [0u8; 20]; 91 | array[..20].clone_from_slice(&vec[..20]); 92 | Ok(WebSocketAccept(array)) 93 | } 94 | Err(_) => Err(WebSocketError::ProtocolError( 95 | "Invalid Sec-WebSocket-Accept ", 96 | )), 97 | } 98 | } 99 | } 100 | 101 | impl WebSocketAccept { 102 | /// Create a new WebSocketAccept from the given WebSocketKey 103 | pub fn new(key: &WebSocketKey) -> WebSocketAccept { 104 | let serialized = key.serialize(); 105 | let mut concat_key = String::with_capacity(serialized.len() + 36); 106 | concat_key.push_str(&serialized[..]); 107 | concat_key.push_str(MAGIC_GUID); 108 | let hash = Sha1::digest(concat_key.as_bytes()); 109 | WebSocketAccept(hash.into()) 110 | } 111 | /// Return the Base64 encoding of this WebSocketAccept 112 | pub fn serialize(&self) -> String { 113 | let WebSocketAccept(accept) = *self; 114 | base64::encode(&accept) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /websocket-base/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is part of `websocket` crate that is independent from `hyper`. 2 | //! It contains code for processing WebSocket streams, 3 | //! (after HTTP upgrade already happened) 4 | //! WebSocket message definition, some error type. 5 | //! 6 | //! For now it still optionally depends on `futures 0.1` and `tokio-io 0.1`, but that's going to 7 | //! be extracted to a separate crate, if `rust-websocket`'s depelopment continues. 8 | //! 9 | //! Note that there is no normal example of usage so far. See usual `websocket` crate or 10 | //! alternative like `tungstenite`. 11 | 12 | #![allow(clippy::match_ref_pats, clippy::needless_doctest_main)] 13 | extern crate byteorder; 14 | 15 | extern crate rand; 16 | #[macro_use] 17 | extern crate bitflags; 18 | 19 | 20 | 21 | pub mod dataframe; 22 | pub mod header; 23 | pub mod message; 24 | pub mod result; 25 | pub mod stream; 26 | pub mod ws; 27 | 28 | pub use crate::message::Message; 29 | pub use crate::message::OwnedMessage; 30 | -------------------------------------------------------------------------------- /websocket-base/src/message.rs: -------------------------------------------------------------------------------- 1 | //! Module containing the default implementation for messages. 2 | use crate::dataframe::Opcode; 3 | use crate::result::{WebSocketError, WebSocketResult}; 4 | use crate::ws; 5 | use crate::ws::dataframe::DataFrame as DataFrameTrait; 6 | use crate::ws::util::bytes_to_string; 7 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 8 | use std::borrow::Cow; 9 | use std::io; 10 | use std::io::Write; 11 | use std::str::from_utf8; 12 | 13 | const FALSE_RESERVED_BITS: &[bool; 3] = &[false; 3]; 14 | 15 | /// Valid types of messages (in the default implementation) 16 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] 17 | pub enum Type { 18 | /// Message with UTF8 test 19 | Text = 1, 20 | /// Message containing binary data 21 | Binary = 2, 22 | /// Ping message with data 23 | Ping = 9, 24 | /// Pong message with data 25 | Pong = 10, 26 | /// Close connection message with optional reason 27 | Close = 8, 28 | } 29 | 30 | /// Represents a WebSocket message. 31 | /// 32 | /// This message also has the ability to not own its payload, and stores its entire payload in 33 | /// chunks that get written in order when the message gets sent. This makes the `write_payload` 34 | /// allocate less memory than the `payload` method (which creates a new buffer every time). 35 | /// 36 | /// Incidentally this (the default implementation of `Message`) implements the `DataFrame` trait 37 | /// because this message just gets sent as one single `DataFrame`. 38 | #[derive(Eq, PartialEq, Clone, Debug)] 39 | pub struct Message<'a> { 40 | /// Type of WebSocket message 41 | pub opcode: Type, 42 | /// Optional status code to send when closing a connection. 43 | /// (only used if this message is of Type::Close) 44 | pub cd_status_code: Option, 45 | /// Main payload 46 | pub payload: Cow<'a, [u8]>, 47 | } 48 | 49 | impl<'a> Message<'a> { 50 | fn new(code: Type, status: Option, payload: Cow<'a, [u8]>) -> Self { 51 | Message { 52 | opcode: code, 53 | cd_status_code: status, 54 | payload, 55 | } 56 | } 57 | 58 | /// Create a new WebSocket message with text data 59 | pub fn text(data: S) -> Self 60 | where 61 | S: Into>, 62 | { 63 | Message::new( 64 | Type::Text, 65 | None, 66 | match data.into() { 67 | Cow::Owned(msg) => Cow::Owned(msg.into_bytes()), 68 | Cow::Borrowed(msg) => Cow::Borrowed(msg.as_bytes()), 69 | }, 70 | ) 71 | } 72 | 73 | /// Create a new WebSocket message with binary data 74 | pub fn binary(data: B) -> Self 75 | where 76 | B: IntoCowBytes<'a>, 77 | { 78 | Message::new(Type::Binary, None, data.into()) 79 | } 80 | 81 | /// Create a new WebSocket message that signals the end of a WebSocket 82 | /// connection, although messages can still be sent after sending this 83 | pub fn close() -> Self { 84 | Message::new(Type::Close, None, Cow::Borrowed(&[0; 0])) 85 | } 86 | 87 | /// Create a new WebSocket message that signals the end of a WebSocket 88 | /// connection and provide a text reason and a status code for why. 89 | /// Messages can still be sent after sending this message. 90 | pub fn close_because(code: u16, reason: S) -> Self 91 | where 92 | S: Into>, 93 | { 94 | Message::new( 95 | Type::Close, 96 | Some(code), 97 | match reason.into() { 98 | Cow::Owned(msg) => Cow::Owned(msg.into_bytes()), 99 | Cow::Borrowed(msg) => Cow::Borrowed(msg.as_bytes()), 100 | }, 101 | ) 102 | } 103 | 104 | /// Create a ping WebSocket message, a pong is usually sent back 105 | /// after sending this with the same data 106 | pub fn ping

(data: P) -> Self 107 | where 108 | P: IntoCowBytes<'a>, 109 | { 110 | Message::new(Type::Ping, None, data.into()) 111 | } 112 | 113 | /// Create a pong WebSocket message, usually a response to a 114 | /// ping message 115 | pub fn pong

(data: P) -> Self 116 | where 117 | P: IntoCowBytes<'a>, 118 | { 119 | Message::new(Type::Pong, None, data.into()) 120 | } 121 | 122 | // TODO: change this to match conventions 123 | #[allow(clippy::wrong_self_convention)] 124 | /// Convert a ping message to a pong, keeping the data. 125 | /// This will fail if the original message is not a ping. 126 | pub fn into_pong(&mut self) -> Result<(), ()> { 127 | if self.opcode == Type::Ping { 128 | self.opcode = Type::Pong; 129 | Ok(()) 130 | } else { 131 | Err(()) 132 | } 133 | } 134 | } 135 | 136 | impl<'a> ws::dataframe::DataFrame for Message<'a> { 137 | #[inline(always)] 138 | fn is_last(&self) -> bool { 139 | true 140 | } 141 | 142 | #[inline(always)] 143 | fn opcode(&self) -> u8 { 144 | self.opcode as u8 145 | } 146 | 147 | #[inline(always)] 148 | fn reserved(&self) -> &[bool; 3] { 149 | FALSE_RESERVED_BITS 150 | } 151 | 152 | fn size(&self) -> usize { 153 | self.payload.len() + if self.cd_status_code.is_some() { 2 } else { 0 } 154 | } 155 | 156 | fn write_payload(&self, socket: &mut dyn Write) -> WebSocketResult<()> { 157 | if let Some(reason) = self.cd_status_code { 158 | socket.write_u16::(reason)?; 159 | } 160 | socket.write_all(&*self.payload)?; 161 | Ok(()) 162 | } 163 | 164 | fn take_payload(self) -> Vec { 165 | if let Some(reason) = self.cd_status_code { 166 | let mut buf = Vec::with_capacity(2 + self.payload.len()); 167 | buf.write_u16::(reason) 168 | .expect("failed to write close code in take_payload"); 169 | buf.append(&mut self.payload.into_owned()); 170 | buf 171 | } else { 172 | self.payload.into_owned() 173 | } 174 | } 175 | } 176 | 177 | impl<'a> ws::Message for Message<'a> { 178 | /// Attempt to form a message from a series of data frames 179 | fn serialize(&self, writer: &mut dyn Write, masked: bool) -> WebSocketResult<()> { 180 | self.write_to(writer, masked) 181 | } 182 | 183 | /// Returns how many bytes this message will take up 184 | fn message_size(&self, masked: bool) -> usize { 185 | self.frame_size(masked) 186 | } 187 | 188 | /// Attempt to form a message from a series of data frames 189 | fn from_dataframes(frames: Vec) -> WebSocketResult 190 | where 191 | D: DataFrameTrait, 192 | { 193 | let opcode = frames 194 | .first() 195 | .ok_or(WebSocketError::ProtocolError("No dataframes provided")) 196 | .map(ws::dataframe::DataFrame::opcode)?; 197 | let opcode = Opcode::new(opcode); 198 | 199 | let payload_size = frames.iter().map(ws::dataframe::DataFrame::size).sum(); 200 | 201 | let mut data = Vec::with_capacity(payload_size); 202 | 203 | for (i, dataframe) in frames.into_iter().enumerate() { 204 | if i > 0 && dataframe.opcode() != Opcode::Continuation as u8 { 205 | return Err(WebSocketError::ProtocolError( 206 | "Unexpected non-continuation data frame", 207 | )); 208 | } 209 | if *dataframe.reserved() != [false; 3] { 210 | return Err(WebSocketError::ProtocolError( 211 | "Unsupported reserved bits received", 212 | )); 213 | } 214 | data.append(&mut dataframe.take_payload()); 215 | } 216 | 217 | if opcode == Some(Opcode::Text) { 218 | if let Err(e) = from_utf8(data.as_slice()) { 219 | return Err(e.into()); 220 | } 221 | } 222 | 223 | let msg = match opcode { 224 | Some(Opcode::Text) => Message { 225 | opcode: Type::Text, 226 | cd_status_code: None, 227 | payload: Cow::Owned(data), 228 | }, 229 | Some(Opcode::Binary) => Message::binary(data), 230 | Some(Opcode::Close) => { 231 | if !data.is_empty() { 232 | let status_code = (&data[..]).read_u16::()?; 233 | let reason = bytes_to_string(&data[2..])?; 234 | Message::close_because(status_code, reason) 235 | } else { 236 | Message::close() 237 | } 238 | } 239 | Some(Opcode::Ping) => Message::ping(data), 240 | Some(Opcode::Pong) => Message::pong(data), 241 | _ => return Err(WebSocketError::ProtocolError("Unsupported opcode received")), 242 | }; 243 | Ok(msg) 244 | } 245 | } 246 | 247 | /// Represents an owned WebSocket message. 248 | /// 249 | /// `OwnedMessage`s are generated when the user receives a message (since the data 250 | /// has to be copied out of the network buffer anyway). 251 | /// If you would like to create a message out of borrowed data to use for sending 252 | /// please use the `Message` struct (which contains a `Cow`). 253 | /// 254 | /// Note that `OwnedMessage` and `Message` can be converted into each other. 255 | #[derive(Eq, PartialEq, Clone, Debug)] 256 | pub enum OwnedMessage { 257 | /// A message containing UTF-8 text data 258 | Text(String), 259 | /// A message containing binary data 260 | Binary(Vec), 261 | /// A message which indicates closure of the WebSocket connection. 262 | /// This message may or may not contain data. 263 | Close(Option), 264 | /// A ping message - should be responded to with a pong message. 265 | /// Usually the pong message will be sent with the same data as the 266 | /// received ping message. 267 | Ping(Vec), 268 | /// A pong message, sent in response to a Ping message, usually 269 | /// containing the same data as the received ping message. 270 | Pong(Vec), 271 | } 272 | 273 | impl OwnedMessage { 274 | /// Checks if this message is a close message. 275 | /// 276 | ///```rust 277 | ///# use websocket_base::OwnedMessage; 278 | ///assert!(OwnedMessage::Close(None).is_close()); 279 | ///``` 280 | pub fn is_close(&self) -> bool { 281 | matches!(*self, OwnedMessage::Close(_)) 282 | } 283 | 284 | /// Checks if this message is a control message. 285 | /// Control messages are either `Close`, `Ping`, or `Pong`. 286 | /// 287 | ///```rust 288 | ///# use websocket_base::OwnedMessage; 289 | ///assert!(OwnedMessage::Ping(vec![]).is_control()); 290 | ///assert!(OwnedMessage::Pong(vec![]).is_control()); 291 | ///assert!(OwnedMessage::Close(None).is_control()); 292 | ///``` 293 | pub fn is_control(&self) -> bool { 294 | matches!(*self, OwnedMessage::Close(_) | OwnedMessage::Ping(_) | OwnedMessage::Pong(_)) 295 | } 296 | 297 | /// Checks if this message is a data message. 298 | /// Data messages are either `Text` or `Binary`. 299 | /// 300 | ///```rust 301 | ///# use websocket_base::OwnedMessage; 302 | ///assert!(OwnedMessage::Text("1337".to_string()).is_data()); 303 | ///assert!(OwnedMessage::Binary(vec![]).is_data()); 304 | ///``` 305 | pub fn is_data(&self) -> bool { 306 | !self.is_control() 307 | } 308 | 309 | /// Checks if this message is a ping message. 310 | /// `Ping` messages can come at any time and usually generate a `Pong` message 311 | /// response. 312 | /// 313 | ///```rust 314 | ///# use websocket_base::OwnedMessage; 315 | ///assert!(OwnedMessage::Ping("ping".to_string().into_bytes()).is_ping()); 316 | ///``` 317 | pub fn is_ping(&self) -> bool { 318 | matches!(*self, OwnedMessage::Ping(_)) 319 | } 320 | 321 | /// Checks if this message is a pong message. 322 | /// `Pong` messages are usually sent only in response to `Ping` messages. 323 | /// 324 | ///```rust 325 | ///# use websocket_base::OwnedMessage; 326 | ///assert!(OwnedMessage::Pong("pong".to_string().into_bytes()).is_pong()); 327 | ///``` 328 | pub fn is_pong(&self) -> bool { 329 | matches!(*self, OwnedMessage::Pong(_)) 330 | } 331 | } 332 | 333 | impl ws::Message for OwnedMessage { 334 | /// Attempt to form a message from a series of data frames 335 | fn serialize(&self, writer: &mut dyn Write, masked: bool) -> WebSocketResult<()> { 336 | self.write_to(writer, masked) 337 | } 338 | 339 | /// Returns how many bytes this message will take up 340 | fn message_size(&self, masked: bool) -> usize { 341 | self.frame_size(masked) 342 | } 343 | 344 | /// Attempt to form a message from a series of data frames 345 | fn from_dataframes(frames: Vec) -> WebSocketResult 346 | where 347 | D: DataFrameTrait, 348 | { 349 | Ok(Message::from_dataframes(frames)?.into()) 350 | } 351 | } 352 | 353 | impl ws::dataframe::DataFrame for OwnedMessage { 354 | #[inline(always)] 355 | fn is_last(&self) -> bool { 356 | true 357 | } 358 | 359 | #[inline(always)] 360 | fn opcode(&self) -> u8 { 361 | (match *self { 362 | OwnedMessage::Text(_) => Type::Text, 363 | OwnedMessage::Binary(_) => Type::Binary, 364 | OwnedMessage::Close(_) => Type::Close, 365 | OwnedMessage::Ping(_) => Type::Ping, 366 | OwnedMessage::Pong(_) => Type::Pong, 367 | }) as u8 368 | } 369 | 370 | #[inline(always)] 371 | fn reserved(&self) -> &[bool; 3] { 372 | FALSE_RESERVED_BITS 373 | } 374 | 375 | fn size(&self) -> usize { 376 | match *self { 377 | OwnedMessage::Text(ref txt) => txt.len(), 378 | OwnedMessage::Binary(ref bin) => bin.len(), 379 | OwnedMessage::Ping(ref data) => data.len(), 380 | OwnedMessage::Pong(ref data) => data.len(), 381 | OwnedMessage::Close(ref data) => match data { 382 | &Some(ref c) => c.reason.len() + 2, 383 | &None => 0, 384 | }, 385 | } 386 | } 387 | 388 | fn write_payload(&self, socket: &mut dyn Write) -> WebSocketResult<()> { 389 | match *self { 390 | OwnedMessage::Text(ref txt) => socket.write_all(txt.as_bytes())?, 391 | OwnedMessage::Binary(ref bin) => socket.write_all(bin.as_slice())?, 392 | OwnedMessage::Ping(ref data) => socket.write_all(data.as_slice())?, 393 | OwnedMessage::Pong(ref data) => socket.write_all(data.as_slice())?, 394 | OwnedMessage::Close(ref data) => match data { 395 | &Some(ref c) => { 396 | socket.write_u16::(c.status_code)?; 397 | socket.write_all(c.reason.as_bytes())? 398 | } 399 | &None => (), 400 | }, 401 | }; 402 | Ok(()) 403 | } 404 | 405 | fn take_payload(self) -> Vec { 406 | match self { 407 | OwnedMessage::Text(txt) => txt.into_bytes(), 408 | OwnedMessage::Binary(bin) => bin, 409 | OwnedMessage::Ping(data) => data, 410 | OwnedMessage::Pong(data) => data, 411 | OwnedMessage::Close(data) => match data { 412 | Some(c) => { 413 | let mut buf = Vec::with_capacity(2 + c.reason.len()); 414 | buf.write_u16::(c.status_code) 415 | .expect("failed to write close code in take_payload"); 416 | buf.append(&mut c.reason.into_bytes()); 417 | buf 418 | } 419 | None => vec![], 420 | }, 421 | } 422 | } 423 | } 424 | 425 | impl From for OwnedMessage { 426 | fn from(text: String) -> Self { 427 | OwnedMessage::Text(text) 428 | } 429 | } 430 | 431 | impl From> for OwnedMessage { 432 | fn from(buf: Vec) -> Self { 433 | OwnedMessage::Binary(buf) 434 | } 435 | } 436 | 437 | impl<'m> From> for OwnedMessage { 438 | fn from(message: Message<'m>) -> Self { 439 | match message.opcode { 440 | Type::Text => { 441 | let convert = String::from_utf8_lossy(&message.payload).into_owned(); 442 | OwnedMessage::Text(convert) 443 | } 444 | Type::Close => match message.cd_status_code { 445 | Some(code) => OwnedMessage::Close(Some(CloseData { 446 | status_code: code, 447 | reason: String::from_utf8_lossy(&message.payload).into_owned(), 448 | })), 449 | None => OwnedMessage::Close(None), 450 | }, 451 | Type::Binary => OwnedMessage::Binary(message.payload.into_owned()), 452 | Type::Ping => OwnedMessage::Ping(message.payload.into_owned()), 453 | Type::Pong => OwnedMessage::Pong(message.payload.into_owned()), 454 | } 455 | } 456 | } 457 | 458 | impl<'m> From for Message<'m> { 459 | fn from(message: OwnedMessage) -> Self { 460 | match message { 461 | OwnedMessage::Text(txt) => Message::text(txt), 462 | OwnedMessage::Binary(bin) => Message::binary(bin), 463 | OwnedMessage::Close(because) => match because { 464 | Some(c) => Message::close_because(c.status_code, c.reason), 465 | None => Message::close(), 466 | }, 467 | OwnedMessage::Ping(data) => Message::ping(data), 468 | OwnedMessage::Pong(data) => Message::pong(data), 469 | } 470 | } 471 | } 472 | 473 | /// Represents data contained in a Close message 474 | #[derive(Eq, PartialEq, Clone, Debug)] 475 | pub struct CloseData { 476 | /// The status-code of the CloseData 477 | pub status_code: u16, 478 | /// The reason-phrase of the CloseData 479 | pub reason: String, 480 | } 481 | 482 | impl CloseData { 483 | /// Create a new CloseData object 484 | pub fn new(status_code: u16, reason: String) -> CloseData { 485 | CloseData { 486 | status_code, 487 | reason, 488 | } 489 | } 490 | /// Convert this into a vector of bytes 491 | pub fn into_bytes(self) -> io::Result> { 492 | let mut buf = Vec::new(); 493 | buf.write_u16::(self.status_code)?; 494 | for i in self.reason.as_bytes().iter() { 495 | buf.push(*i); 496 | } 497 | Ok(buf) 498 | } 499 | } 500 | 501 | /// Trait representing the ability to convert 502 | /// self to a `Cow<'a, [u8]>` 503 | pub trait IntoCowBytes<'a> { 504 | /// Consume `self` and produce a `Cow<'a, [u8]>` 505 | fn into(self) -> Cow<'a, [u8]>; 506 | } 507 | 508 | impl<'a> IntoCowBytes<'a> for Vec { 509 | fn into(self) -> Cow<'a, [u8]> { 510 | Cow::Owned(self) 511 | } 512 | } 513 | 514 | impl<'a> IntoCowBytes<'a> for &'a [u8] { 515 | fn into(self) -> Cow<'a, [u8]> { 516 | Cow::Borrowed(self) 517 | } 518 | } 519 | 520 | impl<'a> IntoCowBytes<'a> for Cow<'a, [u8]> { 521 | fn into(self) -> Cow<'a, [u8]> { 522 | self 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /websocket-base/src/result.rs: -------------------------------------------------------------------------------- 1 | //! The result type used within Rust-WebSocket 2 | 3 | use std::convert::From; 4 | use std::error::Error; 5 | use std::fmt; 6 | use std::io; 7 | use std::str::Utf8Error; 8 | 9 | /// The type used for WebSocket results 10 | pub type WebSocketResult = Result; 11 | 12 | 13 | /// Represents a WebSocket error 14 | #[derive(Debug)] 15 | pub enum WebSocketError { 16 | /// A WebSocket protocol error 17 | ProtocolError(&'static str), 18 | /// Invalid WebSocket data frame error 19 | DataFrameError(&'static str), 20 | /// No data available 21 | NoDataAvailable, 22 | /// An input/output error 23 | IoError(io::Error), 24 | /// A UTF-8 error 25 | Utf8Error(Utf8Error), 26 | /// Other error from higher-level crate, for downcasting 27 | Other(Box), 28 | } 29 | 30 | impl fmt::Display for WebSocketError { 31 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 32 | match self { 33 | WebSocketError::Other(x) => x.fmt(fmt)?, 34 | _ => { 35 | fmt.write_str("WebSocketError: ")?; 36 | self.fmt(fmt)?; 37 | // fmt.write_str(self.description())?; 38 | } 39 | } 40 | Ok(()) 41 | } 42 | } 43 | 44 | impl Error for WebSocketError { 45 | fn description(&self) -> &str { 46 | match *self { 47 | WebSocketError::ProtocolError(_) => "WebSocket protocol error", 48 | WebSocketError::DataFrameError(_) => "WebSocket data frame error", 49 | WebSocketError::NoDataAvailable => "No data available", 50 | WebSocketError::IoError(_) => "I/O failure", 51 | WebSocketError::Utf8Error(_) => "UTF-8 failure", 52 | WebSocketError::Other(ref e) => e.description(), 53 | } 54 | } 55 | 56 | #[allow(deprecated)] 57 | fn cause(&self) -> Option<&dyn Error> { 58 | match *self { 59 | WebSocketError::IoError(ref error) => Some(error), 60 | WebSocketError::Utf8Error(ref error) => Some(error), 61 | WebSocketError::Other(ref error) => error.cause(), 62 | _ => None, 63 | } 64 | } 65 | } 66 | 67 | impl From for WebSocketError { 68 | fn from(err: io::Error) -> WebSocketError { 69 | if err.kind() == io::ErrorKind::UnexpectedEof { 70 | return WebSocketError::NoDataAvailable; 71 | } 72 | WebSocketError::IoError(err) 73 | } 74 | } 75 | 76 | impl From for WebSocketError { 77 | fn from(err: Utf8Error) -> WebSocketError { 78 | WebSocketError::Utf8Error(err) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /websocket-base/src/stream.rs: -------------------------------------------------------------------------------- 1 | //! Provides the default stream type for WebSocket connections. 2 | 3 | use std::fmt::Arguments; 4 | use std::io::{self, Read, Write}; 5 | 6 | /// Represents a stream that can be read from, and written to. 7 | /// This is an abstraction around readable and writable things to be able 8 | /// to speak websockets over ssl, tcp, unix sockets, etc. 9 | pub trait Stream: Read + Write {} 10 | impl Stream for S where S: Read + Write {} 11 | 12 | /// If you would like to combine an input stream and an output stream into a single 13 | /// stream to talk websockets over then this is the struct for you! 14 | /// 15 | /// This is useful if you want to use different mediums for different directions. 16 | pub struct ReadWritePair(pub R, pub W) 17 | where 18 | R: Read, 19 | W: Write; 20 | 21 | impl Read for ReadWritePair 22 | where 23 | R: Read, 24 | W: Write, 25 | { 26 | #[inline(always)] 27 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 28 | self.0.read(buf) 29 | } 30 | #[inline(always)] 31 | fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { 32 | self.0.read_to_end(buf) 33 | } 34 | #[inline(always)] 35 | fn read_to_string(&mut self, buf: &mut String) -> io::Result { 36 | self.0.read_to_string(buf) 37 | } 38 | #[inline(always)] 39 | fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { 40 | self.0.read_exact(buf) 41 | } 42 | } 43 | 44 | impl Write for ReadWritePair 45 | where 46 | R: Read, 47 | W: Write, 48 | { 49 | #[inline(always)] 50 | fn write(&mut self, buf: &[u8]) -> io::Result { 51 | self.1.write(buf) 52 | } 53 | #[inline(always)] 54 | fn flush(&mut self) -> io::Result<()> { 55 | self.1.flush() 56 | } 57 | #[inline(always)] 58 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { 59 | self.1.write_all(buf) 60 | } 61 | #[inline(always)] 62 | fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> { 63 | self.1.write_fmt(fmt) 64 | } 65 | } 66 | 67 | /// A collection of traits and implementations for synchronous streams. 68 | pub mod sync { 69 | pub use super::ReadWritePair; 70 | use std::io::{self, Read, Write}; 71 | pub use std::net::Shutdown; 72 | pub use std::net::TcpStream; 73 | use std::ops::Deref; 74 | 75 | pub use super::Stream; 76 | 77 | /// a `Stream` that can also be used as a borrow to a `TcpStream` 78 | /// this is useful when you want to set `TcpStream` options on a 79 | /// `Stream` like `nonblocking`. 80 | pub trait NetworkStream: Read + Write + AsTcpStream {} 81 | 82 | impl NetworkStream for S where S: Read + Write + AsTcpStream {} 83 | 84 | /// some streams can be split up into separate reading and writing components 85 | /// `TcpStream` is an example. This trait marks this ability so one can split 86 | /// up the client into two parts. 87 | /// 88 | /// Notice however that this is not possible to do with SSL. 89 | pub trait Splittable { 90 | /// The reading component of this type 91 | type Reader: Read; 92 | /// The writing component of this type 93 | type Writer: Write; 94 | 95 | /// Split apart this type into a reading and writing component. 96 | fn split(self) -> io::Result<(Self::Reader, Self::Writer)>; 97 | } 98 | 99 | impl Splittable for ReadWritePair 100 | where 101 | R: Read, 102 | W: Write, 103 | { 104 | type Reader = R; 105 | type Writer = W; 106 | 107 | fn split(self) -> io::Result<(R, W)> { 108 | Ok((self.0, self.1)) 109 | } 110 | } 111 | 112 | impl Splittable for TcpStream { 113 | type Reader = TcpStream; 114 | type Writer = TcpStream; 115 | 116 | fn split(self) -> io::Result<(TcpStream, TcpStream)> { 117 | self.try_clone().map(|s| (s, self)) 118 | } 119 | } 120 | 121 | /// The ability access a borrow to an underlying TcpStream, 122 | /// so one can set options on the stream such as `nonblocking`. 123 | pub trait AsTcpStream { 124 | /// Get a borrow of the TcpStream 125 | fn as_tcp(&self) -> &TcpStream; 126 | } 127 | 128 | impl AsTcpStream for TcpStream { 129 | fn as_tcp(&self) -> &TcpStream { 130 | self 131 | } 132 | } 133 | 134 | 135 | impl AsTcpStream for Box 136 | where 137 | T: AsTcpStream + ?Sized, 138 | { 139 | fn as_tcp(&self) -> &TcpStream { 140 | self.deref().as_tcp() 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /websocket-base/src/ws/dataframe.rs: -------------------------------------------------------------------------------- 1 | //! Describes the generic DataFrame, defining a trait 2 | //! that all dataframes should share. This is so one can 3 | //! optimize the memory footprint of a dataframe for their 4 | //! own needs, and be able to use custom dataframes quickly 5 | use crate::result::WebSocketResult; 6 | use crate::ws::util::header as dfh; 7 | use crate::ws::util::mask; 8 | use crate::ws::util::mask::Masker; 9 | use std::io::Write; 10 | 11 | /// A generic DataFrame. Every dataframe should be able to 12 | /// provide these methods. (If the payload is not known in advance then 13 | /// rewrite the write_payload method) 14 | pub trait DataFrame { 15 | /// Is this dataframe the final dataframe of the message? 16 | fn is_last(&self) -> bool; 17 | /// What type of data does this dataframe contain? 18 | fn opcode(&self) -> u8; 19 | /// Reserved bits of this dataframe 20 | fn reserved(&self) -> &[bool; 3]; 21 | 22 | /// How long (in bytes) is this dataframe's payload 23 | fn size(&self) -> usize; 24 | 25 | /// Get's the size of the entire dataframe in bytes, 26 | /// i.e. header and payload. 27 | fn frame_size(&self, masked: bool) -> usize { 28 | // one byte for the opcode & reserved & fin 29 | 1 30 | // depending on the size of the payload, add the right payload len bytes 31 | + match self.size() { 32 | s if s <= 125 => 1, 33 | s if s <= 65535 => 3, 34 | _ => 9, 35 | } 36 | // add the mask size if there is one 37 | + if masked { 38 | 4 39 | } else { 40 | 0 41 | } 42 | // finally add the payload len 43 | + self.size() 44 | } 45 | 46 | /// Write the payload to a writer 47 | fn write_payload(&self, socket: &mut dyn Write) -> WebSocketResult<()>; 48 | 49 | /// Takes the payload out into a vec 50 | fn take_payload(self) -> Vec; 51 | 52 | /// Writes a DataFrame to a Writer. 53 | fn write_to(&self, writer: &mut dyn Write, mask: bool) -> WebSocketResult<()> { 54 | let mut flags = dfh::DataFrameFlags::empty(); 55 | if self.is_last() { 56 | flags.insert(dfh::DataFrameFlags::FIN); 57 | } 58 | { 59 | let reserved = self.reserved(); 60 | if reserved[0] { 61 | flags.insert(dfh::DataFrameFlags::RSV1); 62 | } 63 | if reserved[1] { 64 | flags.insert(dfh::DataFrameFlags::RSV2); 65 | } 66 | if reserved[2] { 67 | flags.insert(dfh::DataFrameFlags::RSV3); 68 | } 69 | } 70 | 71 | let masking_key = if mask { Some(mask::gen_mask()) } else { None }; 72 | 73 | let header = dfh::DataFrameHeader { 74 | flags, 75 | opcode: self.opcode() as u8, 76 | mask: masking_key, 77 | len: self.size() as u64, 78 | }; 79 | 80 | let mut data = Vec::::new(); 81 | dfh::write_header(&mut data, header)?; 82 | 83 | match masking_key { 84 | Some(mask) => { 85 | let mut masker = Masker::new(mask, &mut data); 86 | self.write_payload(&mut masker)? 87 | } 88 | None => self.write_payload(&mut data)?, 89 | }; 90 | writer.write_all(data.as_slice())?; 91 | Ok(()) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /websocket-base/src/ws/message.rs: -------------------------------------------------------------------------------- 1 | //! Provides a trait for WebSocket messages 2 | //! 3 | //! See the `ws` module documentation for more information. 4 | 5 | use crate::result::WebSocketResult; 6 | use crate::ws::dataframe::DataFrame as DataFrameable; 7 | use std::io::Write; 8 | 9 | /// A trait for WebSocket messages 10 | pub trait Message: Sized { 11 | /// Writes this message to the writer 12 | fn serialize(&self, _: &mut dyn Write, masked: bool) -> WebSocketResult<()>; 13 | 14 | /// Returns how many bytes this message will take up 15 | fn message_size(&self, masked: bool) -> usize; 16 | 17 | /// Attempt to form a message from a series of data frames 18 | fn from_dataframes(frames: Vec) -> WebSocketResult; 19 | } 20 | -------------------------------------------------------------------------------- /websocket-base/src/ws/mod.rs: -------------------------------------------------------------------------------- 1 | //! A module containing the traits and structs that lower layer of Rust-WebSocket is based on. 2 | //! 3 | //! This should not need to be used by regular users. 4 | //! 5 | //! Rust-WebSocket is based on three core traits: `Message`, `Sender` and `Receiver`. These 6 | //! traits have default implementations outside this module, however can be implemented 7 | //! by a user to extend the functionality provided. 8 | //! 9 | //! If a user wishes to use a different representation of a data frame, all three traits 10 | //! must be implemented by the user. If a user wishes to use a different representation 11 | //! of a message (but the same data frame), they must implement the `Message` and `Receiver` 12 | //! traits. 13 | //! 14 | //! A WebSocket message type must implement `Message` where `D` is the type of data frame 15 | //! that the message can be converted to/from. 16 | //! 17 | //! When sending a message, the message is converted into an iterator with its `into_iter()` 18 | //! method, which allows the message to output data frames forming a fragmented message 19 | //! where each data frame is sent immediately to be reassembled at the remote endpoint. 20 | //! 21 | //! The type of data frame can be any type, however, if you choose a data frame type other than 22 | //! `DataFrame`, you will also have to implement the `Sender` and `Receiver` traits to 23 | //! send and receive data frames. 24 | //! 25 | //! A `Sender` sends a data frame of type `D`, typically wrapping an underlying Writer, 26 | //! by implementing the `send_dataframe()` method. The `send_message()` method has a default 27 | //! implementation which turns the message into an iterator and then continually calls 28 | //! `send_dataframe()` with the frames from the iterator. 29 | //! 30 | //! To make life easier for a `Sender`, several utility functions are provided which write 31 | //! various pieces of data to a Writer. These are found within the `util` module. 32 | //! 33 | //! A Receiver receives data frames of type D and messages of type Receiver::Message, 34 | //! typically wrapping an underlying Reader, by implementing the `recv_dataframe()` and 35 | //! `recv_message_dataframes()` methods. The `recv_message_dataframes()` method has to 36 | //! form a `Vec` of data frames which comprise one whole, single message. 37 | //! 38 | //! To make life easier for a `Receiver`, several utility functions are provided which read 39 | //! various pieces of data from a Reader. These are found within the `util` module. 40 | pub use self::message::Message; 41 | pub use self::receiver::Receiver; 42 | pub use self::receiver::{DataFrameIterator, MessageIterator}; 43 | pub use self::sender::Sender; 44 | pub mod dataframe; 45 | pub mod message; 46 | pub mod util; 47 | pub mod receiver; 48 | pub mod sender; 49 | -------------------------------------------------------------------------------- /websocket-base/src/ws/receiver.rs: -------------------------------------------------------------------------------- 1 | //! Provides a trait for receiving data frames and messages. 2 | //! 3 | //! Also provides iterators over data frames and messages. 4 | //! See the `ws` module documentation for more information. 5 | 6 | use crate::result::WebSocketResult; 7 | use crate::ws::dataframe::DataFrame; 8 | use crate::ws::Message; 9 | use std::io::Read; 10 | 11 | /// A trait for receiving data frames and messages. 12 | pub trait Receiver: Sized { 13 | /// The type of dataframe that incoming messages will be serialized to. 14 | type F: DataFrame; 15 | 16 | /// The type of message that incoming messages will be serialized to. 17 | type M: Message; 18 | 19 | /// Reads a single data frame from this receiver. 20 | fn recv_dataframe(&mut self, reader: &mut R) -> WebSocketResult 21 | where 22 | R: Read; 23 | 24 | /// Returns the data frames that constitute one message. 25 | fn recv_message_dataframes(&mut self, reader: &mut R) -> WebSocketResult> 26 | where 27 | R: Read; 28 | 29 | /// Returns an iterator over incoming data frames. 30 | fn incoming_dataframes<'a, R>(&'a mut self, reader: &'a mut R) -> DataFrameIterator<'a, Self, R> 31 | where 32 | R: Read, 33 | { 34 | DataFrameIterator { 35 | reader, 36 | inner: self, 37 | } 38 | } 39 | 40 | /// Reads a single message from this receiver. 41 | fn recv_message(&mut self, reader: &mut R) -> WebSocketResult 42 | where 43 | R: Read, 44 | { 45 | let dataframes = self.recv_message_dataframes(reader)?; 46 | Self::M::from_dataframes(dataframes) 47 | } 48 | 49 | /// Returns an iterator over incoming messages. 50 | fn incoming_messages<'a, R>(&'a mut self, reader: &'a mut R) -> MessageIterator<'a, Self, R> 51 | where 52 | R: Read, 53 | { 54 | MessageIterator { 55 | reader, 56 | inner: self, 57 | } 58 | } 59 | } 60 | 61 | /// An iterator over data frames from a Receiver. 62 | pub struct DataFrameIterator<'a, Recv, R> 63 | where 64 | Recv: 'a + Receiver, 65 | R: 'a + Read, 66 | { 67 | reader: &'a mut R, 68 | inner: &'a mut Recv, 69 | } 70 | 71 | impl<'a, Recv, R> Iterator for DataFrameIterator<'a, Recv, R> 72 | where 73 | Recv: 'a + Receiver, 74 | R: Read, 75 | { 76 | type Item = WebSocketResult; 77 | 78 | /// Get the next data frame from the receiver. Always returns `Some`. 79 | fn next(&mut self) -> Option> { 80 | Some(self.inner.recv_dataframe(self.reader)) 81 | } 82 | } 83 | 84 | /// An iterator over messages from a Receiver. 85 | pub struct MessageIterator<'a, Recv, R> 86 | where 87 | Recv: 'a + Receiver, 88 | R: 'a + Read, 89 | { 90 | reader: &'a mut R, 91 | inner: &'a mut Recv, 92 | } 93 | 94 | impl<'a, Recv, R> Iterator for MessageIterator<'a, Recv, R> 95 | where 96 | Recv: 'a + Receiver, 97 | R: Read, 98 | { 99 | type Item = WebSocketResult; 100 | 101 | /// Get the next message from the receiver. Always returns `Some`. 102 | fn next(&mut self) -> Option> { 103 | Some(self.inner.recv_message(self.reader)) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /websocket-base/src/ws/sender.rs: -------------------------------------------------------------------------------- 1 | //! Provides a trait for sending data frames and messages. 2 | //! 3 | //! See the `ws` module documentation for more information. 4 | 5 | use crate::result::WebSocketResult; 6 | use crate::ws::dataframe::DataFrame; 7 | use crate::ws::Message; 8 | use std::io::Write; 9 | 10 | /// A trait for sending data frames and messages. 11 | pub trait Sender { 12 | /// Should the messages sent be masked. 13 | /// See the [RFC](https://tools.ietf.org/html/rfc6455#section-5.3) 14 | /// for more detail. 15 | fn is_masked(&self) -> bool; 16 | 17 | /// Sends a single data frame using this sender. 18 | fn send_dataframe(&mut self, writer: &mut W, dataframe: &D) -> WebSocketResult<()> 19 | where 20 | D: DataFrame, 21 | W: Write, 22 | { 23 | dataframe.write_to(writer, self.is_masked())?; 24 | Ok(()) 25 | } 26 | 27 | /// Sends a single message using this sender. 28 | fn send_message(&mut self, writer: &mut W, message: &M) -> WebSocketResult<()> 29 | where 30 | M: Message, 31 | W: Write, 32 | { 33 | message.serialize(writer, self.is_masked())?; 34 | Ok(()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /websocket-base/src/ws/util/header.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions for reading and writing data frame headers. 2 | 3 | use crate::result::{WebSocketError, WebSocketResult}; 4 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 5 | use std::io::{Read, Write}; 6 | 7 | bitflags! { 8 | /// Flags relevant to a WebSocket data frame. 9 | pub struct DataFrameFlags: u8 { 10 | /// Marks this dataframe as the last dataframe 11 | const FIN = 0x80; 12 | /// First reserved bit 13 | const RSV1 = 0x40; 14 | /// Second reserved bit 15 | const RSV2 = 0x20; 16 | /// Third reserved bit 17 | const RSV3 = 0x10; 18 | } 19 | } 20 | 21 | /// Represents a data frame header. 22 | #[derive(Debug, Clone, Copy, PartialEq)] 23 | pub struct DataFrameHeader { 24 | /// The bit flags for the first byte of the header. 25 | pub flags: DataFrameFlags, 26 | /// The opcode of the header - must be <= 16. 27 | pub opcode: u8, 28 | /// The masking key, if any. 29 | pub mask: Option<[u8; 4]>, 30 | /// The length of the payload. 31 | pub len: u64, 32 | } 33 | 34 | /// Writes a data frame header. 35 | pub fn write_header(writer: &mut dyn Write, header: DataFrameHeader) -> WebSocketResult<()> { 36 | if header.opcode > 0xF { 37 | return Err(WebSocketError::DataFrameError("Invalid data frame opcode")); 38 | } 39 | if header.opcode >= 8 && header.len >= 126 { 40 | return Err(WebSocketError::DataFrameError( 41 | "Control frame length too long", 42 | )); 43 | } 44 | 45 | // Write 'FIN', 'RSV1', 'RSV2', 'RSV3' and 'opcode' 46 | writer.write_u8((header.flags.bits) | header.opcode)?; 47 | 48 | writer.write_u8( 49 | // Write the 'MASK' 50 | if header.mask.is_some() { 0x80 } else { 0x00 } | 51 | // Write the 'Payload len' 52 | if header.len <= 125 { header.len as u8 } 53 | else if header.len <= 65535 { 126 } 54 | else { 127 }, 55 | )?; 56 | 57 | // Write 'Extended payload length' 58 | if header.len >= 126 && header.len <= 65535 { 59 | writer.write_u16::(header.len as u16)?; 60 | } else if header.len > 65535 { 61 | writer.write_u64::(header.len)?; 62 | } 63 | 64 | // Write 'Masking-key' 65 | if let Some(mask) = header.mask { 66 | writer.write_all(&mask)? 67 | } 68 | 69 | Ok(()) 70 | } 71 | 72 | /// Reads a data frame header. 73 | pub fn read_header(reader: &mut R) -> WebSocketResult 74 | where 75 | R: Read, 76 | { 77 | let byte0 = reader.read_u8()?; 78 | let byte1 = reader.read_u8()?; 79 | 80 | let flags = DataFrameFlags::from_bits_truncate(byte0); 81 | let opcode = byte0 & 0x0F; 82 | 83 | let len = match byte1 & 0x7F { 84 | 0..=125 => u64::from(byte1 & 0x7F), 85 | 126 => { 86 | let len = u64::from(reader.read_u16::()?); 87 | if len <= 125 { 88 | return Err(WebSocketError::DataFrameError("Invalid data frame length")); 89 | } 90 | len 91 | } 92 | 127 => { 93 | let len = reader.read_u64::()?; 94 | if len <= 65535 { 95 | return Err(WebSocketError::DataFrameError("Invalid data frame length")); 96 | } 97 | len 98 | } 99 | _ => unreachable!(), 100 | }; 101 | 102 | if opcode >= 8 { 103 | if len >= 126 { 104 | return Err(WebSocketError::DataFrameError( 105 | "Control frame length too long", 106 | )); 107 | } 108 | if !flags.contains(DataFrameFlags::FIN) { 109 | return Err(WebSocketError::ProtocolError( 110 | "Illegal fragmented control frame", 111 | )); 112 | } 113 | } 114 | 115 | let mask = if byte1 & 0x80 == 0x80 { 116 | Some([ 117 | reader.read_u8()?, 118 | reader.read_u8()?, 119 | reader.read_u8()?, 120 | reader.read_u8()?, 121 | ]) 122 | } else { 123 | None 124 | }; 125 | 126 | Ok(DataFrameHeader { 127 | flags, 128 | opcode, 129 | mask, 130 | len, 131 | }) 132 | } 133 | 134 | #[cfg(all(feature = "nightly", test))] 135 | mod tests { 136 | use super::*; 137 | use test; 138 | 139 | #[test] 140 | fn test_read_header_simple() { 141 | let header = [0x81, 0x2B]; 142 | let obtained = read_header(&mut &header[..]).unwrap(); 143 | let expected = DataFrameHeader { 144 | flags: DataFrameFlags::FIN, 145 | opcode: 1, 146 | mask: None, 147 | len: 43, 148 | }; 149 | assert_eq!(obtained, expected); 150 | } 151 | 152 | #[test] 153 | fn test_write_header_simple() { 154 | let header = DataFrameHeader { 155 | flags: DataFrameFlags::FIN, 156 | opcode: 1, 157 | mask: None, 158 | len: 43, 159 | }; 160 | let expected = [0x81, 0x2B]; 161 | let mut obtained = Vec::with_capacity(2); 162 | write_header(&mut obtained, header).unwrap(); 163 | 164 | assert_eq!(&obtained[..], &expected[..]); 165 | } 166 | 167 | #[test] 168 | fn test_read_header_complex() { 169 | let header = [0x42, 0xFE, 0x02, 0x00, 0x02, 0x04, 0x08, 0x10]; 170 | let obtained = read_header(&mut &header[..]).unwrap(); 171 | let expected = DataFrameHeader { 172 | flags: DataFrameFlags::RSV1, 173 | opcode: 2, 174 | mask: Some([2, 4, 8, 16]), 175 | len: 512, 176 | }; 177 | assert_eq!(obtained, expected); 178 | } 179 | 180 | #[test] 181 | fn test_write_header_complex() { 182 | let header = DataFrameHeader { 183 | flags: DataFrameFlags::RSV1, 184 | opcode: 2, 185 | mask: Some([2, 4, 8, 16]), 186 | len: 512, 187 | }; 188 | let expected = [0x42, 0xFE, 0x02, 0x00, 0x02, 0x04, 0x08, 0x10]; 189 | let mut obtained = Vec::with_capacity(8); 190 | write_header(&mut obtained, header).unwrap(); 191 | 192 | assert_eq!(&obtained[..], &expected[..]); 193 | } 194 | 195 | #[bench] 196 | fn bench_read_header(b: &mut test::Bencher) { 197 | let header = vec![0x42u8, 0xFE, 0x02, 0x00, 0x02, 0x04, 0x08, 0x10]; 198 | b.iter(|| { 199 | read_header(&mut &header[..]).unwrap(); 200 | }); 201 | } 202 | 203 | #[bench] 204 | fn bench_write_header(b: &mut test::Bencher) { 205 | let header = DataFrameHeader { 206 | flags: DataFrameFlags::RSV1, 207 | opcode: 2, 208 | mask: Some([2, 4, 8, 16]), 209 | len: 512, 210 | }; 211 | let mut writer = Vec::with_capacity(8); 212 | b.iter(|| { 213 | write_header(&mut writer, header).unwrap(); 214 | }); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /websocket-base/src/ws/util/mask.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions for masking data frame payload data 2 | use std::io::Result as IoResult; 3 | use std::io::Write; 4 | 5 | /// Struct to pipe data into another writer, 6 | /// while masking the data being written 7 | pub struct Masker<'w> { 8 | key: [u8; 4], 9 | pos: usize, 10 | end: &'w mut dyn Write, 11 | } 12 | 13 | impl<'w> Masker<'w> { 14 | /// Create a new Masker with the key and the endpoint 15 | /// to be writer to. 16 | pub fn new(key: [u8; 4], endpoint: &'w mut dyn Write) -> Self { 17 | Masker { 18 | key, 19 | pos: 0, 20 | end: endpoint, 21 | } 22 | } 23 | } 24 | 25 | impl<'w> Write for Masker<'w> { 26 | fn write(&mut self, data: &[u8]) -> IoResult { 27 | let mut buf = Vec::with_capacity(data.len()); 28 | for &byte in data.iter() { 29 | buf.push(byte ^ self.key[self.pos]); 30 | self.pos = (self.pos + 1) % self.key.len(); 31 | } 32 | self.end.write(&buf) 33 | } 34 | 35 | fn flush(&mut self) -> IoResult<()> { 36 | self.end.flush() 37 | } 38 | } 39 | 40 | /// Generates a random masking key 41 | pub fn gen_mask() -> [u8; 4] { 42 | rand::random() 43 | } 44 | 45 | /// Masks data to send to a server and writes 46 | pub fn mask_data(mask: [u8; 4], data: &[u8]) -> Vec { 47 | let mut out = Vec::with_capacity(data.len()); 48 | let zip_iter = data.iter().zip(mask.iter().cycle()); 49 | for (&buf_item, &key_item) in zip_iter { 50 | out.push(buf_item ^ key_item); 51 | } 52 | out 53 | } 54 | 55 | #[cfg(all(feature = "nightly", test))] 56 | mod tests { 57 | use super::*; 58 | use test; 59 | 60 | #[test] 61 | fn test_mask_data() { 62 | let key = [1u8, 2u8, 3u8, 4u8]; 63 | let original = vec![10u8, 11u8, 12u8, 13u8, 14u8, 15u8, 16u8, 17u8]; 64 | let expected = vec![11u8, 9u8, 15u8, 9u8, 15u8, 13u8, 19u8, 21u8]; 65 | let obtained = mask_data(key, &original[..]); 66 | let reversed = mask_data(key, &obtained[..]); 67 | 68 | assert_eq!(original, reversed); 69 | assert_eq!(obtained, expected); 70 | } 71 | 72 | #[bench] 73 | fn bench_mask_data(b: &mut test::Bencher) { 74 | let buffer = b"The quick brown fox jumps over the lazy dog"; 75 | let key = gen_mask(); 76 | b.iter(|| { 77 | let mut output = mask_data(key, buffer); 78 | test::black_box(&mut output); 79 | }); 80 | } 81 | 82 | #[bench] 83 | fn bench_gen_mask(b: &mut test::Bencher) { 84 | b.iter(|| { 85 | let mut key = gen_mask(); 86 | test::black_box(&mut key); 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /websocket-base/src/ws/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions for various portions of Rust-WebSocket. 2 | 3 | pub mod header; 4 | pub mod mask; 5 | 6 | use std::str::from_utf8; 7 | use std::str::Utf8Error; 8 | 9 | 10 | /// Transforms a u8 slice into an owned String 11 | pub fn bytes_to_string(data: &[u8]) -> Result { 12 | let utf8 = from_utf8(data)?; 13 | Ok(utf8.to_string()) 14 | } 15 | --------------------------------------------------------------------------------