github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/prepare/02_http/proxy/server/server.go (about) 1 package main 2 3 import ( 4 "crypto/tls" 5 "fmt" 6 "io" 7 "log" 8 "net" 9 "net/http" 10 "net/http/httputil" 11 "time" 12 ) 13 14 /* 15 #!/usr/bin/env bash 16 case `uname -s` in 17 Linux*) sslConfig=/etc/ssl/openssl.cnf;; 18 Darwin*) sslConfig=/System/Library/OpenSSL/openssl.cnf;; 19 esac 20 openssl req \ 21 -newkey rsa:2048 \ 22 -x509 \ 23 -nodes \ 24 -keyout server.key \ 25 -new \ 26 -out server.pem \ 27 -subj /CN=localhost \ 28 -reqexts SAN \ 29 -extensions SAN \ 30 -config <(cat $sslConfig \ 31 <(printf '[SAN]\nsubjectAltName=DNS:localhost')) \ 32 -sha256 \ 33 -days 3650 34 签名证书脚本 35 36 1. 生成 512位的私钥 37 openssl genrsa -out server.key 512 38 39 2. 身份证申请(CSR)文件生成 scr(certificate signing request) 客户端必须可以通过域名 locahost 访问到这个 HTTPS 服务 40 openssl req -nodes -key server.key -subj '/CN=localhost' -out server.csr 41 42 3. 签署身份证, 自签名(self-sign) 用自己的私钥 签署自己的CSR 43 openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt 44 45 k8s中 apiServer 签署 46 会用一个CA的私钥来签署一个CSR, 47 48 */ 49 /* 50 http代理 51 */ 52 53 func main() { 54 pemPath := "server.crt" // 证书路径 55 keyPath := "server.key" // 私钥路径 56 proto := "https" // 协议 http 或https 57 58 server := &http.Server{ 59 Addr: ":443", 60 Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 61 if r.Method == http.MethodConnect { 62 //支持https websocket deng ... tcp 63 handleTunneling(w, r) 64 } else { 65 //直接http代理 66 handleHTTP(w, r) 67 } 68 }), 69 // 关闭http2 70 TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), 71 } 72 73 if proto == "http" { 74 log.Fatal(server.ListenAndServe()) 75 } else { 76 log.Fatal(server.ListenAndServeTLS(pemPath, keyPath)) 77 } 78 } 79 80 func handleTunneling(w http.ResponseWriter, r *http.Request) { 81 //设置超时防止大量超时导致服务器资源不大量占用 82 dest_conn, err := net.DialTimeout("tcp", r.Host, 10*time.Second) 83 if err != nil { 84 http.Error(w, err.Error(), http.StatusServiceUnavailable) 85 return 86 } 87 w.WriteHeader(http.StatusOK) 88 //类型转换,Hijacker 接口容许接管连接.在此之后调用者有责任管理这个链接 89 hijacker, ok := w.(http.Hijacker) 90 if !ok { 91 http.Error(w, "Hijacking not supported", http.StatusInternalServerError) 92 return 93 } 94 //接管连接 95 client_conn, _, err := hijacker.Hijack() 96 if err != nil { 97 http.Error(w, err.Error(), http.StatusServiceUnavailable) 98 } 99 reqDump, err := httputil.DumpRequest(r, true) 100 fmt.Printf("req-> %q", reqDump) 101 102 go transfer(dest_conn, client_conn) 103 go transfer(client_conn, dest_conn) 104 } 105 106 //转发连接的数据 107 func transfer(destination io.WriteCloser, source io.ReadCloser) { 108 defer destination.Close() 109 defer source.Close() 110 io.Copy(destination, source) 111 } 112 113 func handleHTTP(w http.ResponseWriter, req *http.Request) { 114 //roudtrip 传递发送的请求返回响应的结果 115 resp, err := http.DefaultTransport.RoundTrip(req) 116 if err != nil { 117 http.Error(w, err.Error(), http.StatusServiceUnavailable) 118 return 119 } 120 defer resp.Body.Close() 121 //把目标服务器的响应header复制 122 copyHeader(w.Header(), resp.Header) 123 w.WriteHeader(resp.StatusCode) 124 io.Copy(w, resp.Body) 125 } 126 127 //复制响应头 128 func copyHeader(dst, src http.Header) { 129 for k, vv := range src { 130 for _, v := range vv { 131 dst.Add(k, v) 132 } 133 } 134 }