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
[SSHD-85] Tests & Candidate fix #33
Conversation
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.
In general, in order for me to actually test the code I need it to conform to the project style. Please make sure you run a mvn clean install
before re-posting the changes. In general here is what seems to be missing:
- Standard license header
- Standard class javadoc format
- We no longer use
final
modifier - All our test methods are called
testXXX
- All our test classes extend
BaseTestSupport
and callassertXXX
directly instead ofAssert.assertXXX
Please make the necessary changes and re-post once mvn clean install
passed so I can start examining it.
@@ -215,6 +215,7 @@ public void run() { | |||
try (InputStream sockIn = s.getInputStream(); | |||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) { | |||
|
|||
//Reag a payload worth of data, save it to baos | |||
while (baos.size() < payload.length()) { |
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.
- Typo error (I assume)
Read
... - Please align the comment indentation
@@ -0,0 +1,227 @@ | |||
package org.apache.sshd.common.forward.sshd85; |
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.
Please attach the standard license header
* close the TCP connection to indicate that all data has been sent. | ||
* | ||
* This test implements TWO methods to connect to and read data from that | ||
* server. |
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.
Please format this comment according to our standards
* @author bkuker | ||
* | ||
*/ | ||
public abstract class AbstractServerCloseTest { |
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.
All out tests extend BaseTestSupport
class
private final String PAYLOAD; | ||
private AsynchronousServerSocketChannel testServerSock; | ||
|
||
public AbstractServerCloseTest() { |
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.
abstract class constructor should be protected
*/ | ||
@Before | ||
public void startTestServer() throws Exception { | ||
final InetSocketAddress sockAddr = new InetSocketAddress("localhost", TEST_SERVER_PORT); |
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.
no need to declare it final
@Override | ||
public void completed(final AsynchronousSocketChannel sockChannel, | ||
final AsynchronousServerSocketChannel serverSock) { | ||
// a connection is accepted, start to accept next |
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.
Please remove final
modifier
// a connection is accepted, start to accept next | ||
// connection | ||
serverSock.accept(serverSock, this); | ||
final ByteBuffer buf = ByteBuffer.wrap(PAYLOAD.getBytes()); |
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.
Please remove final
modifier
try { | ||
final int read2 = s.getInputStream().read(b2); | ||
log.info("Got {} bytes from the server: {}", read2, new String(b2, 0, read2)); | ||
Assert.assertEquals(PAYLOAD, new String(b1, 0, read1) + new String(b2, 0, read2)); |
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.
We use direct assertXXX
calls (inherited from BaseTestSupport
)
Will do thanks! I had just copied these tests from my internal branch and had not worried about style yet... |
Our (current) opinion is that it makes the code cluttered with un-necessary |
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.apache.sshd.common.forward.sshd85; |
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.
No need for the sshd85
package - org.apache.sshd.common.forward
is good enough
log.info("SSHD Running on port {}", server.getPort()); | ||
} | ||
|
||
@AfterClass |
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.
Please use some reasonable timeout for the await
to avoid stuck servers,
@Override | ||
protected int startLocalPF() throws Exception { | ||
final SshdSocketAddress remote = new SshdSocketAddress("localhost", 0); | ||
final SshdSocketAddress local = new SshdSocketAddress("localhost", TEST_SERVER_PORT); |
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.
Don't use localhost
- use TEST_LOCALHOST
from BaseTestSuppot
public class ApacheServerJSchClient extends AbstractServerCloseTest { | ||
private static final Logger log = LoggerFactory.getLogger(ApacheServerJSchClient.class); | ||
|
||
private static final int SSH_SERVER_PORT = 1123; |
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.
Please don't use static ports - what if this port is used by some service on the user's host
return TEST_SERVER_PORT; | ||
} | ||
|
||
} |
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.
Superfluous empty line
*/ | ||
package org.apache.sshd.common.forward.sshd85; | ||
|
||
public class NoServerNoClient extends AbstractServerCloseTest { |
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.
Missing explicit public no-args constructor
|
||
@Override | ||
protected int startRemotePF() throws Exception { | ||
port++; |
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.
Use AtomicInteger.incrementAndGet()
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.
Actually I had to change that to use dynamically chosen free ports instead.
@Before | ||
public void createClient() throws Exception { | ||
log.info("Creating SSH Client..."); | ||
JSch client = new JSch(); |
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.
Please call JSchLogger.init()
in the @BeforeClass
method
|
||
@AfterClass | ||
public static void stopServer() throws IOException { | ||
server.close(true).await(); |
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.
Please add a reasonable await timeout to avoid stuck servers
* @author bkuker | ||
* | ||
*/ | ||
public abstract class AbstractServerCloseTest { |
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.
abstract classes from which other tests are derived are called XXXTestSupport
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class ApacheServerApacheClient extends AbstractServerCloseTest { |
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.
Test classes are called XXXTest
and have a @FixMethodOrder(MethodSorters.NAME_ASCENDING
annotaton
import com.jcraft.jsch.JSch; | ||
import com.jcraft.jsch.Session; | ||
|
||
public class ApacheServerJSchClient extends AbstractServerCloseTest { |
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.
See comment regarding test class name and required annotation
*/ | ||
package org.apache.sshd.common.forward.sshd85; | ||
|
||
public class NoServerNoClient extends AbstractServerCloseTest { |
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.
See comment regarding test class name and required annotation
@lgoldstein Before I kill you with 1000 more style violations, can you direct me to the current version of those guidelines? The web page https://mina.apache.org/sshd-project/sources.html is horribly out of date, and the "contributing" link just asks for money, not code! |
Apologies for so many commits - I am not able to upgrade Maven on my Windows desktop where the bug exists, so I have to run checkstyle on the Linux desktop! I am getting dizzy spinning my chair from one to the other. |
Thanks so much for your feedback; I'd been so deep in debugging the past few days I was worried less about the style. Also I did not mean to imply anything negative about the "final" style, I have just been getting used to doing it more often. (BTW at the moment fixing this falls under "my job" so I'll be very quick to fix anything you ask, please don't take that for impatience or demanding responses from you immediately) |
Thanks for all your effort, my comments may seem short/terse but they are in no way a sign of impatience - I appreciate your willingness to adhere to the project standards. Many programmers discount style as being petty, but since this is an open-source project, it is helpful to have the source code have a standard look-and-feel so one can find one's way easily around it. I apologize for not having more time to discuss or write a document explaining the style choices and why we chose them - but as you may have surmised, my involvement with this project is purely on a voluntary basis - for which my day job does not leave too much time. All I can recommend at the moment is to look at other existing test classes and adopt the style in them. |
No problem and no worries, and I totally agree (Well I may have a different opinion on tabs and final but it is more important to conform to contribute!) I'll have another look through later on, I think the tests are a lot better then before your feedback. I also want to take some time to better understand WHY the one line fix makes a difference, because I fee like the behavior should be the same with or without it. |
Thanks for the updates, every one is an improvement to the tests. They still pass, which is good, and they still fail without the fix, which is great. I am not sure from your branch if you intended to remove ApacheServerJSchClientTest and NoServerNoClientTest. IMHO the JSCH one should still be included. I actually had to write it in order to narrow down the problem. The NoServerNoClientTest... I see it as a test to ensure that the Test Server and Test Client work properly, and confirmation that yes, TCP works that way and you should be able to expect to receive all the data. I did have to run it and see it pass ten thousand times before I was certain enough to re-open the ticket :) |
try (OutputStream sockOut = s.getOutputStream()) { | ||
sockOut.write(baos.toByteArray(), 0, payload.length()); | ||
sockOut.flush(); | ||
sockOut.close(); |
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.
Why the change here ?
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.
I probably should not have mixed this in, it is unrelated to SSHD-85, sorry. This test did not pass on the unmodified code base , it seems logically wrong, so I fixed it.
I have added all the tests from this PR to my branch and surprisingly, the previously failing test now succeeds - I am not comfortable with this as it seems that the tests affect each other. I will run some more tests to make sure that is not the case... |
I suspect that with so many minor formatting, renaming, and small cleanups and fixes something may have been broken.. I will go through everything thoroughly and make sure each test still fails as expected without the fix, and passes as expected with the fix. I will also remove the unrelated fix to PortForwardingLoadTest from this PR. I will look further into what I believe is a bug in that test, make sure it really is a bug, and track it as a different ticket as I should have from the beginning! |
I updated the branch - everything passes - I am still running some tests to be doubly sure - including running tests on Linux (Fedora 22 in this case). If they all pass I will merge the branch |
FInal version was completed and merged by @lgoldstein |
SSHD-85 is a bug that occurs on Windows where the last few bytes of data sent through a port forward can be lost when the server at the far end of the port forward sends the data and immediately closes the socket.
An example of this server behavior would be HTTP 1.0 with the Connection: close header. The server sends the data, and then closes the socket to indicate that it is done sending data. The server does not send a Content-Length header or similar. This is a stupid way to do it, all modern http servers do better, but there are still old embedded systems that work this way, and it does impact my use case.
The test cases include some sleeps on the client end. This seems to be a race condition that occurs possibly one time in a hundred or more, but these sleeps cause it to happen every single time.
The test cases include two controls: JSCH as the client
During testing I did verify that the test server did send all data to the ssh client by examining the buffers in the ssh client and seeing all of the data. I then verified that all of the data was transferred from the ssh client to the ssh server by examining the buffers in the sshd server. The sshd server does write all of that data to the socket as far as I can tell, but on Windows some still goes missing at the end! I also verified that on Local port forwards the error does occur with Apache SSHD as the client, and does NOT occur with JSCH as the client. I would like to try that the other way around with Apache SSH Client and another server, but the pickings for Java SSH servers are slim, and this one is the best!
I am currently trying to understand WHY the fix (basically one line, getSocket().shutdownOutput()) fixes the problem. I am submitting this PR now in hopes that someone with a better understanding will point out why it is the correct fix and accept it. All I can say at the moment is that it works.
Also includes a fix for a test in PortForwardingLoadTest which was failing for me before I started investigating. Frankly it looks like it never worked; I would guess that there was some copy-paste mistake in the past when it got updated to use try-with-resources.
I am ... very motivated to get this fixed so I do not have to maintain my own private branch of sshd, so please ask questions, make suggestions, if you think I am totally crazy help me prove that I am not!