package com.westudio.java.proxy;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.westudio.java.socket.pool.HostInfo;
import com.westudio.java.socket.pool.SocketConnection;
import com.westudio.java.socket.pool.SocketPool;
import com.westudio.java.util.Numbers;
import com.westudio.java.util.Streams;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: tonyhe
 * Date: 14-5-26
 * Time: 下午6:41
 * To change this template use File | Settings | File Templates.
 */
public class RequestHandler implements HttpHandler {

    private SocketPool socketPool = new SocketPool(new GenericKeyedObjectPoolConfig());

    private static HashSet<String> SKIP_HEADERS = new HashSet<>(Arrays.asList(
            "ACCESS-CONTROL-REQUEST-METHODS",
            "ACCESS-CONTROL-REQUEST-HEADERS",
            "CONNECTION",
            "CONTENT-LENGTH",
            "CONTENT-TRANSFER-ENCODING",
            "KEEP-ALIVE",
            "TRAILER",
            "TRANSFER-ENCODING",
            "UPGRADE",
            "VIA",
            "X-FORWARDED-FOR",
            "X-FORWARDED-PROTO",
            "X-PKCS7-CERTIFICATES-BASE64",
            "DNT"
    ));
    public RequestHandler() {
    }

    private static void write(OutputStream ops, String str) throws IOException {
        ops.write(str.getBytes(StandardCharsets.ISO_8859_1));
    }

    private static void writeln(OutputStream ops) throws IOException {
        ops.write('\r');
        ops.write('\n');
    }

    private static void writeHeader(OutputStream ops, String key, String value) throws IOException {
        write(ops, key);
        ops.write(':');
        ops.write(' ');
        write(ops, value);
        writeln(ops);
    }

    @Override
    public void handle(HttpExchange httpExchange) throws IOException {

        URI uri = httpExchange.getRequestURI();
        String schema = uri.getScheme();
        String host = uri.getHost();
        int port = uri.getPort();
        port = port < 0 ? ("https".equals(schema) ? 443 : 80) : port;

        String query = uri.getQuery();
        String path = uri.getPath();
        String method = httpExchange.getRequestMethod();

        SocketConnection socketConnection;
        try {
            socketConnection = socketPool.getResource(new HostInfo(host, port));
            // Add request header
            BufferedOutputStream outputStream = new BufferedOutputStream(socketConnection.getSocket().getOutputStream());
            write(outputStream, method);
            outputStream.write(' ');
            write(outputStream, path);
            write(outputStream, " HTTP/1.1");
            writeln(outputStream);

            boolean withHost = false;
            int contentLength = -1;
            for (Map.Entry<String, List<String>> entry : httpExchange.getRequestHeaders().entrySet()) {
                String key = entry.getKey();

                if (key.equalsIgnoreCase("Host")) {
                    withHost = true;
                }

                if (SKIP_HEADERS.contains(key.toUpperCase())) {
                    if (key.equalsIgnoreCase("Content-Length")) {
                        List<String> value = entry.getValue();
                        if (!value.isEmpty()) {
                            contentLength = Numbers.parseInt(value.get(0));
                        }
                    }
                    continue;
                }

                for (String value : entry.getValue()) {
                    if (key.equalsIgnoreCase("Destination")) {
                        if ("https".equals(schema) && value.startsWith("https")) {
                            value = "http:" + value.substring(6);
                        }
                    }

                    writeHeader(outputStream, key, value);
                }
            }

            if (!withHost) {
                writeHeader(outputStream, "Host", host + ":" + port);
            }
            writeHeader(outputStream, "Connection", "Keep-Alive");

            if (contentLength > 0) {
                writeHeader(outputStream, "Content-Length", "" + contentLength);
                writeln(outputStream);
                // Simply pipe the input stream to the output stream
                Streams.pipe(httpExchange.getRequestBody(), outputStream);
            } else if (contentLength == 0) {
                writeHeader(outputStream, "Content-Length", "0");
                writeln(outputStream);
            } else if ("GET".equals(method) || "HEAD".equals(method)) {
                writeln(outputStream);
            } else {
                //TODO:ADD REQUEST BODY CHUNKED
            }
        } catch (IOException e) {

        }
    }
}

@@ -472,24 +472,52 @@
 OException;%0A
+import java.io.InputStream;%0A
 import java.
@@ -1510,24 +1510,147 @@
 NT%22%0A    ));%0A
+    private static final int RECV_MAX_CHUNK_SIZE = 4095;%0A    private static final String HEX_DIGITS = %220123456789ABCDEF%22;%0A%0A
     public R
@@ -5067,51 +5067,1058 @@
   //
-TODO:ADD REQUEST BODY CHUNKED%0A            %7D
+ Handle chunked transfer encoding%0A                writeHeader(outputStream, %22Transfer-Encoding%22, %22chunked%22);%0A                InputStream is = httpExchange.getRequestBody();%0A                byte%5B%5D buffer = new byte%5BRECV_MAX_CHUNK_SIZE%5D;%0A                buffer%5B3%5D = '%5Cr';%0A                buffer%5B4%5D = '%5Cn';%0A                int bytesRead;%0A                while ((bytesRead = is.read(buffer, 5, RECV_MAX_CHUNK_SIZE)) %3E 0) %7B%0A                    buffer%5B0%5D = (byte)HEX_DIGITS.charAt(bytesRead / 256);%0A                    buffer%5B1%5D = (byte)HEX_DIGITS.charAt(bytesRead / 16 %25 16);%0A                    buffer%5B2%5D = (byte)HEX_DIGITS.charAt(bytesRead %25 16);%0A                    buffer%5BbytesRead + 5%5D = '%5Cr';%0A                    buffer%5BbytesRead + 6%5D = '%5Cn';%0A                    outputStream.write(buffer, 0, bytesRead + 7);%0A                %7D%0A                // Add the last block chunk of size 0%0A                outputStream.write('0');%0A                writeln(outputStream);%0A                writeln(outputStream);%0A            %7D%0A            outputStream.flush();%0A%0A
 %0A   

handle the chunked transfer encoding......