// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// HTTP reverse proxy handler

package main

import (
	"io"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
    "flag"
    "compress/gzip"
)

var rawurl = flag.String("url", "https://trade.lmaxtrader.com", "lmax trade url")
var host  =  flag.String("http", ":8088", "sever port")

/*
func setNoDelayAddress(conn net.Conn) {
	file, _ := conn.(*net.TCPConn).File()
	fd := file.Fd()
	syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.TCP_NODELAY, 1);
}
*/

func main() {
    flag.Parse()
    myHandler, err := NewHostReverseProxy(*rawurl)
    if err != nil {
        log.Fatal(err)
    }
    s := &http.Server{
		Addr : *host,
        Handler: myHandler,
    }
	//创建Listen 
    log.Fatal(s.ListenAndServeTLS("certs/server.pem", "certs/server.key"))
}

func NewHostReverseProxy(rawurl string) (*ReverseProxy, error) {
    u, err := url.Parse(rawurl)
    if err != nil {
        return nil, err
    }
    proxy := NewSingleHostReverseProxy(u)
    proxy.FlushInterval = time.Second
    return proxy, nil
}

// onExitFlushLoop is a callback set by tests to detect the state of the
// flushLoop() goroutine.
var onExitFlushLoop func()

// ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the
// client.
type ReverseProxy struct {
	// Director must be a function which modifies
	// the request into a new request to be sent
	// using Transport. Its response is then copied
	// back to the original client unmodified.
	Director func(*http.Request)

	// The transport used to perform proxy requests.
	// If nil, http.DefaultTransport is used.
	Transport http.RoundTripper

	// FlushInterval specifies the flush interval
	// to flush to the client while copying the
	// response body.
	// If zero, no periodic flushing is done.
	FlushInterval time.Duration
}

func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
	targetQuery := target.RawQuery
	director := func(req *http.Request) {
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}
	return &ReverseProxy{Director: director}
}

func copyHeader(dst, src http.Header) {
	for k, vv := range src {
		for _, v := range vv {
			dst.Add(k, v)
		}
	}
}

// Hop-by-hop headers. These are removed when sent to the backend.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
var hopHeaders = []string{
	"Connection",
	"Keep-Alive",
	"Proxy-Authenticate",
	"Proxy-Authorization",
	"Te", // canonicalized version of "TE"
	"Trailers",
	"Transfer-Encoding",
	"Upgrade",

}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	transport := p.Transport
	if transport == nil {
		transport = http.DefaultTransport
	}

	outreq := new(http.Request)
	*outreq = *req // includes shallow copies of maps, but okay

	p.Director(outreq)
	outreq.Proto = "HTTP/1.1"
	outreq.ProtoMajor = 1
	outreq.ProtoMinor = 1
	outreq.Close = false

	// Remove hop-by-hop headers to the backend.  Especially
	// important is "Connection" because we want a persistent
	// connection, regardless of what the client sent to us.  This
	// is modifying the same underlying map from req (shallow
	// copied above) so we only copy it if necessary.
	copiedHeaders := false
	for _, h := range hopHeaders {
		if outreq.Header.Get(h) != "" {
			if !copiedHeaders {
				outreq.Header = make(http.Header)
				copyHeader(outreq.Header, req.Header)
				copiedHeaders = true
			}
			outreq.Header.Del(h)
		}
	}
	res, err := transport.RoundTrip(outreq)
	if err != nil {
		log.Printf("http: proxy error: %v", err)
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}
	defer res.Body.Close()
	copyHeader(rw.Header(), res.Header)
	//判断是否需要压缩
    if rw.Header().Get("Content-Encoding") != "gzip" {
        rw.Header().Set("Content-Encoding", "gzip")
	    rw.WriteHeader(res.StatusCode)
        gz , _ := gzip.NewWriterLevel(rw, gzip.BestCompression)
        defer gz.Close()
        p.copyResponse(gzipResponseWriter{Writer: gz, ResponseWriter: rw}, res.Body)
        return
    }
	rw.WriteHeader(res.StatusCode)
    p.copyResponse(rw, res.Body)
}

type gzipResponseWriter struct {
    io.Writer
    http.ResponseWriter
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
    return w.Writer.Write(b)
}

func (w gzipResponseWriter) Flush() {
    w.Writer.(*gzip.Writer).Flush()
    w.ResponseWriter.(http.Flusher).Flush()
}

func (p *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) (err error) {
    buf := make([]byte, 32*1024)
    for {
        nr, er := src.Read(buf)
        if nr > 0 {
            nw, ew := dst.Write(buf[0:nr])
            if ew != nil {
                err = ew
                break
            }
            if nr != nw {
                err = io.ErrShortWrite
                break
            }
            dst.(http.Flusher).Flush()
        }
        if er == io.EOF {
            break
        }
        if er != nil {
            err = er
            break
        }
    }
    return nil
}


type writeFlusher interface {
	io.Writer
	http.Flusher
}