In short, MessageLoop<Connection> was redesigned as Protocol<Connection>,
and the protocol methods no longer consume self.
API changes are briefly documented in the following page:
https://docs.rs/tokio-hglib/0.3.0/tokio_hglib/struct.Protocol.html
| Alphare |
| hg-reviewers |
In short, MessageLoop<Connection> was redesigned as Protocol<Connection>,
and the protocol methods no longer consume self.
API changes are briefly documented in the following page:
https://docs.rs/tokio-hglib/0.3.0/tokio_hglib/struct.Protocol.html
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | ||||
| // | // | ||||
| // This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
| // GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
| //! Functions to send client-side fds over the command server channel. | //! Functions to send client-side fds over the command server channel. | ||||
| use futures::{try_ready, Async, Future, Poll}; | |||||
| use std::io; | use std::io; | ||||
| use std::os::unix::io::AsRawFd; | use std::os::unix::io::AsRawFd; | ||||
| use tokio_hglib::codec::ChannelMessage; | use tokio_hglib::codec::ChannelMessage; | ||||
| use tokio_hglib::protocol::MessageLoop; | use tokio_hglib::{Connection, Protocol}; | ||||
| use tokio_hglib::{Client, Connection}; | |||||
| use crate::message; | use crate::message; | ||||
| use crate::procutil; | use crate::procutil; | ||||
| /// Future to send client-side fds over the command server channel. | /// Sends client-side fds over the command server channel. | ||||
| /// | /// | ||||
| /// This works as follows: | /// This works as follows: | ||||
| /// 1. Client sends "attachio" request. | /// 1. Client sends "attachio" request. | ||||
| /// 2. Server sends back 1-byte input request. | /// 2. Server sends back 1-byte input request. | ||||
| /// 3. Client sends fds with 1-byte dummy payload in response. | /// 3. Client sends fds with 1-byte dummy payload in response. | ||||
| /// 4. Server returns the number of the fds received. | /// 4. Server returns the number of the fds received. | ||||
| /// | /// | ||||
| /// If the stderr is omitted, it will be redirected to the stdout. This | /// If the stderr is omitted, it will be redirected to the stdout. This | ||||
| /// allows us to attach the pager stdin to both stdout and stderr, and | /// allows us to attach the pager stdin to both stdout and stderr, and | ||||
| /// dispose of the client-side handle once attached. | /// dispose of the client-side handle once attached. | ||||
| #[must_use = "futures do nothing unless polled"] | pub async fn attach_io( | ||||
| pub struct AttachIo<C, I, O, E> | proto: &mut Protocol<impl Connection + AsRawFd>, | ||||
| where | stdin: impl AsRawFd, | ||||
| C: Connection, | stdout: impl AsRawFd, | ||||
| stderr: Option<impl AsRawFd>, | |||||
| ) -> io::Result<()> { | |||||
| // TODO: unindent | |||||
| { | { | ||||
| msg_loop: MessageLoop<C>, | proto.send_command("attachio").await?; | ||||
| stdin: I, | |||||
| stdout: O, | |||||
| stderr: Option<E>, | |||||
| } | |||||
| impl<C, I, O, E> AttachIo<C, I, O, E> | |||||
| where | |||||
| C: Connection + AsRawFd, | |||||
| I: AsRawFd, | |||||
| O: AsRawFd, | |||||
| E: AsRawFd, | |||||
| { | |||||
| pub fn with_client( | |||||
| client: Client<C>, | |||||
| stdin: I, | |||||
| stdout: O, | |||||
| stderr: Option<E>, | |||||
| ) -> AttachIo<C, I, O, E> { | |||||
| let msg_loop = MessageLoop::start(client, b"attachio"); | |||||
| AttachIo { | |||||
| msg_loop, | |||||
| stdin, | |||||
| stdout, | |||||
| stderr, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<C, I, O, E> Future for AttachIo<C, I, O, E> | |||||
| where | |||||
| C: Connection + AsRawFd, | |||||
| I: AsRawFd, | |||||
| O: AsRawFd, | |||||
| E: AsRawFd, | |||||
| { | |||||
| type Item = Client<C>; | |||||
| type Error = io::Error; | |||||
| fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | |||||
| loop { | loop { | ||||
| let (client, msg) = try_ready!(self.msg_loop.poll()); | match proto.fetch_response().await? { | ||||
| match msg { | |||||
| ChannelMessage::Data(b'r', data) => { | ChannelMessage::Data(b'r', data) => { | ||||
| let fd_cnt = message::parse_result_code(data)?; | let fd_cnt = message::parse_result_code(data)?; | ||||
| if fd_cnt == 3 { | if fd_cnt == 3 { | ||||
| return Ok(Async::Ready(client)); | return Ok(()); | ||||
| } else { | } else { | ||||
| return Err(io::Error::new( | return Err(io::Error::new( | ||||
| io::ErrorKind::InvalidData, | io::ErrorKind::InvalidData, | ||||
| "unexpected attachio result", | "unexpected attachio result", | ||||
| )); | )); | ||||
| } | } | ||||
| } | } | ||||
| ChannelMessage::Data(..) => { | ChannelMessage::Data(..) => { | ||||
| // just ignore data sent to uninteresting (optional) channel | // just ignore data sent to uninteresting (optional) channel | ||||
| self.msg_loop = MessageLoop::resume(client); | |||||
| } | } | ||||
| ChannelMessage::InputRequest(1) => { | ChannelMessage::InputRequest(1) => { | ||||
| // this may fail with EWOULDBLOCK in theory, but the | // this may fail with EWOULDBLOCK in theory, but the | ||||
| // payload is quite small, and the send buffer should | // payload is quite small, and the send buffer should | ||||
| // be empty so the operation will complete immediately | // be empty so the operation will complete immediately | ||||
| let sock_fd = client.as_raw_fd(); | let sock_fd = proto.as_raw_fd(); | ||||
| let ifd = self.stdin.as_raw_fd(); | let ifd = stdin.as_raw_fd(); | ||||
| let ofd = self.stdout.as_raw_fd(); | let ofd = stdout.as_raw_fd(); | ||||
| let efd = self.stderr.as_ref().map_or(ofd, |f| f.as_raw_fd()); | let efd = stderr.as_ref().map_or(ofd, |f| f.as_raw_fd()); | ||||
| procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; | procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; | ||||
| self.msg_loop = MessageLoop::resume(client); | |||||
| } | } | ||||
| ChannelMessage::InputRequest(..) | ChannelMessage::InputRequest(..) | ||||
| | ChannelMessage::LineRequest(..) | | ChannelMessage::LineRequest(..) | ||||
| | ChannelMessage::SystemRequest(..) => { | | ChannelMessage::SystemRequest(..) => { | ||||
| return Err(io::Error::new( | return Err(io::Error::new( | ||||
| io::ErrorKind::InvalidData, | io::ErrorKind::InvalidData, | ||||
| "unsupported request while attaching io", | "unsupported request while attaching io", | ||||
| )); | )); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | ||||
| // | // | ||||
| // This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
| // GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
| //mod attachio; | mod attachio; | ||||
| //mod clientext; | //mod clientext; | ||||
| //pub mod locator; | //pub mod locator; | ||||
| pub mod message; | pub mod message; | ||||
| pub mod procutil; | pub mod procutil; | ||||
| //mod runcommand; | //mod runcommand; | ||||
| //mod uihandler; | //mod uihandler; | ||||
| //pub use clientext::ChgClientExt; | //pub use clientext::ChgClientExt; | ||||
| //pub use uihandler::{ChgUiHandler, SystemHandler}; | //pub use uihandler::{ChgUiHandler, SystemHandler}; | ||||