1034 int c; |
1051 int c; |
1035 while ((c = r.read()) != -1) { |
1052 while ((c = r.read()) != -1) { |
1036 if (c == '\n') break; |
1053 if (c == '\n') break; |
1037 b.appendCodePoint(c); |
1054 b.appendCodePoint(c); |
1038 } |
1055 } |
|
1056 if (b.length() == 0) { |
|
1057 return ""; |
|
1058 } |
1039 if (b.codePointAt(b.length() -1) == '\r') { |
1059 if (b.codePointAt(b.length() -1) == '\r') { |
1040 b.delete(b.length() -1, b.length()); |
1060 b.delete(b.length() -1, b.length()); |
1041 } |
1061 } |
1042 return b.toString(); |
1062 return b.toString(); |
1043 } |
1063 } |
1044 |
1064 |
1045 @Override |
1065 @Override |
1046 public void run() { |
1066 public void run() { |
1047 Socket clientConnection = null; |
1067 Socket clientConnection = null; |
1048 try { |
1068 while (!stop) { |
1049 while (true) { |
1069 System.out.println("Tunnel: Waiting for client at: " + ss); |
1050 System.out.println("Tunnel: Waiting for client"); |
1070 final Socket previous = clientConnection; |
1051 Socket previous = clientConnection; |
1071 try { |
|
1072 clientConnection = ss.accept(); |
|
1073 } catch (IOException io) { |
1052 try { |
1074 try { |
1053 clientConnection = ss.accept(); |
1075 ss.close(); |
|
1076 } catch (IOException ex) { |
|
1077 if (DEBUG) { |
|
1078 ex.printStackTrace(System.out); |
|
1079 } |
|
1080 } |
|
1081 // log the reason that caused the server to stop accepting connections |
|
1082 if (!stop) { |
|
1083 System.err.println("Server will stop accepting connections due to an exception:"); |
|
1084 io.printStackTrace(); |
|
1085 } |
|
1086 break; |
|
1087 } finally { |
|
1088 // close the previous connection |
|
1089 if (previous != null) { |
|
1090 try { |
|
1091 previous.close(); |
|
1092 } catch (IOException e) { |
|
1093 // ignore |
|
1094 if (DEBUG) { |
|
1095 System.out.println("Ignoring exception that happened while closing " + |
|
1096 "an older connection:"); |
|
1097 e.printStackTrace(System.out); |
|
1098 } |
|
1099 } |
|
1100 } |
|
1101 } |
|
1102 System.out.println("Tunnel: Client accepted"); |
|
1103 try { |
|
1104 // We have only 1 client... process the current client |
|
1105 // request and wait until it has finished before |
|
1106 // accepting a new connection request. |
|
1107 processRequestAndWaitToComplete(clientConnection); |
|
1108 } catch (IOException ioe) { |
|
1109 // close the client connection |
|
1110 try { |
|
1111 clientConnection.close(); |
1054 } catch (IOException io) { |
1112 } catch (IOException io) { |
1055 if (DEBUG) io.printStackTrace(System.out); |
1113 // ignore |
1056 break; |
1114 if (DEBUG) { |
|
1115 System.out.println("Ignoring exception that happened during client" + |
|
1116 " connection close:"); |
|
1117 io.printStackTrace(System.out); |
|
1118 } |
1057 } finally { |
1119 } finally { |
1058 // close the previous connection |
1120 clientConnection = null; |
1059 if (previous != null) previous.close(); |
|
1060 } |
1121 } |
1061 System.out.println("Tunnel: Client accepted"); |
1122 } catch (Throwable t) { |
1062 Socket targetConnection = null; |
1123 // don't close the client connection for non-IOExceptions, instead |
1063 InputStream ccis = clientConnection.getInputStream(); |
1124 // just log it and move on to accept next connection |
1064 OutputStream ccos = clientConnection.getOutputStream(); |
1125 if (!stop) { |
1065 Writer w = new OutputStreamWriter( |
1126 t.printStackTrace(); |
1066 clientConnection.getOutputStream(), "UTF-8"); |
|
1067 PrintWriter pw = new PrintWriter(w); |
|
1068 System.out.println("Tunnel: Reading request line"); |
|
1069 String requestLine = readLine(ccis); |
|
1070 System.out.println("Tunnel: Request line: " + requestLine); |
|
1071 if (requestLine.startsWith("CONNECT ")) { |
|
1072 // We should probably check that the next word following |
|
1073 // CONNECT is the host:port of our HTTPS serverImpl. |
|
1074 // Some improvement for a followup! |
|
1075 |
|
1076 // Read all headers until we find the empty line that |
|
1077 // signals the end of all headers. |
|
1078 while(!requestLine.equals("")) { |
|
1079 System.out.println("Tunnel: Reading header: " |
|
1080 + (requestLine = readLine(ccis))); |
|
1081 } |
|
1082 |
|
1083 targetConnection = new Socket( |
|
1084 serverImpl.getAddress().getAddress(), |
|
1085 serverImpl.getAddress().getPort()); |
|
1086 |
|
1087 // Then send the 200 OK response to the client |
|
1088 System.out.println("Tunnel: Sending " |
|
1089 + "HTTP/1.1 200 OK\r\n\r\n"); |
|
1090 pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); |
|
1091 pw.flush(); |
|
1092 } else { |
|
1093 // This should not happen. If it does let our serverImpl |
|
1094 // deal with it. |
|
1095 throw new IOException("Tunnel: Unexpected status line: " |
|
1096 + requestLine); |
|
1097 } |
1127 } |
1098 |
1128 } |
1099 // Pipe the input stream of the client connection to the |
1129 } |
1100 // output stream of the target connection and conversely. |
1130 } |
1101 // Now the client and target will just talk to each other. |
1131 |
1102 System.out.println("Tunnel: Starting tunnel pipes"); |
1132 private void processRequestAndWaitToComplete(final Socket clientConnection) |
1103 Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+'); |
1133 throws IOException, InterruptedException { |
1104 Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-'); |
1134 final Socket targetConnection; |
1105 t1.start(); |
1135 InputStream ccis = clientConnection.getInputStream(); |
1106 t2.start(); |
1136 OutputStream ccos = clientConnection.getOutputStream(); |
1107 |
1137 Writer w = new OutputStreamWriter( |
1108 // We have only 1 client... wait until it has finished before |
1138 clientConnection.getOutputStream(), "UTF-8"); |
1109 // accepting a new connection request. |
1139 PrintWriter pw = new PrintWriter(w); |
1110 t1.join(); |
1140 System.out.println("Tunnel: Reading request line"); |
1111 t2.join(); |
1141 String requestLine = readLine(ccis); |
1112 } |
1142 System.out.println("Tunnel: Request line: " + requestLine); |
1113 } catch (Throwable ex) { |
1143 if (requestLine.startsWith("CONNECT ")) { |
1114 try { |
1144 // We should probably check that the next word following |
1115 ss.close(); |
1145 // CONNECT is the host:port of our HTTPS serverImpl. |
1116 } catch (IOException ex1) { |
1146 // Some improvement for a followup! |
1117 ex.addSuppressed(ex1); |
1147 |
1118 } |
1148 // Read all headers until we find the empty line that |
1119 ex.printStackTrace(System.err); |
1149 // signals the end of all headers. |
1120 } |
1150 while(!requestLine.equals("")) { |
1121 } |
1151 System.out.println("Tunnel: Reading header: " |
1122 |
1152 + (requestLine = readLine(ccis))); |
|
1153 } |
|
1154 |
|
1155 targetConnection = new Socket( |
|
1156 serverImpl.getAddress().getAddress(), |
|
1157 serverImpl.getAddress().getPort()); |
|
1158 |
|
1159 // Then send the 200 OK response to the client |
|
1160 System.out.println("Tunnel: Sending " |
|
1161 + "HTTP/1.1 200 OK\r\n\r\n"); |
|
1162 pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); |
|
1163 pw.flush(); |
|
1164 } else { |
|
1165 // This should not happen. If it does then consider it a |
|
1166 // client error and throw an IOException |
|
1167 System.out.println("Tunnel: Throwing an IOException due to unexpected" + |
|
1168 " request line: " + requestLine); |
|
1169 throw new IOException("Client request error - Unexpected request line"); |
|
1170 } |
|
1171 |
|
1172 // Pipe the input stream of the client connection to the |
|
1173 // output stream of the target connection and conversely. |
|
1174 // Now the client and target will just talk to each other. |
|
1175 System.out.println("Tunnel: Starting tunnel pipes"); |
|
1176 Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+'); |
|
1177 Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-'); |
|
1178 t1.start(); |
|
1179 t2.start(); |
|
1180 // wait for the request to complete |
|
1181 t1.join(); |
|
1182 t2.join(); |
|
1183 } |
1123 } |
1184 } |
1124 } |
1185 } |