mirror of
https://github.com/actix/actix-web.git
synced 2024-12-17 21:56:38 +00:00
update ws docs
This commit is contained in:
parent
f2d20514fa
commit
a021a06743
5 changed files with 108 additions and 37 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
137
src/ws.rs
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue