Conversation
Current coverage is 32.46% (diff: 0.00%)@@ master #138 diff @@
==========================================
Files 139 146 +7
Lines 3460 3610 +150
Methods 0 0
Messages 0 0
Branches 498 502 +4
==========================================
Hits 1172 1172
- Misses 2258 2408 +150
Partials 30 30
|
|
Is the class naming in line for what KICL generally uses? I'd imagine the classes should be named 'IrcDcc' etc. Because the ISupport class follows the same pattern. |
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| final class NettyManager { | ||
|
|
There was a problem hiding this comment.
Looks like your IDE is conflicting with Mbaxter's. I don't really care, but it might be useful to ensure you've got the same code style configuration as him later.
| case "FINGER": | ||
| reply = "FINGER om nom nom tasty finger"; | ||
| break; | ||
| case "DCC": |
There was a problem hiding this comment.
It falls through out (it's the last one), plus there's the entire comment below it :)
There was a problem hiding this comment.
Sounds like bad coding practice to me. I thought the primary use of that was to make more than one case statement apply.
There was a problem hiding this comment.
There's no break needed on the last statement...what would that accomplish?
There was a problem hiding this comment.
It's got a break. Let me have a break.
| */ | ||
| package org.kitteh.irc.client.library; | ||
|
|
||
| import java.io.File; |
| import javax.annotation.Nonnull; | ||
|
|
||
| public interface DCCChat extends DCCExchange { | ||
|
|
There was a problem hiding this comment.
No empty line at the start of the class.
| * | ||
| * @return the socket address of the local end | ||
| */ | ||
| SocketAddress getLocalSocket(); |
There was a problem hiding this comment.
Should be getLocalSocketAddress.
| * | ||
| * @return the socket address of the remote end | ||
| */ | ||
| SocketAddress getRemoteSocket(); |
There was a problem hiding this comment.
Should be getRemoteSocketAddress.
| * the message to send | ||
| */ | ||
| void sendMessage(@Nonnull String message); | ||
|
|
There was a problem hiding this comment.
No empty line at the end of the class.
| } | ||
|
|
||
| class IRCDCCChatSnapshot extends IRCDCCExchangeSnapshot implements DCCChat { | ||
|
|
There was a problem hiding this comment.
No empty line at the start of the class.
|
|
||
| @Override | ||
| public void sendMessage(String message) { | ||
| this.nettyChannel.writeAndFlush(message.trim()); |
| public void sendMessage(String message) { | ||
| this.nettyChannel.writeAndFlush(message.trim()); | ||
| } | ||
|
|
There was a problem hiding this comment.
No empty line at the end of the class.
| */ | ||
| package org.kitteh.irc.client.library.implementation; | ||
|
|
||
| import java.io.File; |
| long delay = 0; | ||
| if (this.scheduledSending != null) { | ||
| delay = this.scheduledSending.getDelay(TimeUnit.MILLISECONDS); // Negligible added delay processing this | ||
| delay = this.scheduledSending.getDelay(TimeUnit.MILLISECONDS); // Negligible |
| void shutdown(@Nonnull String reason); | ||
|
|
||
| @Nonnull | ||
| void beginDCCChat(@Nonnull User target); |
| boolean isAway(); | ||
|
|
||
| /** | ||
| * Begins a DCC chat with this user. When the chat is connected, |
There was a problem hiding this comment.
/**
* Begins a DCC chat with this user.
*
* <p>When the chat is connected, a {@link DCCConnectedEvent} will be fired. If the connection fails,
* a {@link DCCFailedEvent} will be fired.</p>
*/| IRCDCCChatSnapshot snapshot() { | ||
| return new IRCDCCChatSnapshot(this); | ||
| } | ||
|
|
There was a problem hiding this comment.
No empty line at the end of the class.
| abstract class IRCDCCExchangeSnapshot extends IRCActorSnapshot implements DCCExchange { | ||
|
|
||
| protected final io.netty.channel.Channel nettyChannel; | ||
| private final SocketAddress local; |
|
|
||
| protected final io.netty.channel.Channel nettyChannel; | ||
| private final SocketAddress local; | ||
| private final SocketAddress remote; |
| @Override | ||
| @Nonnull | ||
| public String toString() { | ||
| return new ToStringer(this).add("client", this.getClient()).add("name", this.getName()).add("localSocket", this.local).add("remoteSocket", this.remote).add("connected", this.connected).toString(); |
There was a problem hiding this comment.
localSocket -> localSocketAddress
remoteSocket -> ``remoteSocketAddress`
| private final String type; | ||
| private final String name; | ||
| private io.netty.channel.Channel nettyChannel; | ||
| private SocketAddress local; |
| private final String name; | ||
| private io.netty.channel.Channel nettyChannel; | ||
| private SocketAddress local; | ||
| private SocketAddress remote; |
| */ | ||
| void shutdown(@Nonnull String reason); | ||
|
|
||
| void beginDCCChat(@Nonnull User target); |
| public interface DCCExchange extends Actor { | ||
| /** | ||
| * Gets the socket address of the local end. | ||
| * May return null if not connected. |
|
|
||
| /** | ||
| * Gets the socket address of the remote end. | ||
| * May return null if not connected. |
|
|
||
| public DCCFailedEvent(Client client, String reason, Throwable cause) { | ||
| super(client); | ||
| this.reason = reason; |
There was a problem hiding this comment.
Should be Sanity checked, or marked as Optional.
| public DCCFailedEvent(Client client, String reason, Throwable cause) { | ||
| super(client); | ||
| this.reason = reason; | ||
| this.cause = cause; |
There was a problem hiding this comment.
Should be Sanity checked, or marked as Optional.
| abstract void onMessage(String msg); | ||
|
|
||
| final void onSocketBound() { | ||
| sendCTCP(); |
|
|
||
| @Override | ||
| public void sendMessage(@Nonnull String message) { | ||
| Sanity.nullCheck(message, "message cannot be null"); |
There was a problem hiding this comment.
Should most likely ensure the contents of the message are valid? Not sure if DCC allows different content and does not need validation.
There was a problem hiding this comment.
DCC only limitation is that messages are CRLF terminated. I think that accepting CRLF in the message is fine.
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| final class NettyManager { | ||
|
|
| * | ||
| * <p>When the chat is connected, a {@link DCCConnectedEvent} will be fired. If the connection fails, | ||
| * a {@link DCCFailedEvent} will be fired.</p> | ||
| * @see Client#beginDCCChat(User) |
| */ | ||
| void shutdown(@Nonnull String reason); | ||
|
|
||
| /** |
There was a problem hiding this comment.
I think this comment is really well written.
|
|
||
| import javax.annotation.Nonnull; | ||
|
|
||
| public interface DCCChat extends DCCExchange { |
There was a problem hiding this comment.
How does one "shutdown"/"terminate" a DCC chat?
Hold on am i a contributor here or did github extend assignments to non-contributors too? Either way whoa. |
|
You're a |
|
Any plans for increasing the (💥 🌠 ☑️ 🎉 ) coverage once the work is stable? Seems it would currently be decreased by pulling. Lemme know if you want a hand with testing |
| * | ||
| * @see Client#beginDCCChat(User) | ||
| */ | ||
| default void beginDCCChat() { |
There was a problem hiding this comment.
I'd consider renaming this to requestDccChat over begin, because the latter sort-of implies that the target-user does not have to confirm it. In this case you're requesting a (connection|chat), not beginning it. Might be useful to have some more opinions on this. Afaik DCC only sends your IP if you accept, right?
(Unless they don't, and I'm just wrong.)
There was a problem hiding this comment.
You're right, it is a request.
The protocol goes like this:
A = Initiator
B = Target
A: "/ctcp DCC CHAT chat <bound-ip> <bound-port>"
B: Accept/rejects, on accept connects to <bound-ip>:<bound-port>
A/B: Send CRLF terminated messages.
|
@lol768 test will be coming as soon as this lands out of WIP 📝 |
| case "DCC": | ||
| // Handle targeted DCC chat | ||
| // this.fire(new DCCRequestEvent()); | ||
| // TODO how can the parameters be extracted @mbaxter??? |
There was a problem hiding this comment.
event.getParameters().get(2) is going to be the CHAT
3 will be ... uh... chat again? wut
4 will be ip
5 will be ... addr? What is your documentation saying? :P
| /** | ||
| * Sends a DCC CHAT request to the target. | ||
| * | ||
| * <p>When the chat is connected, a {@link DCCConnectedEvent} will be fired. If the connection fails, |
There was a problem hiding this comment.
I wrap javadocs at a max of column 78.
There was a problem hiding this comment.
Also, I don't use html tags for this. Seems to work out fine. :3
There was a problem hiding this comment.
it won't actually work out fine, at least in Eclipse
There was a problem hiding this comment.
Only thing that I care about for 'fine' is the resulting javadocs page.
mbax
left a comment
There was a problem hiding this comment.
Fun stuff. Some initial comments made.
| /** | ||
| * Sends the user a message over DCC chat. | ||
| * | ||
| * <p>This method won't return until the chat is connected and the message has |
There was a problem hiding this comment.
Why is this blocking, though? What if you want to send multiple messages over one DCC session?
If we decide to stick with blocking, I'd advocate for the word 'block' or 'blocking' in the javadoc.
There was a problem hiding this comment.
It's actually non-blocking currently, I need to update docs.
But I don't understand how blocking affects sending multiple messages?
There was a problem hiding this comment.
It affects it only in terms of causing some very sloooooow code. :3
I'm trying to keep KICL non-blocking everywhere I can in the API.
| public interface DCCExchange extends Actor { | ||
| /** | ||
| * Gets the socket address of the local end. | ||
| * May return {@link Optional#empty()} if not connected. |
There was a problem hiding this comment.
I'd just leave it as @return the socket address of the local end if connected
Same for the one below
| /** | ||
| * Gets the connection status. | ||
| * | ||
| * @return {@code true} if the exchange is connected, otherwise false |
There was a problem hiding this comment.
I should probably update my stuff to be {@code true} but why do you not do it for false?
| default void requestDCCChat() { | ||
| this.getClient().requestDCCChat(this); | ||
| } | ||
|
|
| case "DCC": | ||
| // Handle targeted DCC chat | ||
| // this.fire(new DCCRequestEvent()); | ||
| // TODO how can the parameters be extracted @mbaxter??? |
There was a problem hiding this comment.
event.getParameters().get(2) is going to be the CHAT
3 will be ... uh... chat again? wut
4 will be ip
5 will be ... addr? What is your documentation saying? :P
| private final IRCDCCExchange exchange; | ||
| private final InternalClient client; | ||
| // Only allow one connection per DCC. Weird, I know. | ||
| private boolean oneConnection; |
There was a problem hiding this comment.
Perhaps rename to connected? Or something similar. if(oneConnection) reads oddly to me.
| } | ||
| this.oneConnection = true; | ||
| this.exchange.setNettyChannel(channel); | ||
| dccConnections.computeIfAbsent(this.client, c -> new ArrayList<>()).add(channel); |
There was a problem hiding this comment.
Reference this a bit more... clearly. NettyManager.this.dccConnections
There was a problem hiding this comment.
It's actually NettyManager.dccConnections. Do you still want fully qualified for outer static fields?
|
|
||
| private static synchronized void removeClientConnection(@Nonnull ClientConnection connection, boolean reconnecting) { | ||
| connections.remove(connection); | ||
| List<Channel> dcc = dccConnections.get(connection.client); |
There was a problem hiding this comment.
NettyManager.dccConnections?
There was a problem hiding this comment.
Uhhhhh yeah. Missed it being static. This is fine.
40c6bb6 to
1f642f9
Compare
|
|
||
| static Runnable connectDCC(InternalClient client, IRCDCCExchange exchange) { | ||
| Sanity.nullCheck(eventLoopGroup, "A DCC connection cannot be made without a client"); | ||
| ServerBootstrap dccBootstrap = new ServerBootstrap(); |
There was a problem hiding this comment.
You can chain this all:
ChannelFuture future = new ServerBootstrap()
.channel(NioServerSocketChannel.class);
.childHandler(new DCCConnection(exchange, client));
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.group(eventLoopGroup)
.bind(0);| dccConnections.computeIfAbsent(this.client, c -> new ArrayList<>()).add(channel); | ||
|
|
||
| // Outbound - Processed in pipeline back to front. | ||
| channel.pipeline().addFirst("[OUTPUT] Output listener", new MessageToMessageEncoder<String>() { |
There was a problem hiding this comment.
This looks like lots of duplication from above - it should be possible to share most, if not all, of it.
| * a {@link DCCConnectedEvent} will be fired. If the connection fails, a | ||
| * {@link DCCFailedEvent} will be fired.</p> | ||
| */ | ||
| void requestDCCChat(@Nonnull User target); |
There was a problem hiding this comment.
So there's a way to request a new DCC chat, but I see no way to get existing ones.
There was a problem hiding this comment.
I don't know if that should be needed? All DCC chats will come through as events so you can keep tabs on them if you want.
However, it currently isn't possible to tell if you have multiple DCC exchanges open to a client or distinguish between them, so maybe we can figure out a way to do that and add a way to get a specific event?
| /** | ||
| * Represents an exchange using DCC. | ||
| */ | ||
| public interface DCCExchange extends Actor { |
| reply = "FINGER om nom nom tasty finger"; | ||
| break; | ||
| case "DCC": | ||
| handleDccEvent(user, event.getOriginalMessages(), event.getParameters()); |
| } catch (NumberFormatException invalidPort) { | ||
| return; | ||
| } | ||
| fire(new DCCRequestEvent(this.client, originalMessages, dccType, ip, portInt, user)); |
| * @return the readable reason for the failure | ||
| */ | ||
| public Optional<String> getReason() { | ||
| return Optional.ofNullable(this.reason); |
There was a problem hiding this comment.
Store the Optional in the field instead.
| * @return the exception that caused the failure | ||
| */ | ||
| public Optional<Throwable> getCause() { | ||
| return Optional.ofNullable(this.cause); |
There was a problem hiding this comment.
Store the Optional in the field instead.
| this.port = port; | ||
| } | ||
|
|
||
| public String getType() { |
| ActorProvider.this.client.sendCTCPMessage(this.getName(), this.getCTCP()); | ||
| } | ||
|
|
||
| String getType() { |
| ActorProvider.this.client.getEventManager().callEvent(new DCCMessageEvent(ActorProvider.this.client, Collections.emptyList(), snapshot(), msg)); | ||
| } | ||
|
|
||
| @Override |
| } | ||
|
|
||
| @Override | ||
| IRCDCCChatSnapshot snapshot() { |
| this.connected = actor.connected; | ||
| } | ||
|
|
||
| @Override |
| return Optional.ofNullable(this.localAddress); | ||
| } | ||
|
|
||
| @Override |
ad9896f to
5060a91
Compare
|
k then |
|
💀 RIP DCC.... attempt 1. DCC 2: Electric BoogalooComing soon to a repository near you. |

It's probably robust, but the code style needs work.
This closes #93.
Things to do:
DCCConnectionClosedEvent:P