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  }