I have the following code, i want to be able to restart the thread if an exception occurred while processing a request.
The following in the run method of a thread:
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK) {
// Is this html?
String contentType = httpConn
.getHeaderField(HEADER_CONTENTTYPE);
boolean htmlContent = (contentType != null && contentType
.startsWith(CONTENTTYPE_TEXTHTML));
InputStream input = s.openInputStream();
byte[] data = new byte[1000];
int len = 0;
int size = 0;
StringBuffer raw = new StringBuffer();
while (-1 != (len = input.read(data))) {
// Exit condition for the thread. An
// IOException
// is
// thrown because of the call to
// httpConn.close(),
// causing the thread to terminate.
if (_stop) {
httpConn.close();
s.close();
input.close();
}
raw.append(new String(data, 0, len));
size += len;
}
// raw.insert(0, "bytes received]\n");
// raw.insert(0, size);
// raw.insert(0, '[');
content = raw.toString();
if (htmlContent) {
content = prepareData(raw.toString());
}
input.close();
} else {
try{
httpConn.close();
}catch (Exception e) {
// TODO: handle exception
}
errorDialog(status+", status code");
retryFeed(getUrl(), "Network error. Retrying...");
}
s.close();
} else {
errorDialog("Sorry Insufficient Network Coverage.");
return;
}
} catch (IOCancelledException e) {
errorDialog(e.getMessage());
retryFeed(getUrl(), "Network error. Retrying...");
} catch (IOException e) {
errorDialog(e.getMessage());
retryFeed(getUrl(), "Network error. Retrying...");
}
What is the safest way to retry the connection if failed?
Thanks.
//New This is the Error thread. That check for errors in the connection... will this help? and is it the most efficient method? thanks..
/Error Thread - Thread to check errors/
private class ErrorThread extends Thread {
private static final int TIMEOUT = 3000; // EVERY 3 Seconds
private boolean hasException = false;
private String _theUrl;
/**
* Stops this thread from listening for messages
*/
private synchronized void stop()
{
hasException =false;
}
/**
* Listens for incoming messages until stop() is called
* #see #stop()
* #see java.lang.Runnable#run()
*/
public void run()
{
try
{
while (true) {
if((hasException==true))
{
// Synchronize here so that we don't end up creating a connection that is never closed.
errorDialog("Will Fetch new");
synchronized(this)
{
hasException=false;
if (!_connectionThread.isStarted()) {
fetchPage(_theUrl);
} else {
createNewFetch(_theUrl);
}
}
}
try {
//errorDialog("No exception.");
sleep(TIMEOUT);
} catch (InterruptedException e)
{
errorDialog("Exceptions"+e.toString()+e.getMessage());
System.exit(1);
//System.exit(0);/*Kill System*/
}
}
}
catch (Exception except)
{
}
}
public void setActive(boolean exception,String url)
{
this.hasException=exception;
this._theUrl=url;
}
}
If the connecrtion fails, typically, you want to close it, pause a small time, and retry. The purpose of the pause is to prevent your device from devoting excessive resources to trying to connect to a server that's having issues.
Related
I have to create a simple rotating proxy application where 100 requests get evenly distributed to 10 devices. I've got the following structure:
WebServer with a Java-SocketServer running. All Android devices are connected to this Socket-Server to be able to know which devices are currently online and for determining which device should be used for the next request.
10 Android devices in different networks. They are connected to the Socket Server and are waiting for requests that should be forwarded to the remote address and then sent back to the SocketServer.
In easy words: I basically have to create an application similar like Honeygain, Peer2Profit or IPRoyal Pawns so that I can later do requests like this:
//Use "-x" to set Proxy-IP and Proxy-Port
curl -x ANDROID_DEVICE_IP:PORT -L https://www.google.com
I managed to have an always running proxy service in an Android application. It basically looks like this and just forwards HTTP-Requests from Port 1440 to the desired remote address and then sends the response back to the original client. The Proxy basically works fine.
public class ProxyServerThread extends Thread {
public static void main(String[] args) {
(new ProxyServerThread()).run();
}
public ProxyServerThread() {
super("Server Thread");
}
#Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(1440)) {
Socket socket;
try {
while ((socket = serverSocket.accept()) != null) {
(new Handler(socket)).start();
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
return;
}
}
public static class Handler extends Thread {
public static final Pattern CONNECT_PATTERN = Pattern.compile("CONNECT (.+):(.+) HTTP/(1\\.[01])", Pattern.CASE_INSENSITIVE);
private final Socket clientSocket;
private boolean previousWasR = false;
public Handler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
#Override
public void run() {
try {
String request = readLine(clientSocket);
System.out.println(request);
Matcher matcher = CONNECT_PATTERN.matcher(request);
if (matcher.matches()) {
String header;
do {
header = readLine(clientSocket);
} while (!"".equals(header));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(clientSocket.getOutputStream(), "ISO-8859-1");
final Socket forwardSocket;
try {
forwardSocket = new Socket(matcher.group(1), Integer.parseInt(matcher.group(2)));
System.out.println(forwardSocket);
} catch (IOException | NumberFormatException e) {
e.printStackTrace(); // TODO: implement catch
outputStreamWriter.write("HTTP/" + matcher.group(3) + " 502 Bad Gateway\r\n");
outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
outputStreamWriter.write("\r\n");
outputStreamWriter.flush();
return;
}
try {
outputStreamWriter.write("HTTP/" + matcher.group(3) + " 200 Connection established\r\n");
outputStreamWriter.write("Proxy-agent: Simple/0.1\r\n");
outputStreamWriter.write("\r\n");
outputStreamWriter.flush();
Thread remoteToClient = new Thread() {
#Override
public void run() {
forwardData(forwardSocket, clientSocket);
}
};
remoteToClient.start();
try {
if (previousWasR) {
int read = clientSocket.getInputStream().read();
if (read != -1) {
if (read != '\n') {
forwardSocket.getOutputStream().write(read);
}
forwardData(clientSocket, forwardSocket);
} else {
if (!forwardSocket.isOutputShutdown()) {
forwardSocket.shutdownOutput();
}
if (!clientSocket.isInputShutdown()) {
clientSocket.shutdownInput();
}
}
} else {
forwardData(clientSocket, forwardSocket);
}
} finally {
try {
remoteToClient.join();
} catch (InterruptedException e) {
e.printStackTrace(); // TODO: implement catch
}
}
} finally {
forwardSocket.close();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
}
private static void forwardData(Socket inputSocket, Socket outputSocket) {
try {
InputStream inputStream = inputSocket.getInputStream();
try {
OutputStream outputStream = outputSocket.getOutputStream();
try {
byte[] buffer = new byte[4096];
int read;
do {
read = inputStream.read(buffer);
if (read > 0) {
outputStream.write(buffer, 0, read);
if (inputStream.available() < 1) {
outputStream.flush();
}
}
} while (read >= 0);
} finally {
if (!outputSocket.isOutputShutdown()) {
outputSocket.shutdownOutput();
}
}
} finally {
if (!inputSocket.isInputShutdown()) {
inputSocket.shutdownInput();
}
}
} catch (IOException e) {
e.printStackTrace(); // TODO: implement catch
}
}
private String readLine(Socket socket) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int next;
readerLoop:
while ((next = socket.getInputStream().read()) != -1) {
if (previousWasR && next == '\n') {
previousWasR = false;
continue;
}
previousWasR = false;
switch (next) {
case '\r':
previousWasR = true;
break readerLoop;
case '\n':
break readerLoop;
default:
byteArrayOutputStream.write(next);
break;
}
}
return byteArrayOutputStream.toString("ISO-8859-1");
}
}
}
Here comes the Problem:
Everything works fine but only on the local network. I cannot manage to get this to work without port forwarding. Since all devices are on their mobile cellular data I need a way to be able to connect to the device anyway.
How do the mentioned apps manage to connect to the devices?
I getting a message from Message class and putting it in a string ss, Now everytime i get the string HOME i'm sending a message dir, but it never sends the message
I'm not sure why but the first condition is never satisfied even though ss is HOME which is proof because tv.settext becomes HOME (I've tested this multiple times)
TextView tv = (TextView) findViewById(R.id.readField);
ss=(String) message.obj;
if(ss.equals("HOME")) {
Message msg = Message.obtain();
msg.obj = dir;
writeHandler.sendMessage(msg);
}
else {
tv.setText(ss);
}
Reading part of my code to understand the above code
/**
* Return data read from the socket, or a blank string.
*/
private String read() {
String s = "";
try {
// Check if there are bytes available
if (inStream.available() > 0) {
// Read bytes into a buffer
byte[] inBuffer = new byte[1024];
int bytesRead = inStream.read(inBuffer);
// Convert read bytes into a string
s = new String(inBuffer, "ASCII");
s = s.substring(0, bytesRead);
}
} catch (Exception e) {
Log.e(TAG, "Read failed!", e);
}
return s;
}
//Under Message class
//Message coding
private void sendToReadHandler(String s) {
Message msg = Message.obtain();
msg.obj = s;
readHandler.sendMessage(msg);
Log.i(TAG, s);
}
/**
* Send complete messages from the rx_buffer to the read handler.
*/
private void parseMessages() {
// Find the first delimiter in the buffer
int inx = rx_buffer.indexOf(DELIMITER);
// If there is none, exit
if (inx == -1)
return;
// Get the complete message
String s = rx_buffer.substring(0, inx);
// Remove the message from the buffer
rx_buffer = rx_buffer.substring(inx + 1);
// Send to read handler
sendToReadHandler(s);
// Look for more complete messages
parseMessages();
}
public void run() {
// Attempt to connect and exit the thread if it failed
try {
connect();
sendToReadHandler("CONNECTED");
} catch (Exception e) {
Log.e(TAG, "Failed to connect!", e);
sendToReadHandler("CONNECTION FAILED");
disconnect();
return;
}
// Loop continuously, reading data, until thread.interrupt() is called
while (!this.isInterrupted()) {
// Make sure things haven't gone wrong
if ((inStream == null) || (outStream == null)) {
Log.e(TAG, "Lost bluetooth connection!");
break;
}
// Read data and add it to the buffer
String s = read();
if (s.length() > 0)
rx_buffer += s;
// Look for complete messages
parseMessages();
}
// If thread is interrupted, close connections
disconnect();
sendToReadHandler("DISCONNECTED");
}
}
I am working on a java application in which I am facing a problem. When I send a file to a server and an exception is thrown, the file is not sent. How can I retry sending the file?
public void uploadtxtFile(String localFileFullName, String fileName, String hostDir)
throws Exception {
File file = new File(localFileFullName);
if (!(file.isDirectory())) {
if (file.exists()) {
FileInputStream input = null;
try {
input = new FileInputStream(new File(localFileFullName));
if (input != null) {
hostDir = hostDir.replaceAll("//", "/");
logger.info("uploading host dir : " + hostDir);
//new
// TestThread testThread=new TestThread(hostDir,input);
// Thread t=new Thread(testThread);
//
// try{
// t.start();
//
// }catch(Exception ex){
// logger.error("UPLOADE start thread create exception new:" + ex);
// }
// // new end
DBConnection.getFTPConnection().enterLocalPassiveMode();
// the below line exeption is come
boolean bool = DBConnection.getFTPConnection().storeFile(hostDir, input);
//input.close();//new comment
if (bool) {
logger.info("Success uploading file on host dir :"+hostDir);
} else {
logger.error("file not uploaded.");
}
} else {
logger.error("uploading file input null.");
}
}catch(CopyStreamException cs)
{ logger.error("Copy StreamExeption is come "+cs);
} catch(Exception ex)
{
logger.error("Error in connection ="+ex);//this is catch where I handle the exeption
}finally {
// boolean disconnect= DBConnection.disConnect();
input.close();
}
} else {
logger.info("uploading file is not exists.");
}
}
}
This is the code and I want to restart the file uploading but I don't have any idea. I tried it using the thread but the exception is thrown again. I also tried to use a while loop, but it loops infinitely and also shows the exception as well as another exception.
Below is the thread code that I use:
public void run() {
System.out.println("Enter Thread TestThread");
DBConnection.getFTPConnection().enterLocalPassiveMode();
// System.out.println("Error in DBConnection ");
//here server timeout error is get
boolean bool1=false;
boolean bool=true;
try {
bool = DBConnection.getFTPConnection().storeFile(hostDir1, input1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//disconnect();
try {
input1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (bool) {
System.out.println("File is Uploded");
} else {
while(bool!=true){
try {
DBConnection.getFTPConnection().enterLocalPassiveMode();
bool1=DBConnection.getFTPConnection().storeFile(hostDir1, input1);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//disconnect();
try {
input1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("file not uploaded."+bool1);
bool=bool1;
}
}
}
}
}
Can any one have a solution to how to upload the file to the server?
The exception is shown below:
Software caused connection abort: recv failed
Software caused connection abort: socket write error
org.apache.commons.net.io.CopyStreamException: IOException caught while copying.
Add a static class as below in a class from where you are calling the method which need to be retried:
static class RetryOnExceptionStrategy {
public static final int DEFAULT_RETRIES = 3;
public static final long DEFAULT_WAIT_TIME_IN_MILLI = 2000;
private int numberOfRetries;
private int numberOfTriesLeft;
private long timeToWait;
public RetryOnExceptionStrategy() {
this(DEFAULT_RETRIES, DEFAULT_WAIT_TIME_IN_MILLI);
}
public RetryOnExceptionStrategy(int numberOfRetries,
long timeToWait) {
this.numberOfRetries = numberOfRetries;
numberOfTriesLeft = numberOfRetries;
this.timeToWait = timeToWait;
}
/**
* #return true if there are tries left
*/
public boolean shouldRetry() {
return numberOfTriesLeft > 0;
}
public void errorOccured() throws Exception {
numberOfTriesLeft--;
if (!shouldRetry()) {
throw new Exception("Retry Failed: Total " + numberOfRetries
+ " attempts made at interval " + getTimeToWait()
+ "ms");
}
waitUntilNextTry();
}
public long getTimeToWait() {
return timeToWait;
}
private void waitUntilNextTry() {
try {
Thread.sleep(getTimeToWait());
} catch (InterruptedException ignored) {
}
}
}
Now wrap your method call as below in a while loop :
RetryOnExceptionStrategy errorStrategy=new RetryOnExceptionStrategy();
while(errorStrategy.shouldRetry()){
try{
//Method Call
}
catch(Exception excep){
errorStrategy.errorOccured();
}
}
Basically you are just wrapping you method call in while loop which will
keep returnig true till your retry count is reached to zero say you started with 3.
Every time an exception occurred, the exception is caught and a method is called
which will decrement your retryCount and method call is again executed with some delay.
A general way of working with such application is:
Create a class, say, UploadWorker which extends Callable as the wrapper. Make the wrapper return any error and detail information you need when it fails.
Create a ExecutorService (basically a thread pool) for this wrapper to run in threads.
Submit your UploadWorker instance and then you get a Future. Call get() on the future to wait in blocking way or simply wait some time for the result.
In case the get() returns you the error message, submit your worker again to the thread pool.
I'm trying to pipe some ByteBuffer in a thread (IO1) to another (IO2).
http://tutorials.jenkov.com/java-nio/pipe.html
private int bufferSize;
private boolean isRecording;
private Thread IO1;
private Thread IO2;
private ByteBuffer byteBuffer1;
private ByteBuffer byteBuffer2;
private Pipe pipe;
private Pipe.SinkChannel skChannel;
private Pipe.SourceChannel sourceChannel;
byteBuffer1 = ByteBuffer.allocateDirect(bufferSize);
byteBuffer2 = ByteBuffer.allocateDirect(bufferSize);
//An instance of Pipe is created
try
{
pipe = Pipe.open();
skChannel = pipe.sink();
sourceChannel = pipe.source();
IO1.start();
IO2.start();
}
catch (IOException e)
{
e.printStackTrace();
}
--
IO1 = new Thread(new Runnable()
{
public void run()
{
isRecording = true;
recorder.startRecording();
try
{
int read;
while (isRecording)
{
// byteBuffer1.clear();
read = recorder.read(byteBuffer1, bufferSize);
if (AudioRecord.ERROR_INVALID_OPERATION != read)
{
skChannel.write(byteBuffer1);
Log.v("========IO1 ", String.valueOf(read));
//This triggered almost 20 times/second
}
}
recorder.stop();
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
skChannel.write(byteBuffer1); and
Log.v("========IO1 ", String.valueOf(read));
is triggered almost 20 times/second, and this is the expected behavior, so far so good.
IO2 = new Thread(new Runnable()
{
public void run()
{
try
{
int read;
while ( (read =sourceChannel.read(byteBuffer2)) >0)
{
Log.v("========IO2 ", String.valueOf(read));
//this triggered only once
// To do Codec etc.
//............
}
}
catch (IOException e)
{
e.printStackTrace();
}
Log.v("========IO2 ", "END!!!!!"); //never triggered(this is fine)
}
});
However, Log.v("========IO2 ", String.valueOf(read)); is triggered only once, and I don't know why.
Can someone tell me how can I obtain the update of Thread IO1 in IO2?
Thanks.
You need to flip() the buffer before writing, and compact() it afterwards.
BUT: In a word, don't. Pipes between threads are basically pointless. Use a queue, or have the receiving thread read the sending thread's input directly.
If you must do this, the basic NIO copy loop goes like this:
while (in.read(buffer) > 0 || buffer.position() > 0) // or whatever your read API needs
{
buffer.flip();
out.write(buffer);
buffer.compact();
}
Due to a variety of reasons it became necessary to create our own proxy. Everything is working as it should through HTTP. As soon as we receive a CONNECT to tunnel through SSL is when everything goes wrong. What we do logically is take the CONNECT parse out the host and port so we know where we are sending future ssl requests and create a request to send back to the browser stating we have successfully made the ssl handshake like so:
HTTP/1.0 200 Connection established\r\nProxy-agent: test\r\n\r\n
What we expect to happen is that the browser once receiving this successful message will send us the next https request. However, instead we get sent another CONNECT request over and over.It is clear that is does not like the response we send back. The problem is that i'm not exactly sure why? Does the response back need to be sent back via an https socket? I just don't understand this process enough to move forward.
Here is my server class:
public class HttpServer extends Observable implements IWebServer, Runnable
{
int Port = -1;
int State = HttpState.IDLE;
ArrayList<WebTransactionEvent> History = new ArrayList<WebTransactionEvent>();
ArrayList<HttpService> myServices = new ArrayList<HttpService>();
SocketChannel myChannel = null;
boolean needResponse = false;
boolean shouldStop;
Logger logger = OpsToolsLogger.getLogger(HttpServer.class.getName());
Selector selector ;
static Hashtable<String, HttpServer> myInstances = new Hashtable<String, HttpServer>();
Hashtable<HttpTransaction, HttpService> myTaskTable = new Hashtable<HttpTransaction, HttpService>();
Vector<HttpTransaction> transactionQueue = new Vector<HttpTransaction>();
private HttpServer(){}
private HttpServer(int Port)
{
logger.log(Level.WARNING, "HttpServer: startup - listening to port: " + Port);
this.Port = Port;
shouldStop = false;
// Create the selector
try {
selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(Port));
this.registerSocket(serverChannel);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(this).start();
}
public static HttpServer getInstance(String port)
{
if( !myInstances.containsKey( port ) )
{
myInstances.put( port, new HttpServer(Integer.parseInt(port)));
}
return myInstances.get(port);
}
public int getState()
{
return State;
}
public void stop()
{
shouldStop = true;
}
public boolean needResponse()
{
return needResponse;
}
public HttpTransaction getNextTransaction()
{
if(transactionQueue.isEmpty())
{
return null;
}
//System.out.println("grabbing next trans");
HttpTransaction temp = transactionQueue.firstElement();
transactionQueue.remove(0);//pop trans from queue
return temp;
}
public void dropTransaction()
{
myTaskTable.clear();
needResponse = false;
}
public synchronized boolean respond(HttpTransaction transaction, IHttpResponse editedResponse, boolean closeConnection)
{
logger.log(Level.FINE, "HttpServer: responding ");
needResponse = false;
if(myTaskTable.isEmpty())
{
return false;
}
//see if there isn't a service object registered with that transaction
if(!myTaskTable.containsKey(transaction))
{
return false;
}
State = HttpState.SENDING_RESPONSE;
ManipulatedHttpTransaction myTrans = (ManipulatedHttpTransaction) transaction;
HttpResponse response = (HttpResponse) editedResponse;
myTrans.setManipulatedResponse( response );
HttpService serv = myTaskTable.get(transaction);
if(!serv.respond(myTrans.getManipulatedResponse(), closeConnection))
{
History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_ERROR ) );
return false;
}
myTaskTable.remove(transaction);
History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_COMPLETED ) );
needResponse = !myTaskTable.isEmpty();
return true;
}
public void registerSocket(ServerSocketChannel theSocket)
{
try {
theSocket.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try {
while (!shouldStop ) {
// Wait for an event
selector.select();
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator();
// Process each key
while (it.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)it.next();
// Remove it from the list to indicate that it is being processed
it.remove();
// Check if it's a connection request
if (selKey.isAcceptable()) {
// Get channel with connection request
ServerSocketChannel ssChannel = (ServerSocketChannel)selKey.channel();
SocketChannel theChannel = ssChannel.accept();
if(theChannel != null)
{
logger.log(Level.FINEST, "HttpServer: Connection established");
try
{
theChannel.configureBlocking(false);
}
catch(Exception e)
{
logger.log(Level.WARNING, "myChannel = null ( configureBlocking() )");
//bytesRead = -1;
}
myServices.add( new HttpService(this, theChannel ) );
needResponse = true;
}
//needResponse = !myTaskTable.isEmpty();
//System.out.println("need response: "+ needResponse);
}
}
}
} catch (IOException e) {
}
//shutdown
logger.log(Level.WARNING, "Server stopping - " + Port);
}
public ArrayList<WebTransactionEvent> getHistory()
{
return new ArrayList<WebTransactionEvent>(History);
}
public boolean switchServerToSSL()
{
//HttpService tempService = myTaskTable.get(PendingTransaction);
//tempService.useSSL = true;
return true;
}
/**
* Adds the transaction from browser to the transaction queue and also ties it to a service by adding it to myTasks map
* #param myTrans
* #param httpService
*/
public void addTransaction(ManipulatedHttpTransaction myTrans,
HttpService httpService) {
// TODO Auto-generated method stub
//ensure vector has room to add another transaction
if(transactionQueue.capacity() <= transactionQueue.size())
transactionQueue.ensureCapacity(transactionQueue.size() * 2);
transactionQueue.add(myTrans);//add transaction to queue
myTaskTable.put(myTrans, httpService);//tie the transaction toits service
// System.out.println("server notifying proxy: " + myTrans.getFullURL());
this.setChanged();
this.notifyObservers(myTrans);
}
}
Here is portion in proxy that handles a CONNECT:
if(tempTransaction.getOriginatingRequest().getMethod().contentEquals("CONNECT"))
{
/*tell the browser that the connection exists
*
* Each time you connect to an SSL-protected website, Burp generates a server certificate for that host, signed by the CA certificate
*
* The server certificates presented to the client (i.e. a web browser) are dynamically generated/signed by the proxy and contain most of the same fields as the original webserver certificate. The subject DN, serial number, validity dates, and extensions are preserved. However, the issuer DN is now set to the name of the proxy's self-signed
* certificate and the public/private keys of the proxy are used in creating the forged certificate. These forged certificates are cached (in memory) by the proxy, for better performance
*/
HttpResponse tunnelResponse = new HttpResponse("HTTP/1.0 200 Connection established\r\nProxy-agent: Ops Assistant\r\n\r\n");
tempTransaction.setResponse(tunnelResponse);
if(!finishResponse2(tempTransaction,tempTransaction.getResponse(), false));
{
//close the connection
}
myServer.switchServerToSSL();
}
Here is section sends request back to browser:
public boolean respond(IHttpResponse response, boolean closeConnection)
{
isCloseConnectionRequested = closeConnection;
try
{
if(useSSL)
{
ByteBuffer tmpBuffer = response.getData();
tmpBuffer.position(0);
myConnection.SecureWrite( tmpBuffer );
}
else
{
ByteBuffer tmpBuffer = response.getData();
tmpBuffer.position(0);
myConnection.Write(tmpBuffer);
}
if(closeConnection)
{
myChannel.close();
myChannel = null;
}
}
catch (Exception e)
{
isResponded = true;
return false;
}
isResponded = true;
return true;
}
Probably most important the socket class:
public class SocketConnection implements IConnection
{
public SocketChannel theSocketChannel;
public InetSocketAddress theRemoteAddress;
public int TimeoutThreshold;
private int TimeOutThreshold = 30;
private SSLEngine theSSLEngine;
private SSLContext theSSLContext;
private ByteBuffer inNetworkDataBuffer;
private ByteBuffer inAppDataBuffer;
private ByteBuffer outNetworkDataBuffer;
private ByteBuffer outAppDataBuffer;
//create outbound connection to host/port
public SocketConnection(String Host, int Port ) throws IOException
{
theRemoteAddress = new InetSocketAddress( Host, Port);
theSocketChannel = SocketChannel.open();
theSocketChannel.configureBlocking(false);
theSocketChannel.connect( theRemoteAddress );
theSocketChannel.finishConnect();
}
//use existing socket connection
public SocketConnection(SocketChannel existingChannel) throws IOException
{
theSocketChannel = existingChannel;
theSocketChannel.configureBlocking(false);
theRemoteAddress = new InetSocketAddress( existingChannel.socket().getInetAddress(), existingChannel.socket().getPort() );
}
public boolean setTimeOut(int newTimeOutThreshold)
{
TimeOutThreshold = newTimeOutThreshold;
return true;
}
public void waitForSocketToConnect() throws Exception
{
int i = 0;
while( !this.isConnected() )
{
this.finishConnect();
if(i>=3000)
{
throw new Exception();
}
i++;
try{Thread.sleep(10);}catch(Exception e){}
}
}
public boolean Write( ByteBuffer DataToSend )
{
try
{
//DataToSend.flip();
int numBytesWritten = theSocketChannel.write(DataToSend);
try
{
DataToSend.compact();
}
catch (ReadOnlyBufferException e)
{
DataToSend.rewind();
}
}
catch (IOException e)
{
// Connection may have been closed
}
return true;
}
public ByteBuffer Read()
{
ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);
try
{
ByteBuffer netBuffer = ByteBuffer.wrap(new byte[10000]);
// Clear the buffer and read bytes from socket
netBuffer.clear();
int numBytesRead = theSocketChannel.read(netBuffer);
if(numBytesRead == -1)
return null; //-1 means we done return null as the flag
netBuffer.flip();
ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + netBuffer.limit() ]);
ResponseBytes.position(0);
netBuffer.position(0);
tempBuffer.put(ResponseBytes);
tempBuffer.put(netBuffer);
netBuffer.flip();
ResponseBytes = tempBuffer;
}
catch (IOException e)
{
// Connection may have been closed
e = e;
return ByteBuffer.wrap( e.getMessage().getBytes() );
}
return (ByteBuffer) ResponseBytes.flip();
}
public boolean SecureWrite( ByteBuffer DataToSend )
{
boolean writeSuccess = true;
try
{
//if we don't have a SSLEngine make one
if(theSSLEngine==null)
{
setupSSL();
}
//Convert Data
outAppDataBuffer.clear();
outAppDataBuffer.put(DataToSend);
outAppDataBuffer.flip();
SSLEngineResult sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
outAppDataBuffer.compact();
//outNetworkDataBuffer.flip();
//int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
if(sslResult.getStatus() == SSLEngineResult.Status.OK)
{
if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
{
// Write bytes
outNetworkDataBuffer.flip();
int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
outNetworkDataBuffer.compact();
if(finishHandshake(sslResult))
{
DataToSend.rewind();
return SecureWrite(DataToSend);
}
else
{
return false;
}
}
else
{
// Write bytes
outNetworkDataBuffer.rewind();
Write(outNetworkDataBuffer);
}
}
else
{
}
}
catch(Exception e)
{
writeSuccess = false;
}
return writeSuccess;
}
public ByteBuffer SecureRead() throws ReadTimedOutException
{
int timeElapsed = 0;
ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);
try
{
//if we don't have a SSLEngine make one
if(theSSLEngine==null)
{
setupSSL();
}
int consumedCount = 0;
SSLEngineResult sslResult;
do
{
//inNetworkDataBuffer.clear();
inNetworkDataBuffer.put( Read() );
inNetworkDataBuffer.flip();
sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
consumedCount += sslResult.bytesConsumed();
inNetworkDataBuffer.compact();
if( sslResult.getStatus() == SSLEngineResult.Status.OK )
{
if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
{
if(finishHandshake(sslResult))
{
return SecureRead();
}
else
{
return ByteBuffer.allocateDirect(0);
}
}
else
{
timeElapsed = 0;
inAppDataBuffer.flip();
ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + inAppDataBuffer.limit() ]);
ResponseBytes.position(0);
inAppDataBuffer.position(0);
tempBuffer.put(ResponseBytes);
tempBuffer.put(inAppDataBuffer);
inAppDataBuffer.flip();
ResponseBytes = tempBuffer;
ResponseBytes.flip();
}
}
else
{
//the status wasn't ok
timeElapsed++;
}
}while(consumedCount < inNetworkDataBuffer.limit() && sslResult.getStatus() != SSLEngineResult.Status.OK);
}
catch (Exception e)
{
System.out.println(e.toString());
}
if(timeElapsed>=TimeOutThreshold)
{
throw new ReadTimedOutException();
}
return ResponseBytes;
}
public boolean Disconnect()
{
try
{
theSocketChannel.close();
}
catch(Exception e)
{
return false;
}
return true;
}
public boolean isClosed()
{
return !theSocketChannel.isOpen();
}
#Override
public String getHost()
{
return theRemoteAddress.getHostName();
}
#Override
public int getPort()
{
return theRemoteAddress.getPort();
}
public boolean isConnected()
{
return theSocketChannel.isConnected();
}
#Override
public boolean hasSecure()
{
return true;
}
public boolean finishConnect() throws Exception
{
return theSocketChannel.finishConnect();
}
private void setupSSL() throws NoSuchAlgorithmException, KeyManagementException
{
//create a new SSLEngine instance
System.setProperty( "javax.net.debug", "ssl");
TrustManager[] tm = new TrustManager[] { new NaiveTrustManager() };
SSLContext theSSLContext = SSLContext.getInstance ("TLS");
theSSLContext.init( new KeyManager[0], tm, new SecureRandom( ) );
theSSLEngine = theSSLContext.createSSLEngine( theRemoteAddress.getHostName(), theRemoteAddress.getPort());
theSSLEngine.setUseClientMode(true);
inNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
inAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);
outNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
outAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);
}
private boolean finishHandshake(SSLEngineResult sslResult)
{
boolean bFinished = false;
while(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED)
{
if( sslResult.getStatus() == SSLEngineResult.Status.CLOSED )
{
bFinished = false;
//break;
}
if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK)
{
Runnable task;
while ((task=theSSLEngine.getDelegatedTask()) != null)
{
task.run();
}
try
{
//outNetworkDataBuffer.flip();
sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
//outNetworkDataBuffer.compact();
}
catch (SSLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
{
try
{
outAppDataBuffer.flip();
sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
outAppDataBuffer.compact();
} catch (SSLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if((sslResult.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) || (outNetworkDataBuffer.position() > 0))
{
try
{
outNetworkDataBuffer.flip();
int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
outNetworkDataBuffer.compact();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
{
try
{
int numBytes;
//read data from the socket into inNetworkBuffer
inNetworkDataBuffer.flip();
sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
inNetworkDataBuffer.compact();
if(theSSLEngine.isInboundDone())
{
}
else
{
numBytes = theSocketChannel.read(inNetworkDataBuffer);
numBytes = numBytes;
}
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
}
return true;
}
}
Anyone have any tips on how to best establish this handshake with the browser?
Have you read the Internet draft? The CONNECT is received in plaintext. You form the upstream connection and return the 'HTTP/1.0 200 Connection established' response. After that the proxy isn't processing requests and responses, it is just copying bytes in both directions, whatever they may happen to be. Specifically, the proxy isn't concerned with SSL in any way shape or form.