24 import com.sun.net.httpserver.BasicAuthenticator; |
24 import com.sun.net.httpserver.BasicAuthenticator; |
25 import com.sun.net.httpserver.HttpServer; |
25 import com.sun.net.httpserver.HttpServer; |
26 import com.sun.net.httpserver.HttpsConfigurator; |
26 import com.sun.net.httpserver.HttpsConfigurator; |
27 import com.sun.net.httpserver.HttpsParameters; |
27 import com.sun.net.httpserver.HttpsParameters; |
28 import com.sun.net.httpserver.HttpsServer; |
28 import com.sun.net.httpserver.HttpsServer; |
|
29 |
|
30 import java.io.Closeable; |
29 import java.io.IOException; |
31 import java.io.IOException; |
30 import java.io.InputStream; |
32 import java.io.InputStream; |
31 import java.io.OutputStream; |
33 import java.io.OutputStream; |
32 import java.io.OutputStreamWriter; |
34 import java.io.OutputStreamWriter; |
33 import java.io.PrintWriter; |
35 import java.io.PrintWriter; |
1566 private synchronized Thread pipe(InputStream is, OutputStream os, char tag, CompletableFuture<Void> end) { |
1568 private synchronized Thread pipe(InputStream is, OutputStream os, char tag, CompletableFuture<Void> end) { |
1567 return new Thread("TunnelPipe("+tag+")") { |
1569 return new Thread("TunnelPipe("+tag+")") { |
1568 @Override |
1570 @Override |
1569 public void run() { |
1571 public void run() { |
1570 try { |
1572 try { |
|
1573 int c = 0; |
1571 try { |
1574 try { |
1572 int c; |
|
1573 while ((c = is.read()) != -1) { |
1575 while ((c = is.read()) != -1) { |
1574 os.write(c); |
1576 os.write(c); |
1575 os.flush(); |
1577 os.flush(); |
1576 // if DEBUG prints a + or a - for each transferred |
1578 // if DEBUG prints a + or a - for each transferred |
1577 // character. |
1579 // character. |
1578 if (DEBUG) System.out.print(tag); |
1580 if (DEBUG) System.out.print(tag); |
1579 } |
1581 } |
1580 is.close(); |
1582 is.close(); |
|
1583 } catch (IOException ex) { |
|
1584 if (DEBUG || !stopped && c > -1) |
|
1585 ex.printStackTrace(System.out); |
|
1586 end.completeExceptionally(ex); |
1581 } finally { |
1587 } finally { |
1582 os.close(); |
1588 try {os.close();} catch (Throwable t) {} |
1583 } |
1589 } |
1584 } catch (IOException ex) { |
|
1585 if (DEBUG) ex.printStackTrace(System.out); |
|
1586 } finally { |
1590 } finally { |
1587 end.complete(null); |
1591 end.complete(null); |
1588 } |
1592 } |
1589 } |
1593 } |
1590 }; |
1594 }; |
1630 } |
1634 } |
1631 |
1635 |
1632 @Override |
1636 @Override |
1633 public void run() { |
1637 public void run() { |
1634 Socket clientConnection = null; |
1638 Socket clientConnection = null; |
|
1639 Socket targetConnection = null; |
1635 try { |
1640 try { |
1636 while (!stopped) { |
1641 while (!stopped) { |
1637 System.out.println(now() + "Tunnel: Waiting for client"); |
1642 System.out.println(now() + "Tunnel: Waiting for client"); |
1638 Socket toClose; |
1643 Socket toClose; |
|
1644 targetConnection = clientConnection = null; |
1639 try { |
1645 try { |
1640 toClose = clientConnection = ss.accept(); |
1646 toClose = clientConnection = ss.accept(); |
1641 if (NO_LINGER) { |
1647 if (NO_LINGER) { |
1642 // can be useful to trigger "Connection reset by peer" |
1648 // can be useful to trigger "Connection reset by peer" |
1643 // errors on the client side. |
1649 // errors on the client side. |
1647 if (DEBUG || !stopped) io.printStackTrace(System.out); |
1653 if (DEBUG || !stopped) io.printStackTrace(System.out); |
1648 break; |
1654 break; |
1649 } |
1655 } |
1650 System.out.println(now() + "Tunnel: Client accepted"); |
1656 System.out.println(now() + "Tunnel: Client accepted"); |
1651 StringBuilder headers = new StringBuilder(); |
1657 StringBuilder headers = new StringBuilder(); |
1652 Socket targetConnection = null; |
|
1653 InputStream ccis = clientConnection.getInputStream(); |
1658 InputStream ccis = clientConnection.getInputStream(); |
1654 OutputStream ccos = clientConnection.getOutputStream(); |
1659 OutputStream ccos = clientConnection.getOutputStream(); |
1655 Writer w = new OutputStreamWriter( |
1660 Writer w = new OutputStreamWriter( |
1656 clientConnection.getOutputStream(), "UTF-8"); |
1661 clientConnection.getOutputStream(), "UTF-8"); |
1657 PrintWriter pw = new PrintWriter(w); |
1662 PrintWriter pw = new PrintWriter(w); |
1767 CompletableFuture<Void> end, end1, end2; |
1772 CompletableFuture<Void> end, end1, end2; |
1768 Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+', |
1773 Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+', |
1769 end1 = new CompletableFuture<>()); |
1774 end1 = new CompletableFuture<>()); |
1770 Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-', |
1775 Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-', |
1771 end2 = new CompletableFuture<>()); |
1776 end2 = new CompletableFuture<>()); |
1772 end = CompletableFuture.allOf(end1, end2); |
1777 var end11 = end1.whenComplete((r, t) -> exceptionally(end2, t)); |
|
1778 var end22 = end2.whenComplete((r, t) -> exceptionally(end1, t)); |
|
1779 end = CompletableFuture.allOf(end11, end22); |
|
1780 Socket tc = targetConnection; |
1773 end.whenComplete( |
1781 end.whenComplete( |
1774 (r,t) -> { |
1782 (r,t) -> { |
1775 try { toClose.close(); } catch (IOException x) { } |
1783 try { toClose.close(); } catch (IOException x) { } |
|
1784 try { tc.close(); } catch (IOException x) { } |
1776 finally {connectionCFs.remove(end);} |
1785 finally {connectionCFs.remove(end);} |
1777 }); |
1786 }); |
1778 connectionCFs.add(end); |
1787 connectionCFs.add(end); |
|
1788 targetConnection = clientConnection = null; |
1779 t1.start(); |
1789 t1.start(); |
1780 t2.start(); |
1790 t2.start(); |
1781 } |
1791 } |
1782 } catch (Throwable ex) { |
1792 } catch (Throwable ex) { |
1783 try { |
1793 close(clientConnection, ex); |
1784 ss.close(); |
1794 close(targetConnection, ex); |
1785 } catch (IOException ex1) { |
1795 close(ss, ex); |
1786 ex.addSuppressed(ex1); |
|
1787 } |
|
1788 ex.printStackTrace(System.err); |
1796 ex.printStackTrace(System.err); |
1789 } finally { |
1797 } finally { |
1790 System.out.println(now() + "Tunnel: exiting (stopped=" + stopped + ")"); |
1798 System.out.println(now() + "Tunnel: exiting (stopped=" + stopped + ")"); |
1791 connectionCFs.forEach(cf -> cf.complete(null)); |
1799 connectionCFs.forEach(cf -> cf.complete(null)); |
|
1800 } |
|
1801 } |
|
1802 |
|
1803 void exceptionally(CompletableFuture<?> cf, Throwable t) { |
|
1804 if (t != null) cf.completeExceptionally(t); |
|
1805 } |
|
1806 |
|
1807 void close(Closeable c, Throwable e) { |
|
1808 if (c == null) return; |
|
1809 try { |
|
1810 c.close(); |
|
1811 } catch (IOException x) { |
|
1812 e.addSuppressed(x); |
1792 } |
1813 } |
1793 } |
1814 } |
1794 } |
1815 } |
1795 |
1816 |
1796 /** |
1817 /** |