├── .gitignore ├── Cargo.toml ├── examples ├── client.rs └── server.rs ├── README.md ├── .travis.yml ├── LICENSE └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tokio-serde-json" 3 | version = "0.3.0" 4 | edition = "2018" 5 | authors = ["Carl Lerche "] 6 | license = "MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/carllerche/tokio-serde-json" 9 | homepage = "https://github.com/carllerche/tokio-serde-json" 10 | documentation = "https://docs.rs/tokio-serde-json/0.1.0" 11 | description = """ 12 | Utilities needed to easily implement a Tokio JSON transport using Serde for 13 | JSON serialization and deserialization of frame values. 14 | """ 15 | 16 | [dependencies] 17 | bytes = "0.4" 18 | futures-preview = "0.3.0-alpha" 19 | pin-project = "0.4" 20 | serde = "1.0" 21 | serde_json = "1.0" 22 | tokio-serde = "0.4.0" 23 | 24 | [dev-dependencies] 25 | tokio = "0.2.0-alpha" 26 | -------------------------------------------------------------------------------- /examples/client.rs: -------------------------------------------------------------------------------- 1 | use futures::prelude::*; 2 | use serde_json::json; 3 | use tokio::{ 4 | codec::{FramedWrite, LengthDelimitedCodec}, 5 | net::TcpStream, 6 | }; 7 | use tokio_serde_json::WriteJson; 8 | 9 | #[tokio::main] 10 | pub async fn main() { 11 | // Bind a server socket 12 | let socket = TcpStream::connect("127.0.0.1:17653").await.unwrap(); 13 | 14 | // Delimit frames using a length header 15 | let length_delimited = FramedWrite::new(socket, LengthDelimitedCodec::new()); 16 | 17 | // Serialize frames with JSON 18 | let mut serialized = WriteJson::new(length_delimited); 19 | 20 | // Send the value 21 | serialized 22 | .send(json!({ 23 | "name": "John Doe", 24 | "age": 43, 25 | "phones": [ 26 | "+44 1234567", 27 | "+44 2345678" 28 | ] 29 | })) 30 | .await 31 | .unwrap() 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tokio / Serde bindings for JSON 2 | 3 | Utilities needed to easily implement a Tokio JSON transport using [serde] for 4 | JSON serialization and deserialization of frame values. 5 | 6 | [Documentation](https://carllerche.github.io/tokio-serde-json/tokio_serde_json/index.html) 7 | 8 | ## Usage 9 | 10 | To use `tokio-serde-json`, first add this to your `Cargo.toml`: 11 | 12 | ```toml 13 | [dependencies] 14 | tokio-serde-json = "0.2" 15 | ``` 16 | 17 | Next, add this to your crate: 18 | 19 | ```rust 20 | extern crate tokio_serde_json; 21 | 22 | use tokio_serde_json::{ReadJson, WriteJson}; 23 | ``` 24 | 25 | [serde]: https://serde.rs 26 | 27 | ## License 28 | 29 | This project is licensed under the [MIT license](LICENSE). 30 | 31 | ### Contribution 32 | 33 | Unless you explicitly state otherwise, any contribution intentionally submitted 34 | for inclusion in `tower-web` by you, shall be licensed as MIT, without any 35 | additional terms or conditions. 36 | -------------------------------------------------------------------------------- /examples/server.rs: -------------------------------------------------------------------------------- 1 | use futures::prelude::*; 2 | use serde_json::Value; 3 | use tokio::{ 4 | codec::{FramedRead, LengthDelimitedCodec}, 5 | net::TcpListener, 6 | }; 7 | use tokio_serde_json::ReadJson; 8 | 9 | #[tokio::main] 10 | pub async fn main() { 11 | // Bind a server socket 12 | let listener = TcpListener::bind("127.0.0.1:17653").await.unwrap(); 13 | 14 | println!("listening on {:?}", listener.local_addr()); 15 | 16 | let mut s = listener.incoming(); 17 | 18 | while let Some(socket) = s.try_next().await.unwrap() { 19 | // Delimit frames using a length header 20 | let length_delimited = FramedRead::new(socket, LengthDelimitedCodec::new()); 21 | 22 | // Deserialize frames 23 | let mut deserialized = ReadJson::<_, Value>::new(length_delimited); 24 | 25 | // Spawn a task that prints all received messages to STDOUT 26 | tokio::spawn(async move { 27 | while let Some(msg) = deserialized.try_next().await.unwrap() { 28 | println!("GOT: {:?}", msg); 29 | } 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | rust: 5 | - stable 6 | 7 | before_script: 8 | - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH 9 | 10 | script: 11 | - cargo build 12 | - cargo test 13 | - cargo doc --no-deps 14 | 15 | after_success: 16 | - travis-cargo doc-upload 17 | 18 | env: 19 | global: 20 | - secure: "hXcmhrZo4AGYeA34fgz9wtmVftFsSAq7qvuZ+41HBGTOogbsezCIuePjirDXxON2AhGqw48HXn54CAobWLHVlkW1B5Qj7EhpJif48+Qx0pRShPz3OJ7iPqlq0A6k1PaKht5jHbF2CrQ7O7pL5dS2RcM5EIGkkeNBhgZmmbeSGBE6LuQlbZJHZE8kjxWYYuVyM0EUjPt7882zSNHqdoVjcDf3ez+q3VdEWGHVCFjyRlvOf06Ds5syBUTX2fvSpDtd6UmDjnNH4yvJLYcHmtRFGuwak3xSlkCIA13AIuL3MMqSFc89hDjSGSgL34pb9YqIE289gb9ulFLCGF2ZBYLnzH4CGJb2UMkUmJ0RhQNFHtbPmxx1eiTLojwk8WxSGAGRJGgNNsVpDAE7tXss4EXd8tLrY3MNiWlZAQHjQjiTY676Tpe3clz7YtbzTQZEsi49mq8XPEm/pGglG/e/thxGl6/IjsXEgw82KdZTLzzTMsOhMqLriXj5ZDDM1xTSrJxGqBr6CHyPDOIAkZr8YOLw0TpLU4rst8mXAQRY5EC6sfyRBGtQRoNQNZba6QtFksTh36qxktyucIwFx0fhPz7GL4h82CSERf/Wt0Rf+MP7jyhtxP7zOgpAnBNCtIny/VwUhV+440/ByHvSp+wNSlFXMAk9kAT7y3jpKojKl3NSM6s=" 21 | 22 | notifications: 23 | email: 24 | on_success: never 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Carl Lerche 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 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/tokio-serde-json/0.3.0")] 2 | 3 | //! `Stream` and `Sink` adaptors for serializing and deserializing values using 4 | //! JSON. 5 | //! 6 | //! This crate provides adaptors for going from a stream or sink of buffers 7 | //! ([`Bytes`]) to a stream or sink of values by performing JSON encoding or 8 | //! decoding. It is expected that each yielded buffer contains a single 9 | //! serialized JSON value. The specific strategy by which this is done is left 10 | //! up to the user. One option is to use using [`length_delimited`] from 11 | //! [tokio-io]. 12 | //! 13 | //! # Examples 14 | //! 15 | //! ```no_run 16 | //! use futures::prelude::*; 17 | //! 18 | //! use serde_json::json; 19 | //! 20 | //! use tokio::{codec::{FramedWrite, LengthDelimitedCodec}, net::TcpStream}; 21 | //! 22 | //! use tokio_serde_json::WriteJson; 23 | //! 24 | //! #[tokio::main] 25 | //! async fn main() { 26 | //! // Bind a server socket 27 | //! let socket = TcpStream::connect("127.0.0.1:17653") 28 | //! .await 29 | //! .unwrap(); 30 | //! 31 | //! // Delimit frames using a length header 32 | //! let length_delimited = FramedWrite::new(socket, LengthDelimitedCodec::new()); 33 | //! 34 | //! // Serialize frames with JSON 35 | //! let mut serialized = WriteJson::new(length_delimited); 36 | //! 37 | //! // Send the value 38 | //! serialized.send(json!({ 39 | //! "name": "John Doe", 40 | //! "age": 43, 41 | //! "phones": [ 42 | //! "+44 1234567", 43 | //! "+44 2345678" 44 | //! ] 45 | //! })).await.unwrap() 46 | //! } 47 | //! ``` 48 | //! 49 | //! For a full working server and client example, see the [examples] directory. 50 | //! 51 | //! [`Bytes`]: https://docs.rs/bytes/0.4/bytes/struct.Bytes.html 52 | //! [`length_delimited`]: https://docs.rs/tokio-io/0.1/tokio_io/codec/length_delimited/index.html 53 | //! [tokio-io]: https://github.com/tokio-rs/tokio-io 54 | //! [examples]: https://github.com/carllerche/tokio-serde-json/tree/master/examples 55 | 56 | use bytes::{Buf, Bytes, BytesMut, IntoBuf}; 57 | use futures::prelude::*; 58 | use pin_project::pin_project; 59 | use serde::{Deserialize, Serialize}; 60 | use tokio_serde::{Deserializer, FramedRead, FramedWrite, Serializer}; 61 | 62 | use std::{ 63 | marker::PhantomData, 64 | pin::Pin, 65 | task::{Context, Poll}, 66 | }; 67 | 68 | /// Adapts a stream of JSON encoded buffers to a stream of values by 69 | /// deserializing them. 70 | /// 71 | /// `ReadJson` implements `Sink` by polling the inner buffer stream and 72 | /// deserializing the buffer as JSON. It expects that each yielded buffer 73 | /// represents a single JSON value and does not contain any extra trailing 74 | /// bytes. 75 | /// 76 | /// If a `ReadJson` is used concurrently from two or more threads, it is 77 | /// guaranteed that only one object will be read at a time. In other words, once 78 | /// an object begins being read, that object will continue being read until it 79 | /// finishes or errors, and another object will only be read once the first 80 | /// object completes. 81 | #[pin_project] 82 | pub struct ReadJson { 83 | #[pin] 84 | inner: FramedRead>, 85 | } 86 | 87 | /// Adapts a buffer sink to a value sink by serializing the values as JSON. 88 | /// 89 | /// `WriteJson` implements `Sink` by serializing the submitted values to a 90 | /// buffer. The buffer is then sent to the inner stream, which is responsible 91 | /// for handling framing on the wire. 92 | /// 93 | /// If a `WriteJson` is used concurrently from two or more threads, it is 94 | /// guaranteed that the bytes of different objects written will not be 95 | /// interleaved. In other words, if two calls to `WriteJson::send` overlap, then 96 | /// the bytes of one object will be written entirely before the bytes of the 97 | /// other. 98 | #[pin_project] 99 | pub struct WriteJson { 100 | #[pin] 101 | inner: FramedWrite>, 102 | } 103 | 104 | struct Json { 105 | ghost: PhantomData, 106 | } 107 | 108 | impl ReadJson { 109 | /// Creates a new `ReadJson` with the given buffer stream. 110 | pub fn new(inner: T) -> Self { 111 | let json = Json { ghost: PhantomData }; 112 | Self { 113 | inner: FramedRead::new(inner, json), 114 | } 115 | } 116 | 117 | /// Returns a reference to the underlying stream wrapped by `ReadJson`. 118 | /// 119 | /// Note that care should be taken to not tamper with the underlying stream 120 | /// of data coming in as it may corrupt the stream of frames otherwise 121 | /// being worked with. 122 | pub fn get_ref(&self) -> &T { 123 | self.inner.get_ref() 124 | } 125 | 126 | /// Returns a mutable reference to the underlying stream wrapped by 127 | /// `ReadJson`. 128 | /// 129 | /// Note that care should be taken to not tamper with the underlying stream 130 | /// of data coming in as it may corrupt the stream of frames otherwise 131 | /// being worked with. 132 | pub fn get_mut(&mut self) -> &mut T { 133 | self.inner.get_mut() 134 | } 135 | 136 | /// Consumes the `ReadJson`, returning its underlying stream. 137 | /// 138 | /// Note that care should be taken to not tamper with the underlying stream 139 | /// of data coming in as it may corrupt the stream of frames otherwise being 140 | /// worked with. 141 | pub fn into_inner(self) -> T { 142 | self.inner.into_inner() 143 | } 144 | } 145 | 146 | impl Stream for ReadJson 147 | where 148 | T: TryStream, 149 | T::Error: From, 150 | for<'a> U: Deserialize<'a>, 151 | { 152 | type Item = Result; 153 | 154 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 155 | self.project().inner.poll_next(cx) 156 | } 157 | } 158 | 159 | impl Sink for ReadJson 160 | where 161 | T: Sink, 162 | { 163 | type Error = T::Error; 164 | 165 | fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 166 | self.project().inner.poll_ready(cx) 167 | } 168 | 169 | fn start_send(self: Pin<&mut Self>, item: SinkItem) -> Result<(), Self::Error> { 170 | self.project().inner.start_send(item) 171 | } 172 | 173 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 174 | self.project().inner.poll_flush(cx) 175 | } 176 | 177 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 178 | self.project().inner.poll_close(cx) 179 | } 180 | } 181 | 182 | impl WriteJson { 183 | /// Creates a new `ReadJson` with the given buffer stream. 184 | pub fn new(inner: T) -> Self { 185 | let json = Json { ghost: PhantomData }; 186 | Self { 187 | inner: FramedWrite::new(inner, json), 188 | } 189 | } 190 | 191 | /// Returns a reference to the underlying sink wrapped by `WriteJson`. 192 | /// 193 | /// Note that care should be taken to not tamper with the underlying sink as 194 | /// it may corrupt the sequence of frames otherwise being worked with. 195 | pub fn get_ref(&self) -> &T { 196 | self.inner.get_ref() 197 | } 198 | 199 | /// Returns a mutable reference to the underlying sink wrapped by 200 | /// `WriteJson`. 201 | /// 202 | /// Note that care should be taken to not tamper with the underlying sink as 203 | /// it may corrupt the sequence of frames otherwise being worked with. 204 | pub fn get_mut(&mut self) -> &mut T { 205 | self.inner.get_mut() 206 | } 207 | 208 | /// Consumes the `WriteJson`, returning its underlying sink. 209 | /// 210 | /// Note that care should be taken to not tamper with the underlying sink as 211 | /// it may corrupt the sequence of frames otherwise being worked with. 212 | pub fn into_inner(self) -> T { 213 | self.inner.into_inner() 214 | } 215 | } 216 | 217 | impl Stream for WriteJson 218 | where 219 | T: Stream, 220 | { 221 | type Item = T::Item; 222 | 223 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 224 | self.project().inner.poll_next(cx) 225 | } 226 | } 227 | 228 | impl Sink for WriteJson 229 | where 230 | T: Sink, 231 | T::Error: From, 232 | U: Serialize, 233 | { 234 | type Error = T::Error; 235 | 236 | fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 237 | self.project().inner.poll_ready(cx) 238 | } 239 | 240 | fn start_send(self: Pin<&mut Self>, item: U) -> Result<(), Self::Error> { 241 | self.project().inner.start_send(item) 242 | } 243 | 244 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 245 | self.project().inner.poll_flush(cx) 246 | } 247 | 248 | fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 249 | self.project().inner.poll_close(cx) 250 | } 251 | } 252 | 253 | impl Deserializer for Json 254 | where 255 | for<'a> T: Deserialize<'a>, 256 | { 257 | type Error = serde_json::Error; 258 | 259 | fn deserialize(self: Pin<&mut Self>, src: &BytesMut) -> Result { 260 | serde_json::from_reader(src.into_buf().reader()) 261 | } 262 | } 263 | 264 | impl Serializer for Json { 265 | type Error = serde_json::Error; 266 | 267 | fn serialize(self: Pin<&mut Self>, item: &T) -> Result { 268 | serde_json::to_vec(item).map(Into::into) 269 | } 270 | } 271 | --------------------------------------------------------------------------------