-
Notifications
You must be signed in to change notification settings - Fork 37
Fix TCP client seeing "already connected" error when resend message after having exception in the first send #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
5a75f8f
26fa45b
1807393
ed829dc
93d156f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ dependencies { | |
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.11.1' | ||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.1' | ||
implementation 'org.slf4j:slf4j-api:1.7.30' | ||
implementation 'org.slf4j:slf4j-simple:1.7.30' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This shouldn't be needed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without this, we'd see
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is okay, we don't want or need to provide a logging implementation. User's provide their own logger. |
||
implementation 'org.javatuples:javatuples:1.2' | ||
|
||
// Use JUnit test framework | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,12 +31,13 @@ public class TCPClient implements SocketClient { | |
private boolean shouldConnect = true; | ||
|
||
public TCPClient(Endpoint endpoint) { | ||
socket = createSocket(); | ||
this.endpoint = endpoint; | ||
} | ||
|
||
private void connect() { | ||
try { | ||
// Avoid "socket already connected" error (https://issues.amazon.com/issues/P54323886) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove the link from this comment. Also, the comment might not even be needed. If we keep it, you should be more descriptive as to why we want to create a new socket on every connect() call. |
||
socket = createSocket(); | ||
socket.connect(new InetSocketAddress(endpoint.getHost(), endpoint.getPort())); | ||
shouldConnect = false; | ||
} catch (Exception e) { | ||
|
@@ -51,7 +52,7 @@ protected Socket createSocket() { | |
|
||
@Override | ||
public synchronized void sendMessage(String message) { | ||
if (socket.isClosed() || shouldConnect) { | ||
if (socket == null || socket.isClosed() || shouldConnect) { | ||
connect(); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,4 +48,50 @@ protected Socket createSocket() { | |
|
||
assertEquals(bos.toString(), message); | ||
} | ||
|
||
@Test | ||
public void testSendMessageWithGetOSException_THEN_createSocketTwice() throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once this PR is merged can we also add these tests to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I believe this also apply to that |
||
Socket socket = mock(Socket.class); | ||
doNothing().when(socket).connect(any()); | ||
when(socket.getOutputStream()).thenThrow(IOException.class); | ||
|
||
Endpoint endpoint = Endpoint.DEFAULT_TCP_ENDPOINT; | ||
TCPClient client = | ||
new TCPClient(endpoint) { | ||
@Override | ||
protected Socket createSocket() { | ||
return socket; | ||
} | ||
}; | ||
|
||
TCPClient spyClient = spy(client); | ||
|
||
String message = "Test message"; | ||
spyClient.sendMessage(message); | ||
verify(spyClient, atLeast(2)).createSocket(); | ||
} | ||
|
||
@Test | ||
public void testSendMessageWithWriteOSException_THEN_createSocketTwice() throws IOException { | ||
Socket socket = mock(Socket.class); | ||
doNothing().when(socket).connect(any()); | ||
ByteArrayOutputStream bos = mock(ByteArrayOutputStream.class); | ||
when(socket.getOutputStream()).thenReturn(bos); | ||
doThrow(IOException.class).when(bos).write(any()); | ||
|
||
Endpoint endpoint = Endpoint.DEFAULT_TCP_ENDPOINT; | ||
TCPClient client = | ||
new TCPClient(endpoint) { | ||
@Override | ||
protected Socket createSocket() { | ||
return socket; | ||
} | ||
}; | ||
|
||
TCPClient spyClient = spy(client); | ||
|
||
String message = "Test message"; | ||
spyClient.sendMessage(message); | ||
verify(spyClient, atLeast(2)).createSocket(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you try running integration tests with an IAM user with no session token variable set, and see if this still works?
Also, we'll want this on master later as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay cool. We still want to be using temporary credentials with a session token when possible, I just want to make sure it will still continue to work with long-lived credentials (if someone running the tests chooses to do that).