Merge branch '1.3.x'
This commit is contained in:
commit
6e3faecce6
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.devtools.tunnel.client;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
|
@ -46,6 +47,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Rob Winch
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
* @see TunnelClient
|
||||
* @see org.springframework.boot.devtools.tunnel.server.HttpTunnelServer
|
||||
|
|
@ -157,7 +159,13 @@ public class HttpTunnelConnection implements TunnelConnection {
|
|||
sendAndReceive(payload);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.trace("Unexpected connection error", ex);
|
||||
if (ex instanceof ConnectException) {
|
||||
logger.warn("Failed to connect to remote application at "
|
||||
+ HttpTunnelConnection.this.uri);
|
||||
}
|
||||
else {
|
||||
logger.trace("Unexpected connection error", ex);
|
||||
}
|
||||
closeQuietly();
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +196,12 @@ public class HttpTunnelConnection implements TunnelConnection {
|
|||
close();
|
||||
return;
|
||||
}
|
||||
if (response.getStatusCode() == HttpStatus.SERVICE_UNAVAILABLE) {
|
||||
logger.warn("Remote application responded with service unavailable. Did "
|
||||
+ "you forget to start it with remote debugging enabled?");
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if (response.getStatusCode() == HttpStatus.OK) {
|
||||
HttpTunnelPayload payload = HttpTunnelPayload.get(response);
|
||||
if (payload != null) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousCloseException;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
|
@ -36,6 +37,7 @@ import org.springframework.util.Assert;
|
|||
* specified port for local clients to connect to.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class TunnelClient implements SmartInitializingSingleton {
|
||||
|
|
@ -143,6 +145,9 @@ public class TunnelClient implements SmartInitializingSingleton {
|
|||
try {
|
||||
handleConnection(socket);
|
||||
}
|
||||
catch (AsynchronousCloseException ex) {
|
||||
// Connection has been closed. Keep the server running
|
||||
}
|
||||
finally {
|
||||
socket.close();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -88,6 +88,10 @@ import org.springframework.util.Assert;
|
|||
* <td>410 (Gone)</td>
|
||||
* <td>The target server has disconnected.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>503 (Service Unavailable)</td>
|
||||
* <td>The target server is unavailable</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* <p>
|
||||
* Requests and responses that contain payloads include a {@code x-seq} header that
|
||||
|
|
@ -96,6 +100,7 @@ import org.springframework.util.Assert;
|
|||
* {@code 1}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
* @see org.springframework.boot.devtools.tunnel.client.HttpTunnelConnection
|
||||
*/
|
||||
|
|
@ -153,6 +158,9 @@ public class HttpTunnelServer {
|
|||
catch (ConnectException ex) {
|
||||
httpConnection.respond(HttpStatus.GONE);
|
||||
}
|
||||
catch (RemoteDebugNotRunningException ex) {
|
||||
httpConnection.respond(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.devtools.tunnel.server;
|
||||
|
||||
/**
|
||||
* Exception thrown to indicate that remote debug is not running.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class RemoteDebugNotRunningException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -20,12 +20,12 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.lang.UsesUnsafeJava;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link PortProvider} that provides the port being used by the Java remote debugging.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RemoteDebugPortProvider implements PortProvider {
|
||||
|
||||
|
|
@ -35,7 +35,9 @@ public class RemoteDebugPortProvider implements PortProvider {
|
|||
|
||||
@Override
|
||||
public int getPort() {
|
||||
Assert.state(isRemoteDebugRunning(), "Remote debug is not running");
|
||||
if (!isRemoteDebugRunning()) {
|
||||
throw new RemoteDebugNotRunningException();
|
||||
}
|
||||
return getRemoteDebugPort();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -37,12 +37,13 @@ import org.springframework.mock.http.client.MockClientHttpResponse;
|
|||
* Mock {@link ClientHttpRequestFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
|
||||
|
||||
private AtomicLong seq = new AtomicLong();
|
||||
|
||||
private Deque<Response> responses = new ArrayDeque<Response>();
|
||||
private Deque<Object> responses = new ArrayDeque<Object>();
|
||||
|
||||
private List<MockClientHttpRequest> executedRequests = new ArrayList<MockClientHttpRequest>();
|
||||
|
||||
|
|
@ -58,6 +59,12 @@ public class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public void willRespond(IOException... response) {
|
||||
for (IOException exception : response) {
|
||||
this.responses.addLast(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public void willRespond(String... response) {
|
||||
for (String payload : response) {
|
||||
this.responses.add(new Response(0, payload.getBytes(), HttpStatus.OK));
|
||||
|
|
@ -81,11 +88,15 @@ public class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
|
|||
@Override
|
||||
protected ClientHttpResponse executeInternal() throws IOException {
|
||||
MockClientHttpRequestFactory.this.executedRequests.add(this);
|
||||
Response response = MockClientHttpRequestFactory.this.responses.pollFirst();
|
||||
Object response = MockClientHttpRequestFactory.this.responses.pollFirst();
|
||||
if (response instanceof IOException) {
|
||||
throw (IOException) response;
|
||||
}
|
||||
if (response == null) {
|
||||
response = new Response(0, null, HttpStatus.GONE);
|
||||
}
|
||||
return response.asHttpResponse(MockClientHttpRequestFactory.this.seq);
|
||||
return ((Response) response)
|
||||
.asHttpResponse(MockClientHttpRequestFactory.this.seq);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.devtools.tunnel.client;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
|
@ -33,10 +34,12 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
import org.springframework.boot.devtools.test.MockClientHttpRequestFactory;
|
||||
import org.springframework.boot.devtools.tunnel.client.HttpTunnelConnection.TunnelChannel;
|
||||
import org.springframework.boot.test.OutputCapture;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
|
@ -46,12 +49,16 @@ import static org.mockito.Mockito.verify;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Rob Winch
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class HttpTunnelConnectionTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Rule
|
||||
public OutputCapture outputCapture = new OutputCapture();
|
||||
|
||||
private int port = SocketUtils.findAvailableTcpPort();
|
||||
|
||||
private String url;
|
||||
|
|
@ -142,6 +149,25 @@ public class HttpTunnelConnectionTests {
|
|||
assertThat(this.requestFactory.getExecutedRequests().size()).isGreaterThan(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serviceUnavailableResponseLogsWarningAndClosesTunnel() throws Exception {
|
||||
this.requestFactory.willRespond(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
TunnelChannel tunnel = openTunnel(true);
|
||||
assertThat(tunnel.isOpen()).isFalse();
|
||||
this.outputCapture.expect(containsString(
|
||||
"Did you forget to start it with remote debugging enabled?"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectFailureLogsWarning() throws Exception {
|
||||
this.requestFactory.willRespond(new ConnectException());
|
||||
TunnelChannel tunnel = openTunnel(true);
|
||||
assertThat(tunnel.isOpen()).isFalse();
|
||||
this.outputCapture.expect(containsString(
|
||||
"Failed to connect to remote application at http://localhost:"
|
||||
+ this.port));
|
||||
}
|
||||
|
||||
private void write(TunnelChannel channel, String string) throws IOException {
|
||||
channel.write(ByteBuffer.wrap(string.getBytes()));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue