blob: 75e38a3558bd0ea7cf2fa5a17554bd1f86b45ba5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2017 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.core;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
/**
* ChannelTCP is a IChannel implementation that works on top of TCP sockets as a transport.
*/
public class ChannelTCP extends StreamChannel {
private volatile Socket socket;
private int timeout;
private InputStream inp;
private OutputStream out;
private boolean started;
private boolean closed;
private static SSLContext ssl_context;
public static void setSSLContext(SSLContext ssl_context) {
ChannelTCP.ssl_context = ssl_context;
}
/**
* Main constructor of ChannelTCP
* @param remote_peer Remote Peer to which we want to connect
* @param host Hostname or IP Address of the Remote Peer
* @param port Port of the Remote Peer
* @param ssl true if the socket needs to be an SSL socket, false otherwise
*/
public ChannelTCP(IPeer remote_peer, final String host, final int port, final boolean ssl) {
super(remote_peer);
socket = new Socket();
Protocol.invokeLater(new Runnable() {
public void run() {
Thread thread = new Thread() {
public void run() {
try {
socket.connect(new InetSocketAddress(host, port), timeout);
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
if (ssl) {
if (ssl_context == null) throw new Exception("SSL context is not set");
socket = ssl_context.getSocketFactory().createSocket(socket, host, port, true);
((SSLSocket)socket).startHandshake();
}
inp = new BufferedInputStream(socket.getInputStream());
out = new BufferedOutputStream(socket.getOutputStream());
onSocketConnected(null);
}
catch (Exception x) {
onSocketConnected(x);
}
}
};
thread.setName("TCF Socket Connect");
thread.start();
}
});
}
/**
* Constructs a non-secured ChannelTCP, i.e: the socket underlying the ChannelTCP is non-SSL socket
* @param remote_peer Remote Peer to which we want to connect
* @param host Hostname or IP Address of the Remote Peer
* @param port Port of the Remote Peer
*/
public ChannelTCP(IPeer remote_peer, String host, int port) {
this(remote_peer, host, port, false);
}
/**
* Construct a non-secured ChannelTCP, i.e: the socket underlying the ChannelTCP is non-SSL socket.
* @param local_peer local peer
* @param remote_peer Remote Peer to which we want to connect
* @param socket socket for the underlying tcp connection
* @throws IOException
*/
public ChannelTCP(IPeer local_peer, IPeer remote_peer, Socket socket) throws IOException {
super(local_peer, remote_peer);
this.socket = socket;
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
inp = new BufferedInputStream(socket.getInputStream());
out = new BufferedOutputStream(socket.getOutputStream());
onSocketConnected(null);
}
public void setConnectTimeout(int timeout) {
this.timeout = timeout;
}
public void setReuseAddress(boolean on) throws SocketException {
socket.setReuseAddress(on);
}
public void setReceiveBufferSize(int size) throws SocketException {
socket.setReceiveBufferSize(size);
}
public void setSendBufferSize(int size) throws SocketException {
socket.setSendBufferSize(size);
}
public void setSoLinger(boolean on, int linger) throws SocketException {
socket.setSoLinger(on, linger);
}
public void setTrafficClass(int tc) throws SocketException {
socket.setTrafficClass(tc);
}
public void setPerformancePreferences(int connection_time, int latency, int bandwidth) {
socket.setPerformancePreferences(connection_time, latency, bandwidth);
}
/**
* @param x exception object, or null if there was no exception creating the ChannelTCP object
*/
private void onSocketConnected(final Throwable x) {
Protocol.invokeLater(new Runnable() {
public void run() {
if (x != null) {
terminate(x);
closed = true;
}
if (closed) {
try {
if (socket != null) {
socket.close();
if (out != null) out.close();
if (inp != null) inp.close();
}
}
catch (IOException y) {
Protocol.log("Cannot close socket", y);
}
}
else {
started = true;
start();
}
}
});
}
@Override
protected final int get() throws IOException {
try {
if (closed) return -1;
return inp.read();
}
catch (IOException x) {
if (closed) return -1;
throw x;
}
}
@Override
protected final int get(byte[] buf) throws IOException {
try {
if (closed) return -1;
return inp.read(buf);
}
catch (IOException x) {
if (closed) return -1;
throw x;
}
}
@Override
protected final void put(int b) throws IOException {
assert b >= 0 && b <= 0xff;
if (closed) return;
out.write(b);
}
@Override
protected final void put(byte[] buf) throws IOException {
if (closed) return;
out.write(buf);
}
@Override
protected final void put(byte[] buf, int pos, int len) throws IOException {
if (closed) return;
out.write(buf, pos, len);
}
@Override
protected final void flush() throws IOException {
if (closed) return;
out.flush();
}
@Override
protected void stop() throws IOException {
closed = true;
if (started) {
socket.close();
/*
* We should not write anything to the stream here, just close it.
* So, don't call out.close(), because it calls out.flush().
* The socket output stream is already closed by socket.close().
*/
inp.close();
}
}
}