museun / twitchchat

interface to the irc portion of Twitch's chat

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Panic when sending a message

Chronophylos opened this issue · comments

I am trying to send DuckerZ as einarmwischer in #fischklatscher but get a panic.

my code:

let cmd = commands::privmsg(channel, &msg);
dbg!(cmd);
writer.encode(cmd).await?;

dbg! returns:

[src/bot.rs:42] cmd = Privmsg {
    channel: "fischklatscher",
    msg: "DuckerZ",
}

log snippet:

[tbc::bot] TRACE Sending message to fischklatscher
[src/bot.rs:42] cmd = Privmsg {
    channel: "fischklatscher",
    msg: "DuckerZ",
}
[twitchchat::decoder] TRACE < :einarmwischer.tmi.twitch.tv 353 einarmwischer = #fischklatscher :einarmwischer\r\n
[twitchchat::runner::async_runner] TRACE < :einarmwischer.tmi.twitch.tv 353 einarmwischer = #fischklatscher :einarmwischer\r\n
[twitchchat::runner::async_runner] TRACE draining messages
[twitchchat::decoder] TRACE < :einarmwischer.tmi.twitch.tv 366 einarmwischer #fischklatscher :End of /NAMES list\r\n
[twitchchat::runner::async_runner] TRACE < :einarmwischer.tmi.twitch.tv 366 einarmwischer #fischklatscher :End of /NAMES list\r\n
[twitchchat::runner::async_runner] TRACE draining messages
[twitchchat::runner::rate_limit] TRACE > PRIVMSG #fischklatscher :DuckerZ\r\n

Panic occurs at:

let msg = crate::irc::IrcMessage::parse(crate::MaybeOwned::Borrowed(msg))
.expect("encoder should produce valid IRC messages");

I tried reproducing the panic but the code in this gist did not panic

That is odd, the stuff in commands (e.g. privmsg()) should always produce messages that the internal stuff can parse.

for example:

let msg = "DuckerZ";
let channel = "fischklatscher";
let cmd = commands::privmsg(channel, &msg);

let mut v = vec![];
cmd.encode(&mut v).unwrap();

let msg = std::str::from_utf8(&v).unwrap();
eprintln!("{}", msg.escape_debug());
// PRIVMSG #fischklatscher :DuckerZ\r\n

let msg = IrcMessage::parse(msg.into()).unwrap();
eprintln!("{:#?}", msg);
// IrcMessage {
//     raw: "PRIVMSG #fischklatscher :DuckerZ\r\n",
//     tags: None,
//     prefix: None,
//     command: "PRIVMSG",
//     args: Some(
//         "#fischklatscher",
//     ),
//     data: Some(
//         "DuckerZ",
//     ),
// }

Are you sure thats where the panic was?

This is the log line I get

thread 'async-std/runtime' panicked at 'encoder should produce valid IRC messages: EmptyMessage', /home/chrono/.cargo/registry/src/github.com-1ecc6299db9ec823/twitchchat-0.12.2/src/runner/async_runner.rs:323:27

Currently IrcMessage::parse can only fail in one way: if the string is empty

let data = data.trim();
if data.is_empty() {
return Err(super::MessageError::EmptyMessage);
}

And privmsg() et.al ensure the string isn't empty (it always includes a \r\n). (The trim is the above example is working on a subslice so the \r\n doesn't get trimmed.).

Were you manually writing and not using the Encodable interface?

As I wrote above I am using AsyncWriter::encode and passing Privmsg. I get the writer from AsyncRunner::writer.

Also, as a note, in your gist the 'encode' will not block on the send -- so you need to wait for a bit before ending the test. You may or may not actually do a write because the test ends before the enqueue/rate_limit machinery can do its stuff.

I've tried both with empty messages and empty channels and I cannot get it to fail on the parse.

With the AsyncRunner -- next_message() actually de-queues any possble/pending rate-limited messages and does the writing. The part that is failing is where it reads from the raw user input and determines whether a message should be considered for rate limiting or not. Each channel has its own rate limit (including things like 'slow-mode').

I see.

I know what I may have done wrong. Since you mentioned manually writing I checked my code again and also took a look at the AsyncEncoder::encode and noticed that flush gets called. I had a flush after sending every message.
Removing said call and the panic does not occur anymore.

Oh! That is a bug, a flush on an MpscWriter doesn't actually check to see if any data was buffered.

fn flush(&mut self) -> io::Result<()> {
use crate::channel::TrySendError;
let buf = std::mem::take(&mut self.buf);
match self.channel.try_send(buf.into_boxed_slice()) {
Ok(..) => Ok(()),
Err(TrySendError::Closed(..)) => {
let err = io::Error::new(io::ErrorKind::UnexpectedEof, "writer was closed");
Err(err)
}
// this should never happen, but place the 'data' back into self and
// have it try again
Err(TrySendError::Full(data)) => {
self.buf = data.into();
Ok(())
}
}
}
}

I fixed it in b9f362a

I'll publish a release later today, theres a few more things I want to change.

I released the fix in 7f3f9ff