1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-23 23:51:06 +01:00

update ws docs

This commit is contained in:
Nikolay Kim 2017-10-07 22:41:02 -07:00
parent f2d20514fa
commit a021a06743
5 changed files with 108 additions and 37 deletions

View File

@ -51,7 +51,7 @@ impl Route for MyRoute {
fn request(req: HttpRequest, payload: Option<Payload>, fn request(req: HttpRequest, payload: Option<Payload>,
ctx: &mut HttpContext<Self>) -> HttpMessage<Self> ctx: &mut HttpContext<Self>) -> HttpMessage<Self>
{ {
Self::http_reply(req, httpcodes::HTTPOk) HttpMessage::reply_with(req, httpcodes::HTTPOk)
} }
} }

View File

@ -67,7 +67,7 @@ impl Route for MyWS {
ctx: &mut HttpContext<Self>) -> HttpMessage<Self> ctx: &mut HttpContext<Self>) -> HttpMessage<Self>
{ {
if let Some(payload) = payload { if let Some(payload) = payload {
match ws::do_handshake(req) { match ws::handshake(req) {
Ok(resp) => { Ok(resp) => {
ctx.start(resp); ctx.start(resp);
ctx.add_stream(ws::WsStream::new(payload)); ctx.add_stream(ws::WsStream::new(payload));

137
src/ws.rs
View File

@ -1,14 +1,77 @@
//! `WebSocket` context implementation //! `WebSocket` support for Actix
//!
#![allow(dead_code, unused_variables, unused_imports)] //! To setup a `WebSocket`, first do web socket handshake then on success convert `Payload`
//! into a `WsStream` stream and then use `WsWriter` to communicate with the peer.
use std::io; //!
//! ## Example
//!
//! ```rust
//! extern crate actix;
//! extern crate actix_http;
//! use actix::prelude::*;
//! use actix_http::*;
//!
//! // WebSocket Route
//! struct WsRoute;
//!
//! impl Actor for WsRoute {
//! type Context = HttpContext<Self>;
//! }
//!
//! impl Route for WsRoute {
//! type State = ();
//!
//! fn request(req: HttpRequest, payload: Option<Payload>,
//! ctx: &mut HttpContext<Self>) -> HttpMessage<Self>
//! {
//! if let Some(payload) = payload {
//! // WebSocket handshake
//! match ws::handshake(req) {
//! Ok(resp) => {
//! // Send handshake response to peer
//! ctx.start(resp);
//! // Map Payload into WsStream
//! ctx.add_stream(ws::WsStream::new(payload));
//! // Start ws messages processing
//! HttpMessage::stream(WsRoute)
//! },
//! Err(err) =>
//! HttpMessage::reply(err)
//! }
//! } else {
//! HttpMessage::reply_with(req, httpcodes::HTTPBadRequest)
//! }
//! }
//! }
//!
//! // Define Handler for ws::Message message
//! impl StreamHandler<ws::Message> for WsRoute {}
//!
//! impl Handler<ws::Message> for WsRoute {
//! fn handle(&mut self, msg: ws::Message, ctx: &mut HttpContext<Self>)
//! -> Response<Self, ws::Message>
//! {
//! match msg {
//! ws::Message::Ping(msg) => ws::WsWriter::pong(ctx, msg),
//! ws::Message::Text(text) => ws::WsWriter::text(ctx, text),
//! ws::Message::Binary(bin) => ws::WsWriter::binary(ctx, bin),
//! _ => (),
//! }
//! Self::empty()
//! }
//! }
//!
//! impl ResponseType<ws::Message> for WsRoute {
//! type Item = ();
//! type Error = ();
//! }
//!
//! fn main() {}
//! ```
use std::vec::Vec; use std::vec::Vec;
use std::borrow::Cow;
use http::{Method, StatusCode}; use http::{Method, StatusCode};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::{Async, Future, Poll, Stream}; use futures::{Async, Poll, Stream};
use hyper::header; use hyper::header;
use actix::Actor; use actix::Actor;
@ -19,12 +82,25 @@ use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed};
use httpmessage::{Body, ConnectionType, HttpRequest, HttpResponse, IntoHttpResponse}; use httpmessage::{Body, ConnectionType, HttpRequest, HttpResponse, IntoHttpResponse};
use wsframe; use wsframe;
pub use wsproto::*; use wsproto::*;
header! { (WebSocketAccept, "SEC-WEBSOCKET-ACCEPT") => [String] } #[doc(hidden)]
header! { (WebSocketKey, "SEC-WEBSOCKET-KEY") => [String] } header! {
header! { (WebSocketVersion, "SEC-WEBSOCKET-VERSION") => [String] } /// SEC-WEBSOCKET-ACCEPT header
header! { (WebSocketProtocol, "SEC-WEBSOCKET-PROTOCOL") => [String] } (WebSocketAccept, "SEC-WEBSOCKET-ACCEPT") => [String]
}
header! {
/// SEC-WEBSOCKET-KEY header
(WebSocketKey, "SEC-WEBSOCKET-KEY") => [String]
}
header! {
/// SEC-WEBSOCKET-VERSION header
(WebSocketVersion, "SEC-WEBSOCKET-VERSION") => [String]
}
header! {
/// SEC-WEBSOCKET-PROTOCOL header
(WebSocketProtocol, "SEC-WEBSOCKET-PROTOCOL") => [String]
}
#[derive(Debug)] #[derive(Debug)]
@ -38,24 +114,15 @@ pub enum Message {
Error Error
} }
#[derive(Debug)] /// Prepare `WebSocket` handshake response.
pub enum SendMessage {
Text(String),
Binary(Vec<u8>),
Close(CloseCode),
Ping,
Pong,
}
/// Prepare `WebSocket` handshake.
/// ///
/// It return HTTP response code, response headers, websocket parser, /// This function returns handshake HttpResponse, ready to send to peer.
/// websocket writer. It does not perform any IO. /// It does not perform any IO.
/// ///
/// `protocols` is a sequence of known protocols. On successful handshake, // /// `protocols` is a sequence of known protocols. On successful handshake,
/// the returned response headers contain the first protocol in this list // /// the returned response headers contain the first protocol in this list
/// which the server also knows. // /// which the server also knows.
pub fn do_handshake(req: HttpRequest) -> Result<HttpResponse, HttpResponse> { pub fn handshake(req: HttpRequest) -> Result<HttpResponse, HttpResponse> {
// WebSocket accepts only GET // WebSocket accepts only GET
if *req.method() != Method::GET { if *req.method() != Method::GET {
return Err(HTTPMethodNotAllowed.response(req)) return Err(HTTPMethodNotAllowed.response(req))
@ -116,7 +183,7 @@ pub fn do_handshake(req: HttpRequest) -> Result<HttpResponse, HttpResponse> {
} }
/// Struct represent stream of `ws::Message` items /// Maps `Payload` stream into stream of `ws::Message` items
pub struct WsStream { pub struct WsStream {
rx: Payload, rx: Payload,
buf: BytesMut, buf: BytesMut,
@ -154,7 +221,7 @@ impl Stream for WsStream {
match wsframe::Frame::parse(&mut self.buf) { match wsframe::Frame::parse(&mut self.buf) {
Ok(Some(frame)) => { Ok(Some(frame)) => {
trace!("Frame {}", frame); trace!("Frame {}", frame);
let (finished, opcode, payload) = frame.unpack(); let (_finished, opcode, payload) = frame.unpack();
match opcode { match opcode {
OpCode::Continue => continue, OpCode::Continue => continue,
@ -174,7 +241,7 @@ impl Stream for WsStream {
match String::from_utf8(payload) { match String::from_utf8(payload) {
Ok(s) => Ok(s) =>
return Ok(Async::Ready(Some(Message::Text(s)))), return Ok(Async::Ready(Some(Message::Text(s)))),
Err(err) => Err(_) =>
return Ok(Async::Ready(Some(Message::Error))), return Ok(Async::Ready(Some(Message::Error))),
} }
} }
@ -185,7 +252,7 @@ impl Stream for WsStream {
} else { } else {
return Ok(Async::NotReady) return Ok(Async::NotReady)
}, },
Err(err) => Err(_) =>
return Err(()), return Err(()),
} }
} }
@ -198,6 +265,7 @@ pub struct WsWriter;
impl WsWriter { impl WsWriter {
/// Send text frame
pub fn text<A>(ctx: &mut HttpContext<A>, text: String) pub fn text<A>(ctx: &mut HttpContext<A>, text: String)
where A: Actor<Context=HttpContext<A>> + Route where A: Actor<Context=HttpContext<A>> + Route
{ {
@ -210,6 +278,7 @@ impl WsWriter {
); );
} }
/// Send binary frame
pub fn binary<A>(ctx: &mut HttpContext<A>, data: Vec<u8>) pub fn binary<A>(ctx: &mut HttpContext<A>, data: Vec<u8>)
where A: Actor<Context=HttpContext<A>> + Route where A: Actor<Context=HttpContext<A>> + Route
{ {
@ -222,6 +291,7 @@ impl WsWriter {
); );
} }
/// Send ping frame
pub fn ping<A>(ctx: &mut HttpContext<A>, message: String) pub fn ping<A>(ctx: &mut HttpContext<A>, message: String)
where A: Actor<Context=HttpContext<A>> + Route where A: Actor<Context=HttpContext<A>> + Route
{ {
@ -234,6 +304,7 @@ impl WsWriter {
) )
} }
/// Send pong frame
pub fn pong<A>(ctx: &mut HttpContext<A>, message: String) pub fn pong<A>(ctx: &mut HttpContext<A>, message: String)
where A: Actor<Context=HttpContext<A>> + Route where A: Actor<Context=HttpContext<A>> + Route
{ {

View File

@ -25,7 +25,7 @@ fn generate_mask() -> [u8; 4] {
/// A struct representing a `WebSocket` frame. /// A struct representing a `WebSocket` frame.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Frame { pub(crate) struct Frame {
finished: bool, finished: bool,
rsv1: bool, rsv1: bool,
rsv2: bool, rsv2: bool,

View File

@ -10,7 +10,7 @@ use sha1;
use self::OpCode::*; use self::OpCode::*;
/// Operation codes as part of rfc6455. /// Operation codes as part of rfc6455.
#[derive(Debug, Eq, PartialEq, Clone, Copy)] #[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum OpCode { pub(crate) enum OpCode {
/// Indicates a continuation frame of a fragmented message. /// Indicates a continuation frame of a fragmented message.
Continue, Continue,
/// Indicates a text data frame. /// Indicates a text data frame.