package org.nutz.http;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.Socket;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import org.nutz.http.sender.GetSender;
import org.nutz.http.sender.PostSender;
import org.nutz.lang.stream.NullInputStream;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * @author zozoh(zozohtnt@gmail.com)
 * @author wendal(wendal1985@gmail.com)
 * 
 */
public abstract class Sender {

    /**
     * 默认连接超时, 30秒
     */
    public static int Default_Conn_Timeout = 30 * 1000;
    /**
     * 默认读取超时, 10分钟
     */
    public static int Default_Read_Timeout = 10 * 60 * 1000;
    
    private static final Log log = Logs.get();

    public static Sender create(String url) {
        return create(Request.get(url));
    }

    public static Sender create(String url, int timeout) {
        return create(Request.get(url)).setTimeout(timeout);
    }

    public static Sender create(Request request) {
        return request.isGet() || request.isDelete() ? new GetSender(request) : new PostSender(request);
    }

    public static Sender create(Request request, int timeout) {
        Sender sender = request.isGet() || request.isDelete() ? new GetSender(request) : new PostSender(request);
        return sender.setTimeout(timeout);
    }

    protected Request request;

    protected int timeout;

    protected HttpURLConnection conn;

    protected Sender(Request request) {
        this.request = request;
    }

    public abstract Response send() throws HttpException;

    protected Response createResponse(Map<String, String> reHeaders) throws IOException {
        Response rep = null;
        if (reHeaders != null) {
            rep = new Response(conn, reHeaders);
            if (rep.isOK()) {
                InputStream is1 = conn.getInputStream();
                InputStream is2 = null;
                String encoding = conn.getContentEncoding();
                // 如果采用了压缩,则需要处理否则都是乱码
                if (encoding != null && encoding.contains("gzip")) {
                    is2 = new GZIPInputStream(is1);
                } else if (encoding != null && encoding.contains("deflate")) {
                    is2 = new InflaterInputStream(is1, new Inflater(true));
                } else {
                    is2 = is1;
                }

                BufferedInputStream is = new BufferedInputStream(is2);
                rep.setStream(is);
            }

            else {
                try {
                    rep.setStream(conn.getInputStream());
                }
                catch (IOException e) {
                    rep.setStream(new NullInputStream());
                }
            }
        }
        return rep;
    }

    protected Map<String, String> getResponseHeader() throws IOException {
        if (conn.getResponseCode() < 0)
            throw new IOException("Network error!! resp code < 0");
        Map<String, String> reHeaders = new HashMap<String, String>();
        for (Entry<String, List<String>> en : conn.getHeaderFields().entrySet()) {
            List<String> val = en.getValue();
            if (null != val && val.size() > 0)
                reHeaders.put(en.getKey(), en.getValue().get(0));
        }
        return reHeaders;
    }

    protected void setupDoInputOutputFlag() {
        conn.setDoInput(true);
        conn.setDoOutput(true);
    }

    protected void openConnection() throws IOException {
        ProxySwitcher proxySwitcher = Http.proxySwitcher;
        if (proxySwitcher != null) {
            try {
                Proxy proxy = proxySwitcher.getProxy(request);
                if (proxy != null) {

                	if (Http.autoSwitch) {
                		Socket socket = null;
                		try {
                			socket = new Socket();
                			socket.connect(proxy.address(), 5*1000);
                		} finally {
                			if (socket != null)
                				socket.close();
                		}
                	}
                    
                    conn = (HttpURLConnection) request.getUrl().openConnection(proxy);
                    conn.setConnectTimeout(Default_Conn_Timeout);
                    if (timeout > 0)
                        conn.setReadTimeout(timeout);
                    else
                        conn.setReadTimeout(Default_Read_Timeout);
                    return;
                }
            }
            catch (IOException e) {
                if (!Http.autoSwitch) {
                    throw e;
                }
            	log.info("Test proxy FAIl, fallback to direct connection", e);
            }
        }
        conn = (HttpURLConnection) request.getUrl().openConnection();
        conn.setConnectTimeout(Default_Conn_Timeout);
        conn.setRequestMethod(request.getMethod().name());
        if (timeout > 0)
            conn.setReadTimeout(timeout);
        else
            conn.setReadTimeout(Default_Read_Timeout);
    }

    protected void setupRequestHeader() {
        URL url = request.getUrl();
        String host = url.getHost();
        if (url.getPort() > 0 && url.getPort() != 80)
            host += ":" + url.getPort();
        conn.setRequestProperty("Host", host);
        Header header = request.getHeader();
        if (null != header)
            for (Entry<String, String> entry : header.getAll())
                conn.addRequestProperty(entry.getKey(), entry.getValue());
    }

    public Sender setTimeout(int timeout) {
        this.timeout = timeout;
        return this;
    }

    public int getTimeout() {
        return timeout;
    }

}

@@ -106,32 +106,62 @@
 o.InputStream;%0D%0A
+import java.io.OutputStream;%0D%0A
 import java.net.
@@ -4284,24 +4284,159 @@
 , 5*1000);%0D%0A
+                %09%09%09OutputStream out = socket.getOutputStream();%0D%0A                %09%09%09out.write('%5Cn');%0D%0A                %09%09%09out.flush();%0D%0A
             
@@ -4588,24 +4588,66 @@
             
+log.debug(%22connect via proxy : %22 + proxy);
 %0D%0A          

测试proxy是否可用的时候,需要判断