// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.sdk.internal.transport;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketAddress;
import org.chromium.sdk.ConnectionLogger;
import org.chromium.sdk.ConnectionLogger.StreamListener;
import org.chromium.sdk.util.SignalRelay;
/**
* Wraps net socket and provides loggable reader and loggable writer to facilitate optional
* traffic dumping.
* @param <INPUT_WRAPPER> type of input stream wrapper
* @param <OUTPUT_WRAPPER> type of output stream wrapper
*/
public abstract class AbstractSocketWrapper<INPUT_WRAPPER, OUTPUT_WRAPPER> {
private final Socket socket;
private final INPUT_WRAPPER loggableInput;
private final OUTPUT_WRAPPER loggableOutput;
public AbstractSocketWrapper(SocketAddress endpoint, int connectionTimeoutMs,
ConnectionLogger connectionLogger, WrapperFactory<INPUT_WRAPPER, OUTPUT_WRAPPER> factory)
throws IOException {
this.socket = new Socket();
this.socket.connect(endpoint, connectionTimeoutMs);
INPUT_WRAPPER originalLogReader = factory.wrapInputStream(socket.getInputStream());
OUTPUT_WRAPPER originalLogWriter = factory.wrapOutputStream(socket.getOutputStream());
if (connectionLogger == null) {
loggableInput = originalLogReader;
loggableOutput = originalLogWriter;
} else {
loggableInput = factory.wrapInputStream(originalLogReader,
connectionLogger.getIncomingStreamListener());
loggableOutput = factory.wrapOutputStream(originalLogWriter,
connectionLogger.getOutgoingStreamListener());
connectionLogger.setConnectionCloser(new ConnectionLogger.ConnectionCloser() {
@Override public void closeConnection() {
shutdownRelay.sendSignal(null, new Exception("Close requested from logger UI"));
}
});
}
}
public INPUT_WRAPPER getLoggableInput() {
return loggableInput;
}
public OUTPUT_WRAPPER getLoggableOutput() {
return loggableOutput;
}
public SignalRelay<ShutdownSignal> getShutdownRelay() {
return shutdownRelay;
}
/**
* More a symbolic type than a really used type. Later it may gain some members.
* It is created because we need a generic parameter for SignalRelay.
*/
public interface ShutdownSignal {
}
/**
* Contains a set of abstract methods that cannot belong to {@link AbstractSocketWrapper}
* itself because they are needed from constructor.
* @param <IW>
* @param <OW>
*/
protected interface WrapperFactory<IW, OW> {
IW wrapInputStream(InputStream inputStream);
OW wrapOutputStream(OutputStream outputStream);
IW wrapInputStream(IW originalInputWrapper, StreamListener streamListener);
OW wrapOutputStream(OW originalOutputWrapper, StreamListener streamListener);
}
private final SignalRelay<ShutdownSignal> shutdownRelay =
SignalRelay.create(new SignalRelay.Callback<ShutdownSignal>() {
@Override
public void onSignal(ShutdownSignal param, Exception cause) {
try {
closeImpl();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void closeImpl() throws IOException {
try {
socket.shutdownInput();
} catch (IOException e) {
// ignore
}
try {
socket.shutdownOutput();
} catch (IOException e) {
// ignore
}
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
socket.close();
}
});
}