Skip to content

Commit 6ffdaf2

Browse files
committed
10.0.3 - possible fixes for file manager connection aborts
1 parent 2db0f0d commit 6ffdaf2

5 files changed

Lines changed: 74 additions & 31 deletions

File tree

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ AUTO-GENERATED FILE, CHANGES SHOULD BE DONE IN ./JPM.java or ./src/main/java//JP
1111
<modelVersion>4.0.0</modelVersion>
1212
<groupId>com.osiris.autoplug.client</groupId>
1313
<artifactId>AutoPlug-Client</artifactId>
14-
<version>10.0.1</version>
14+
<version>10.0.3</version>
1515
<properties>
1616
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1717
<java.version>9</java.version>
18-
<version>10.0.1</version>
18+
<version>10.0.3</version>
1919
<main-class>com.osiris.autoplug.client.Main</main-class>
2020
<slf4j.version>2.0.13</slf4j.version>
2121
<name>AutoPlug-Client</name>

src/main/java/JPM.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public ThisProject(List<String> args) throws Exception {
3131
// Override default configurations
3232
this.groupId = "com.osiris.autoplug.client";
3333
this.artifactId = "AutoPlug-Client";
34-
this.version = "10.0.1";
34+
this.version = "10.0.3";
3535
this.mainClass = "com.osiris.autoplug.client.Main";
3636
this.jarName = "AutoPlug-Client-original.jar";
3737
this.fatJarName = "AutoPlug-Client.jar";

src/main/java/com/osiris/autoplug/client/network/online/ConMain.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import io.netty.buffer.ByteBuf;
66
import io.netty.channel.ChannelHandlerContext;
77
import io.netty.handler.codec.ReplayingDecoder;
8+
import io.netty.handler.timeout.IdleStateEvent;
89
import io.netty.handler.timeout.IdleStateHandler;
910
import org.jetbrains.annotations.Nullable;
1011

@@ -25,10 +26,16 @@ public class ConMain extends DefaultConnection {
2526
public boolean isDone = false; // So that the log isn't a mess because of the processes which start right after this.
2627
public AtomicBoolean isUserActive = new AtomicBoolean(false);
2728
public boolean isUserActiveOld = false;
28-
public int msUntilRetry = 30000;
2929

3030
public ConMain() {
3131
super((byte) 0);
32+
CON_PUBLIC_DETAILS.conMain = this;
33+
CON_CONSOLE_RECEIVE.conMain = this;
34+
CON_CONSOLE_SEND.conMain = this;
35+
CON_SYSTEM_CONSOLE_SEND.conMain = this;
36+
CON_SYSTEM_CONSOLE_RECEIVE.conMain = this;
37+
CON_PRIVATE_DETAILS.conMain = this;
38+
CON_FILE_MANAGER.conMain = this;
3239
}
3340

3441
@Override
@@ -38,7 +45,6 @@ public boolean open() {
3845
super.open();
3946
AL.info("Authentication success!");
4047
CON_PUBLIC_DETAILS.open();
41-
msUntilRetry = 30000;
4248
isDone = true;
4349
} catch (Exception e) {
4450
isDone = true;
@@ -85,6 +91,17 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
8591
checkpoint();
8692
}
8793

94+
@Override
95+
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
96+
if (evt instanceof IdleStateEvent) {
97+
AL.warn("Server connection timed out (no ping received). Assuming connection is dead.");
98+
// Closing the context will automatically trigger channelInactive() below and schedule a reconnect
99+
ctx.close();
100+
} else {
101+
super.userEventTriggered(ctx, evt);
102+
}
103+
}
104+
88105
@Override
89106
public void channelInactive(ChannelHandlerContext ctx) {
90107
if (!isClosing.get()) scheduleReconnect();
@@ -99,13 +116,6 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
99116
return true;
100117
}
101118

102-
@Override
103-
protected int getReconnectDelay() {
104-
int delay = msUntilRetry;
105-
msUntilRetry += 30000;
106-
return delay;
107-
}
108-
109119
@Override
110120
public void close() {
111121
closeTempCons();

src/main/java/com/osiris/autoplug/client/network/online/DefaultConnection.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.netty.handler.ssl.SslContext;
1515
import io.netty.handler.ssl.SslContextBuilder;
1616
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
17+
import org.jetbrains.annotations.Nullable;
1718

1819
import java.security.InvalidKeyException;
1920
import java.util.List;
@@ -35,6 +36,11 @@ public abstract class DefaultConnection implements AutoCloseable {
3536
public AtomicBoolean isClosing = new AtomicBoolean(false);
3637
private boolean useSsl = false;
3738

39+
@Nullable public ConMain conMain = null;
40+
public boolean isSecondary(){
41+
return conMain != null;
42+
}
43+
3844
public Thread reconnectThread = null;
3945

4046
public DefaultConnection(byte con_type) {
@@ -58,8 +64,13 @@ public synchronized boolean open() throws Exception {
5864
return errorCode == 0;
5965
}
6066

61-
protected int getReconnectDelay() {
62-
return 30000;
67+
public int initalReconnectDelay = 2000;
68+
public int additionalReconnectDelay = 30000;
69+
public int currentReconnectDelay = initalReconnectDelay;
70+
protected int getNewReconnectDelay() {
71+
int delay = currentReconnectDelay;
72+
currentReconnectDelay = currentReconnectDelay + additionalReconnectDelay; // For next time
73+
return delay;
6374
}
6475

6576
protected synchronized void scheduleReconnect() {
@@ -71,10 +82,17 @@ protected synchronized void scheduleReconnect() {
7182

7283
reconnectThread = new Thread(() -> {
7384
try {
74-
int delay = getReconnectDelay();
85+
int delay = getNewReconnectDelay();
7586
AL.warn(this.getClass().getSimpleName()+ " Connection problems! Reconnecting in " + delay / 1000 + " seconds...");
7687
Thread.sleep(delay);
7788

89+
if(conMain != null // same as isSecondary()
90+
&& !conMain.isConnected()){
91+
AL.debug(this.getClass(), "Reconnect attempt aborted," +
92+
" because main con is not connected. Assume reconnect will be handled by main con.");
93+
return;
94+
}
95+
7896
if (!open()) scheduleReconnect();
7997
} catch (Exception e) {
8098
AL.warn("Reconnect error", e);
@@ -143,6 +161,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
143161
try {
144162
channel = b.connect(ip, port).sync().channel();
145163
this.errorCode = authFuture.get(60, TimeUnit.SECONDS);
164+
currentReconnectDelay = initalReconnectDelay; // Reset reconnect delay on success connect
146165
} catch (Exception e) {
147166
if (e.getMessage() != null && e.getMessage().contains("Throttled")) {
148167
int punishment = Integer.parseInt(e.getMessage().split(":")[1].trim());

src/main/java/com/osiris/autoplug/client/network/online/connections/ConFileManager.java

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.List;
1919
import java.util.concurrent.Executors;
2020

21+
// Client code
2122
public class ConFileManager extends DefaultConnection {
2223
private FileOutputStream tempFileOutputStream;
2324

@@ -32,50 +33,55 @@ public boolean open() throws Exception {
3233

3334
channel.pipeline().addLast(new ReplayingDecoder<Void>() {
3435
int state = 0;
35-
// 0 = Opcode, 1 = FileDetails WantsContent, 2 = SaveFile Chunk, 3 = UploadFile Chunk
3636
File currentFile;
3737

3838
@Override
3939
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> outList) {
4040
try {
4141
if (state == 0) {
4242
byte op = in.readByte();
43-
if (op == 0) { // Get File Details
43+
if (op == 0) {
4444
String path = NettyUtils.readUTF(in);
4545
currentFile = path.isEmpty() ? GD.WORKING_DIR : new File(path);
46-
Executors.newSingleThreadExecutor().submit(() -> sendFileDetailsAndChildren(currentFile));
46+
// FIX: Use shared executor instead of creating a new thread pool every time
47+
DefaultConnection.exec.submit(() -> sendFileDetailsAndChildren(currentFile));
4748
if (!currentFile.isDirectory()) {
4849
state = 1;
4950
}
5051
}
51-
else if (op == 1) { // Create
52+
else if (op == 1) {
5253
String path = NettyUtils.readUTF(in);
5354
boolean isDir = in.readBoolean();
54-
Executors.newSingleThreadExecutor().submit(() -> doCreate(path, isDir));
55+
DefaultConnection.exec.submit(() -> doCreate(path, isDir));
5556
}
56-
else if (op == 2) { // Delete
57+
else if (op == 2) {
5758
int count = in.readInt();
5859
String[] paths = new String[count];
5960
for (int i = 0; i < count; i++) paths[i] = NettyUtils.readUTF(in);
60-
Executors.newSingleThreadExecutor().submit(() -> doDelete(paths));
61+
DefaultConnection.exec.submit(() -> doDelete(paths));
6162
}
62-
else if (op == 3) { // Rename
63+
else if (op == 3) {
6364
String path = NettyUtils.readUTF(in);
6465
String newName = NettyUtils.readUTF(in);
65-
Executors.newSingleThreadExecutor().submit(() -> doRename(path, newName));
66+
DefaultConnection.exec.submit(() -> doRename(path, newName));
6667
}
6768
else if (op == 4) { // Save File
6869
currentFile = new File(NettyUtils.readUTF(in));
70+
if (currentFile.getParentFile() != null) currentFile.getParentFile().mkdirs();
71+
if (!currentFile.exists()) currentFile.createNewFile();
6972
tempFileOutputStream = new FileOutputStream(currentFile);
73+
writeBool(true); // FIX: Notify the server we are ready to receive the stream
7074
state = 2;
7175
}
7276
else if (op == 5) { // Receive Upload
7377
currentFile = new File(NettyUtils.readUTF(in));
78+
if (currentFile.getParentFile() != null) currentFile.getParentFile().mkdirs();
7479
if (!currentFile.exists()) currentFile.createNewFile();
7580
tempFileOutputStream = new FileOutputStream(currentFile);
81+
writeBool(true); // FIX: Notify the server we are ready to receive the stream
7682
state = 3;
7783
}
78-
else if (op == 6) { // Copy/Cut
84+
else if (op == 6) {
7985
int count = in.readInt();
8086
boolean isCopy = in.readBoolean();
8187
String targetDir = NettyUtils.readUTF(in);
@@ -85,20 +91,28 @@ else if (op == 6) { // Copy/Cut
8591
paths[i] = NettyUtils.readUTF(in);
8692
isDirs[i] = in.readBoolean();
8793
}
88-
Executors.newSingleThreadExecutor().submit(() -> doCopyCut(paths, isDirs, isCopy, targetDir));
94+
DefaultConnection.exec.submit(() -> doCopyCut(paths, isDirs, isCopy, targetDir));
8995
}
90-
else if (op == 7) { // Roots
91-
Executors.newSingleThreadExecutor().submit(ConFileManager.this::sendRoots);
96+
else if (op == 7) {
97+
DefaultConnection.exec.submit(ConFileManager.this::sendRoots);
9298
}
9399
checkpoint();
94100
}
95101
else if (state == 1) { // Wait for Content Boolean
96102
boolean wantsContent = in.readBoolean();
97103
if (wantsContent) {
98-
Executors.newSingleThreadExecutor().submit(() -> {
104+
DefaultConnection.exec.submit(() -> {
99105
try {
100106
sendFileContent(currentFile);
101-
} catch (IOException e) { AL.warn(e); }
107+
} catch (Exception e) {
108+
AL.warn("Failed to send file content:", e);
109+
// FIX: Send EOF so the server doesn't wait indefinitely and timeout
110+
if (channel != null && channel.isActive()) {
111+
ByteBuf buf = channel.alloc().buffer();
112+
NettyUtils.writeUTF(buf, NettyUtils.EOF);
113+
channel.writeAndFlush(buf);
114+
}
115+
}
102116
});
103117
}
104118
state = 0;
@@ -167,7 +181,7 @@ private void sendFileDetailsAndChildren(File file) {
167181
encodeDetails(buf, file);
168182
if (file.isDirectory()) {
169183
File[] files = file.listFiles();
170-
if (files == null) buf.writeInt(0);
184+
if (files == null || files.length == 0) buf.writeInt(0);
171185
else {
172186
buf.writeInt(files.length);
173187
for (File f : files) if (f.isDirectory()) encodeDetails(buf, f);

0 commit comments

Comments
 (0)