{"id":56,"date":"2026-02-20T21:29:36","date_gmt":"2026-02-20T13:29:36","guid":{"rendered":"http:\/\/43.138.147.217\/?p=56"},"modified":"2026-02-20T21:30:09","modified_gmt":"2026-02-20T13:30:09","slug":"go%e5%bc%80%e5%8f%91%e4%bc%81%e4%b8%9a%e7%ba%a7%e5%be%ae%e6%9c%8d%e5%8a%a1%e7%bd%91%e5%85%b3%ef%bc%886%ef%bc%89-reverseproxy","status":"publish","type":"post","link":"https:\/\/fableary.top\/?p=56","title":{"rendered":"Go\u5f00\u53d1\u4f01\u4e1a\u7ea7\u5fae\u670d\u52a1\u7f51\u5173\uff086\uff09ReverseProxy"},"content":{"rendered":"\n<p><strong>\u4e4b\u524d\u7684\u7b80\u6613\u7248 HTTP \u4ee3\u7406\u4e0d\u5177\u5907\u4e0b\u9762\u8fd9\u4e9b\u529f\u80fd\uff1a<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9519\u8bef\u56de\u8c03\u53ca\u9519\u8bef\u65e5\u5fd7\u5904\u7406<\/li>\n\n\n\n<li>\u66f4\u6539\u4ee3\u7406\u8fd4\u56de\u5185\u5bb9<\/li>\n\n\n\n<li>\u8d1f\u8f7d\u5747\u8861<\/li>\n\n\n\n<li>url \u91cd\u5199<\/li>\n\n\n\n<li>\u9650\u6d41\u3001\u7194\u65ad\u3001\u964d\u7ea7<\/li>\n\n\n\n<li>\u6570\u636e\u7edf\u8ba1<\/li>\n\n\n\n<li>\u6743\u9650\u9a8c\u8bc1<\/li>\n<\/ul>\n\n\n\n<p><strong>\u5927\u7eb2\uff1a<\/strong>\u7528 ReverseProxy \u5b9e\u73b0\u4e00\u4e2a HTTP \u4ee3\u7406<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ReverseProxy \u529f\u80fd\u70b9<\/li>\n\n\n\n<li>ReverseProxy \u793a\u4f8b<\/li>\n\n\n\n<li>ReverseProxy \u6e90\u7801\u5206\u6790<\/li>\n<\/ul>\n\n\n\n<p>\u62d3\u5c55 ReverseProxy \u529f\u80fd\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>4\u79cd\u8d1f\u8f7d\u8f6e\u8be2\u7c7b\u578b\u5b9e\u73b0\u4ee5\u53ca\u63a5\u53e3\u5c01\u88c5<\/li>\n\n\n\n<li>\u62d3\u5c55\u4e2d\u95f4\u4ef6\u652f\u6301\uff1a\u9650\u6d41\u3001\u7194\u65ad\u5b9e\u73b0\u3001\u6743\u9650\u3001\u6570\u636e\u7edf\u8ba1<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">ReverseProxy \u529f\u80fd\u70b9<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u66f4\u6539\u5185\u5bb9\u652f\u6301<\/li>\n\n\n\n<li>\u9519\u8bef\u4fe1\u606f\u56de\u8c03<\/li>\n\n\n\n<li>\u652f\u6301\u81ea\u5b9a\u4e49\u8d1f\u8f7d\u5747\u8861<\/li>\n\n\n\n<li>url \u91cd\u5199\u529f\u80fd<\/li>\n\n\n\n<li>\u8fde\u63a5\u6c60\u529f\u80fd<\/li>\n\n\n\n<li>\u652f\u6301 websocket \u670d\u52a1<\/li>\n\n\n\n<li>\u652f\u6301 https \u4ee3\u7406<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u7528 ReverseProxy \u5b9e\u73b0\u4e00\u4e2a HTTP \u4ee3\u7406<\/h3>\n\n\n\n<p>\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"log\"\n\t\"net\/http\"\n\t\"net\/http\/httputil\"\n\t\"net\/url\"\n)\n\nvar addr = \"127.0.0.1:2002\"\n\nfunc main() {\n\trs1 := \"http:\/\/127.0.0.1:2003\/base\"\n\turl1, err1 := url.Parse(rs1)\n\tif err1 != nil {\n\t\tlog.Println(err1)\n\t}\n\tproxy := httputil.NewSingleHostReverseProxy(url1)\n\tlog.Println(\"Starting httpserver at \" + addr)\n\tlog.Fatal(http.ListenAndServe(addr, proxy))\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ReverseProxy \u66f4\u6539\u5185\u5bb9\u652f\u6301<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\t\/\/ ModifyResponse is an optional function that modifies the\n\t\/\/ Response from the backend. It is called if the backend\n\t\/\/ returns a response at all, with any HTTP status code.\n\t\/\/ If the backend is unreachable, the optional ErrorHandler is\n\t\/\/ called without any call to ModifyResponse.\n\t\/\/\n\t\/\/ Hop-by-hop headers are removed from the response before\n\t\/\/ calling ModifyResponse. ModifyResponse may need to remove\n\t\/\/ additional headers to fit its deployment model, such as Alt-Svc.\n\t\/\/\n\t\/\/ If ModifyResponse returns an error, ErrorHandler is called\n\t\/\/ with its error value. If ErrorHandler is nil, its default\n\t\/\/ implementation is used.\n\tModifyResponse func(*http.Response) error<\/code><\/pre>\n\n\n\n<p><code>NewSingleHostReverseProxy<\/code> \u90e8\u5206\u5b9e\u73b0\u7684\u529f\u80fd\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {\n\tdirector := func(req *http.Request) {\n\t\trewriteRequestURL(req, target)\n\t}\n\treturn &amp;ReverseProxy{Director: director}\n}\n\nfunc rewriteRequestURL(req *http.Request, target *url.URL) {\u3001\n\t\/\/ http:127.0.0.1:2002\/dir?name=123\n\t\/\/ RawQuery: name=123\n\t\/\/ Scheme: http\n\t\/\/ Host: 127.0.0.1:2002\n\ttargetQuery := target.RawQuery\n\treq.URL.Scheme = target.Scheme\n\treq.URL.Host = target.Host\n\treq.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)\n\tif targetQuery == \"\" || req.URL.RawQuery == \"\" {\n\t\treq.URL.RawQuery = targetQuery + req.URL.RawQuery\n\t} else {\n\t\treq.URL.RawQuery = targetQuery + \"&amp;\" + req.URL.RawQuery\n\t}\n}<\/code><\/pre>\n\n\n\n<p>\u5f53\u6211\u4eec\u5e0c\u671b\u5bf9\u540e\u7aef\u8fd4\u56de\u7684\u54cd\u5e94\u5185\u5bb9\u8fdb\u884c\u4fee\u6539\u65f6\uff0c\u53ef\u4ee5\u5b9e\u73b0\u81ea\u5b9a\u4e49\u7684 modifyResponse \uff0c\u968f\u540e\u901a\u8fc7\u521b\u5efa\u81ea\u5b9a\u4e49\u7684 ReverseProxy \u800c\u4e0d\u662f\u4f7f\u7528 <code>NewSingleHostReverseProxy<\/code> \u6765\u5b9e\u73b0\u81ea\u5b9a\u4e49\u51fd\u6570\u7684\u6302\u8f7d\uff0c\u5b9e\u73b0\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"log\"\n\t\"net\/http\"\n\t\"net\/http\/httputil\"\n\t\"net\/url\"\n\t\"strconv\"\n)\n\nvar addr = \"127.0.0.1:2002\"\n\nfunc main() {\n\trs1 := \"http:\/\/127.0.0.1:2003\/base\"\n\turl1, err1 := url.Parse(rs1)\n\tif err1 != nil {\n\t\tlog.Println(err1)\n\t}\n\t\/\/ proxy := httputil.NewSingleHostReverseProxy(url1)\n\tproxy := &amp;httputil.ReverseProxy{\n\t\tRewrite: func(r *httputil.ProxyRequest) {\n\t\t\tr.SetURL(url1)\n\t\t\tr.Out.Host = r.In.Host\n\t\t},\n\t\tModifyResponse: modifyResponse,\n\t}\n\n\tlog.Println(\"Starting httpserver at \" + addr)\n\tlog.Fatal(http.ListenAndServe(addr, proxy))\n}\n\nfunc modifyResponse(resp *http.Response) error {\n\tif resp.StatusCode &lt; 200 || resp.StatusCode &gt;= 300 {\n\t\treturn nil\n\t}\n\n\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresp.Body.Close()\n\n\tmodifiedBody := append(&#091;]byte(\"Modified: \"), bodyBytes...)\n\tresp.Body = io.NopCloser(bytes.NewReader(modifiedBody))\n\tresp.ContentLength = int64(len(modifiedBody))\n\tresp.Header.Set(\"Content-Length\", strconv.Itoa(len(modifiedBody)))\n\n\treturn nil\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">ReverseProxy \u7279\u6b8a Header<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Connection<\/h4>\n\n\n\n<p>\u6807\u8bb0\u8bf7\u6c42\u53d1\u8d77\u65b9\u4e0e\u7b2c\u4e00\u4ee3\u7406\u7684\u72b6\u6001<br>\u51b3\u5b9a\u5f53\u524d\u4e8b\u52a1\u5b8c\u6210\u540e\uff0c\u662f\u5426\u4f1a\u5173\u95ed\u7f51\u7edc<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Connection: keep-alive \u4e0d\u5173\u95ed\u7f51\u7edc<\/li>\n\n\n\n<li>Connection: close \u5173\u95ed\u7f51\u7edc<\/li>\n\n\n\n<li>Connection: upgrade \u534f\u8bae\u5347\u7ea7<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">TE \u3001Trailer<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>TE \u662f request_header\uff0c\u8868\u793a\u5e0c\u671b\u4f7f\u7528\u7684\u4f20\u8f93\u7f16\u7801\u7c7b\u578b<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u5982\uff1aTE: trailers, deflate; q=0.5 \u8868\u793a\uff0c\u671f\u671b\u5728\u91c7\u7528\u5206\u5757\u4f20\u8f93\u7f16\u7801\u54cd\u5e94\u4e2d\u63a5\u6536\u6302\u8f7d\u5b57\u6bb5\uff0c zlib \u7f16\u7801\uff0c0.5\u4f18\u5148\u7ea7\u6392\u5e8f<\/p>\n<\/blockquote>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Trailer \u662f response_header\uff0c\u5141\u8bb8\u53d1\u9001\u65b9\u5728\u6d88\u606f\u540e\u9762\u6dfb\u52a0\u989d\u5916\u7684\u5143\u4fe1\u606f<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u5982\uff1aTrailer: Expires \u8868\u793a\uff0cExpires \u5c06\u51fa\u73b0\u5728\u5206\u5757\u4fe1\u606f\u7684\u7ed3\u5c3e<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">\u7b2c\u4e00\u4ee3\u7406\u9664\u53bb\u6807\u51c6\u7684\u9010\u6bb5\u4f20\u8f93\u5934\uff08hop-by-hop\uff09<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9010\u6bb5\u4f20\u8f93\u5934\u90fd\u9700\u8981\u5728 Connection \u5934\u4e2d\u5217\u51fa<\/li>\n\n\n\n<li>\u7b2c\u4e00\u4e2a\u4ee3\u7406\u77e5\u9053\u5fc5\u987b\u5904\u7406\u5b83\u4eec\u4e14\u4e0d\u8f6c\u53d1\u5b83\u4eec<\/li>\n\n\n\n<li>\u9010\u6bb5\u4f20\u8f93\u5934\uff1aKeep-Alive, Transfer-Encoding, TE, Connection, Trailer, Upgrade, Proxy-Authorization, Proxy-Authenticate<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">ReverseProxy \u8865\u5145\u77e5\u8bc6\u2014\u2014\u7279\u6b8a StatusCode<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>100 \u8868\u793a\u76ee\u524d\u4e00\u5207\u6b63\u5e38\uff0c\u5ba2\u6237\u7aef\u53ef\u4ee5\u7ee7\u7eed\u8bf7\u6c42<\/li>\n\n\n\n<li>101 \u8868\u793a\u670d\u52a1\u7aef\u53d1\u9001\u7ed9\u5ba2\u6237\u7aef\u5347\u7ea7\u534f\u8bae\u7684\u8bf7\u6c42<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">100-continue<\/h4>\n\n\n\n<p>\u5ba2\u6237\u7aef\u8981 POST \u7684\u6570\u636e\u5927\u4e8e 1024 \u5b57\u8282\u7684\u65f6\u5019\uff0c\u5ba2\u6237\u7aef\u4e0d\u4f1a\u76f4\u63a5\u53d1\u8d77 POST \u8bf7\u6c42\uff0c\u800c\u662f\u4f1a\u5206\u4e24\u6b65\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u53d1\u9001\u4e00\u4e2a\u8bf7\u6c42\uff0c\u5305\u542b\u4e00\u4e2a  Expect:100-continue\uff0c\u8be2\u95ee Server \u662f\u5426\u613f\u610f\u63a5\u6536\u6570\u636e<\/li>\n\n\n\n<li>\u63a5\u6536\u5230 Server \u8fd4\u56de\u7684 100-continue \u5e94\u7b54\u4ee5\u540e\uff0c\u8fd4\u56de 100 \u72b6\u6001\uff0c\u624d\u628a\u6570\u636e POST \u7ed9 Server<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">101-Switching Protocols<\/h4>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"463\" src=\"http:\/\/43.138.147.217\/wp-content\/uploads\/2026\/02\/image-7-1024x463.png\" alt=\"\" class=\"wp-image-58\" srcset=\"https:\/\/fableary.top\/wp-content\/uploads\/2026\/02\/image-7-1024x463.png 1024w, https:\/\/fableary.top\/wp-content\/uploads\/2026\/02\/image-7-300x136.png 300w, https:\/\/fableary.top\/wp-content\/uploads\/2026\/02\/image-7-768x347.png 768w, https:\/\/fableary.top\/wp-content\/uploads\/2026\/02\/image-7-1536x694.png 1536w, https:\/\/fableary.top\/wp-content\/uploads\/2026\/02\/image-7.png 1788w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">ReverseProxy \u6e90\u7801\u5206\u6790<\/h3>\n\n\n\n<p>\u6838\u5fc3\u7ed3\u6784\u4f53\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ReverseProxy \u662f\u4e00\u4e2a\u53cd\u5411\u4ee3\u7406\u5904\u7406\u5668\uff0c\u5c06\u8bf7\u6c42\u8f6c\u53d1\u7ed9\u540e\u7aef\u670d\u52a1\u5668\uff0c\u5e76\u5c06\u54cd\u5e94\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\u3002\ntype ReverseProxy struct {\n\t\/\/ Rewrite \u4fee\u6539\u8bf7\u6c42\uff0c\u751f\u6210\u53d1\u5f80\u540e\u7aef\u7684\u65b0\u8bf7\u6c42\u3002\u4e0d\u80fd\u4e0e Director \u540c\u65f6\u8bbe\u7f6e\u3002\n\t\/\/ \u63a8\u8350\u4f7f\u7528 Rewrite\uff0c\u5b83\u66f4\u53ef\u63a7\u3002\n\tRewrite func(*ProxyRequest)\n\n\t\/\/ Director \u4fee\u6539\u8bf7\u6c42\uff0c\u529f\u80fd\u540c Rewrite\uff0c\u662f\u65e7\u7248\u672c\u7684\u65b9\u5f0f\u3002\u4e0d\u80fd\u4e0e Rewrite \u540c\u65f6\u8bbe\u7f6e\u3002\n\tDirector func(*http.Request)\n\n\t\/\/ Transport \u6267\u884c\u4ee3\u7406\u8bf7\u6c42\uff0c\u9ed8\u8ba4\u4f7f\u7528 http.DefaultTransport\u3002\n\tTransport http.RoundTripper\n\n\t\/\/ FlushInterval \u63a7\u5236\u54cd\u5e94\u5237\u65b0\u7684\u95f4\u9694\u30020 \u8868\u793a\u4e0d\u4e3b\u52a8\u5237\u65b0\uff0c\u8d1f\u6570\u8868\u793a\u6bcf\u6b21\u5199\u5165\u540e\u7acb\u5373\u5237\u65b0\u3002\n\tFlushInterval time.Duration\n\n\t\/\/ ErrorLog \u8bb0\u5f55\u4ee3\u7406\u8fc7\u7a0b\u4e2d\u7684\u9519\u8bef\uff0c\u9ed8\u8ba4\u4f7f\u7528\u6807\u51c6\u65e5\u5fd7\u5305\u3002\n\tErrorLog *log.Logger\n\n\t\/\/ BufferPool \u7528\u4e8e\u590d\u7528\u7f13\u51b2\u533a\uff0c\u63d0\u5347\u6027\u80fd\u3002\n\tBufferPool BufferPool\n\n\t\/\/ ModifyResponse \u53ef\u4fee\u6539\u540e\u7aef\u8fd4\u56de\u7684\u54cd\u5e94\uff08\u4efb\u610f\u72b6\u6001\u7801\uff09\u3002\u82e5\u8fd4\u56de\u9519\u8bef\uff0c\u5219\u8c03\u7528 ErrorHandler\u3002\n\tModifyResponse func(*http.Response) error\n\n\t\/\/ ErrorHandler \u5904\u7406\u540e\u7aef\u4e0d\u53ef\u8fbe\u6216 ModifyResponse \u8fd4\u56de\u7684\u9519\u8bef\u3002\u9ed8\u8ba4\u8fd4\u56de 502 \u9519\u8bef\u3002\n\tErrorHandler func(http.ResponseWriter, *http.Request, error)\n}<\/code><\/pre>\n\n\n\n<p>ServeHTTP \u6838\u5fc3\u903b\u8f91\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ ServeHTTP \u5b9e\u73b0\u4e86 http.Handler \u63a5\u53e3\uff0c\u5904\u7406\u53cd\u5411\u4ee3\u7406\u7684\u6838\u5fc3\u903b\u8f91\u3002\nfunc (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {\n\t\/\/ \u786e\u5b9a\u4f7f\u7528\u7684\u4f20\u8f93\u5c42\uff08Transport\uff09\n\ttransport := p.Transport\n\tif transport == nil {\n\t\ttransport = http.DefaultTransport\n\t}\n\n\tctx := req.Context()\n\t\/\/ \u5904\u7406\u5ba2\u6237\u7aef\u8fde\u63a5\u5173\u95ed\u7684\u901a\u77e5\uff08\u4ec5\u5f53\u8bf7\u6c42\u4e0a\u4e0b\u6587\u6ca1\u6709\u53d6\u6d88\u4fe1\u53f7\u65f6\uff09\n\tif ctx.Done() != nil {\n\t\t\/\/ \u5982\u679c\u8bf7\u6c42\u4e0a\u4e0b\u6587\u5df2\u5305\u542b\u53d6\u6d88\u4fe1\u53f7\uff08\u5982\u8d85\u65f6\u3001\u4e3b\u52a8\u53d6\u6d88\uff09\uff0c\n\t\t\/\/ \u5219\u4e0d\u9700\u8981\u518d\u989d\u5916\u76d1\u542c CloseNotify\uff08\u5b83\u5df2\u88ab\u4e0a\u4e0b\u6587\u53d6\u4ee3\uff09\u3002\n\t\t\/\/ \u8fd9\u91cc\u4e0d\u505a\u4efb\u4f55\u64cd\u4f5c\u3002\n\t} else if cn, ok := rw.(http.CloseNotifier); ok {\n\t\t\/\/ \u5982\u679c\u54cd\u5e94\u5668\u652f\u6301 CloseNotifier\uff08\u5373\u53ef\u4ee5\u611f\u77e5\u5ba2\u6237\u7aef\u8fde\u63a5\u5173\u95ed\uff09\uff0c\n\t\t\/\/ \u5219\u521b\u5efa\u4e00\u4e2a\u53ef\u53d6\u6d88\u7684\u4e0a\u4e0b\u6587\uff0c\u5e76\u5728\u5355\u72ec\u7684 goroutine \u4e2d\u76d1\u542c\u5173\u95ed\u901a\u77e5\u3002\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithCancel(ctx)\n\t\tdefer cancel()\n\t\tnotifyChan := cn.CloseNotify()\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase &lt;-notifyChan:\n\t\t\t\tcancel() \/\/ \u5ba2\u6237\u7aef\u8fde\u63a5\u5173\u95ed\uff0c\u53d6\u6d88\u4e0a\u4e0b\u6587\n\t\t\tcase &lt;-ctx.Done():\n\t\t\t\t\/\/ \u4e0a\u4e0b\u6587\u5df2\u88ab\u53d6\u6d88\uff08\u4f8b\u5982\u8bf7\u6c42\u5904\u7406\u5b8c\u6210\uff09\uff0c\u9000\u51fa goroutine\n\t\t\t}\n\t\t}()\n\t}\n\n\t\/\/ \u514b\u9686\u539f\u59cb\u8bf7\u6c42\uff0c\u4ee5\u4fbf\u540e\u7eed\u4fee\u6539\uff08\u5982\u4fee\u6539 URL\u3001Header \u7b49\uff09\uff0c\u540c\u65f6\u7ed1\u5b9a\u65b0\u7684\u4e0a\u4e0b\u6587 ctx\u3002\n\toutreq := req.Clone(ctx)\n\tif req.ContentLength == 0 {\n\t\t\/\/ \u5982\u679c\u539f\u59cb\u8bf7\u6c42\u5185\u5bb9\u957f\u5ea6\u4e3a 0\uff0c\u5c06\u51fa\u7ad9\u8bf7\u6c42\u7684 Body \u8bbe\u4e3a nil\uff0c\n\t\t\/\/ \u907f\u514d http.Transport \u91cd\u8bd5\u65f6\u51fa\u73b0\u95ee\u9898\uff08\u53c2\u89c1 Issue 16036\uff09\u3002\n\t\toutreq.Body = nil\n\t}\n\tif outreq.Body != nil {\n\t\t\/\/ \u786e\u4fdd\u51fa\u7ad9\u8bf7\u6c42\u7684 Body \u6700\u7ec8\u88ab\u5173\u95ed\uff0c\u9632\u6b62 goroutine \u6cc4\u9732\u3002\n\t\t\/\/ \u6ce8\u610f\uff1aRoundTrip \u53ef\u80fd\u5728\u5904\u7406\u7a0b\u5e8f\u8fd4\u56de\u540e\u4ecd\u5728\u8bfb\u53d6 Body\uff0c\n\t\t\/\/ \u5173\u95ed Body \u53ef\u4ee5\u907f\u514d\u5d29\u6e83\uff08\u53c2\u89c1 Issue 46866\uff09\u3002\n\t\tdefer outreq.Body.Close()\n\t}\n\tif outreq.Header == nil {\n\t\t\/\/ \u4fdd\u8bc1 Header \u4e0d\u4e3a nil\uff0c\u4fdd\u6301\u5386\u53f2\u884c\u4e3a\uff08\u53c2\u89c1 Issue 33142\uff09\n\t\toutreq.Header = make(http.Header)\n\t}\n\n\t\/\/ \u68c0\u67e5\u662f\u5426\u540c\u65f6\u8bbe\u7f6e\u4e86 Director \u548c Rewrite\uff08\u4e24\u8005\u4e92\u65a5\uff09\n\tif (p.Director != nil) == (p.Rewrite != nil) {\n\t\tp.getErrorHandler()(rw, req, errors.New(\"ReverseProxy \u5fc5\u987b\u4e14\u53ea\u80fd\u8bbe\u7f6e Director \u6216 Rewrite \u4e2d\u7684\u4e00\u4e2a\"))\n\t\treturn\n\t}\n\n\t\/\/ \u5982\u679c\u4f7f\u7528 Director \u65b9\u5f0f\uff0c\u8c03\u7528\u5b83\u4fee\u6539\u8bf7\u6c42\n\tif p.Director != nil {\n\t\tp.Director(outreq)\n\t\tif outreq.Form != nil {\n\t\t\t\/\/ \u6e05\u7406\u65e0\u6cd5\u89e3\u6790\u7684\u67e5\u8be2\u53c2\u6570\n\t\t\toutreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery)\n\t\t}\n\t}\n\toutreq.Close = false \/\/ \u7981\u7528\u51fa\u7ad9\u8bf7\u6c42\u7684 Close \u6807\u5fd7\uff08\u8ba9 Transport \u81ea\u884c\u51b3\u5b9a\u662f\u5426\u91cd\u7528\u8fde\u63a5\uff09\n\n\t\/\/ \u68c0\u67e5\u662f\u5426\u4e3a\u534f\u8bae\u5347\u7ea7\u8bf7\u6c42\uff08\u5982 WebSocket\uff09\n\treqUpType := upgradeType(outreq.Header)\n\tif !ascii.IsPrint(reqUpType) {\n\t\t\/\/ \u534f\u8bae\u540d\u79f0\u5fc5\u987b\u662f\u53ef\u6253\u5370\u5b57\u7b26\uff0c\u5426\u5219\u62d2\u7edd\n\t\tp.getErrorHandler()(rw, req, fmt.Errorf(\"\u5ba2\u6237\u7aef\u5c1d\u8bd5\u5207\u6362\u5230\u65e0\u6548\u534f\u8bae %q\", reqUpType))\n\t\treturn\n\t}\n\t\/\/ \u79fb\u9664\u9010\u8df3\u5934\u90e8\uff08hop-by-hop headers\uff09\uff0c\u5982 Connection\u3001Keep-Alive \u7b49\n\tremoveHopByHopHeaders(outreq.Header)\n\n\t\/\/ \u5982\u679c\u539f\u59cb\u8bf7\u6c42\u4e2d\u58f0\u660e\u652f\u6301 trailer\uff0c\u5219\u5728\u51fa\u7ad9\u8bf7\u6c42\u4e2d\u4e5f\u58f0\u660e\u652f\u6301\uff08\u53c2\u89c1 Issue 21096\uff09\n\tif httpguts.HeaderValuesContainsToken(req.Header&#091;\"Te\"], \"trailers\") {\n\t\toutreq.Header.Set(\"Te\", \"trailers\")\n\t}\n\n\t\/\/ \u5982\u679c\u662f\u534f\u8bae\u5347\u7ea7\u8bf7\u6c42\uff0c\u91cd\u65b0\u6dfb\u52a0\u5fc5\u8981\u7684 Connection \u548c Upgrade \u5934\u90e8\n\tif reqUpType != \"\" {\n\t\toutreq.Header.Set(\"Connection\", \"Upgrade\")\n\t\toutreq.Header.Set(\"Upgrade\", reqUpType)\n\t}\n\n\t\/\/ \u5982\u679c\u4f7f\u7528 Rewrite \u65b9\u5f0f\n\tif p.Rewrite != nil {\n\t\t\/\/ \u79fb\u9664\u5ba2\u6237\u7aef\u53ef\u80fd\u63d0\u4f9b\u7684\u8f6c\u53d1\u5934\u90e8\uff0c\u8ba9 Rewrite \u51fd\u6570\u91cd\u65b0\u8bbe\u7f6e\uff08\u5982\u679c\u9700\u8981\uff09\n\t\toutreq.Header.Del(\"Forwarded\")\n\t\toutreq.Header.Del(\"X-Forwarded-For\")\n\t\toutreq.Header.Del(\"X-Forwarded-Host\")\n\t\toutreq.Header.Del(\"X-Forwarded-Proto\")\n\n\t\t\/\/ \u6e05\u7406\u65e0\u6cd5\u89e3\u6790\u7684\u67e5\u8be2\u53c2\u6570\n\t\toutreq.URL.RawQuery = cleanQueryParams(outreq.URL.RawQuery)\n\n\t\t\/\/ \u6784\u9020 ProxyRequest \u5e76\u8c03\u7528 Rewrite \u51fd\u6570\n\t\tpr := &amp;ProxyRequest{\n\t\t\tIn:  req,\n\t\t\tOut: outreq,\n\t\t}\n\t\tp.Rewrite(pr)\n\t\toutreq = pr.Out \/\/ \u4f7f\u7528 Rewrite \u53ef\u80fd\u8fd4\u56de\u4fee\u6539\u540e\u7684\u65b0\u8bf7\u6c42\n\t} else {\n\t\t\/\/ \u4f7f\u7528 Director \u65b9\u5f0f\u65f6\u7684\u9ed8\u8ba4\u884c\u4e3a\uff1a\u8bbe\u7f6e X-Forwarded-For \u5934\u90e8\n\t\tif clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {\n\t\t\t\/\/ \u5982\u679c\u51fa\u7ad9\u8bf7\u6c42\u4e2d\u5df2\u5b58\u5728 X-Forwarded-For \u5934\u90e8\uff0c\u5219\u5c06\u5f53\u524d\u5ba2\u6237\u7aef IP \u8ffd\u52a0\u5230\u540e\u9762\n\t\t\tprior, ok := outreq.Header&#091;\"X-Forwarded-For\"]\n\t\t\tomit := ok &amp;&amp; prior == nil \/\/ Issue 38079\uff1anil \u503c\u8868\u793a\u4e0d\u8981\u8bbe\u7f6e\u8be5\u5934\u90e8\n\t\t\tif len(prior) &gt; 0 {\n\t\t\t\tclientIP = strings.Join(prior, \", \") + \", \" + clientIP\n\t\t\t}\n\t\t\tif !omit {\n\t\t\t\toutreq.Header.Set(\"X-Forwarded-For\", clientIP)\n\t\t\t}\n\t\t}\n\t}\n\n\t\/\/ \u5982\u679c\u51fa\u7ad9\u8bf7\u6c42\u6ca1\u6709\u8bbe\u7f6e User-Agent\uff0c\u5219\u8bbe\u4e3a\u7a7a\u5b57\u7b26\u4e32\uff08\u907f\u514d\u4f7f\u7528\u9ed8\u8ba4\u7684 Go User-Agent\uff09\n\tif _, ok := outreq.Header&#091;\"User-Agent\"]; !ok {\n\t\toutreq.Header.Set(\"User-Agent\", \"\")\n\t}\n\n\t\/\/ \u7528\u4e8e\u8ddf\u8e2a 1xx \u54cd\u5e94\uff08\u5982 100 Continue\uff09\u7684\u540c\u6b65\u673a\u5236\n\tvar (\n\t\troundTripMutex sync.Mutex\n\t\troundTripDone  bool\n\t)\n\ttrace := &amp;httptrace.ClientTrace{\n\t\tGot1xxResponse: func(code int, header textproto.MIMEHeader) error {\n\t\t\troundTripMutex.Lock()\n\t\t\tdefer roundTripMutex.Unlock()\n\t\t\tif roundTripDone {\n\t\t\t\t\/\/ \u5982\u679c RoundTrip \u5df2\u7ecf\u5b8c\u6210\uff0c\u4e0d\u518d\u4fee\u6539\u54cd\u5e94\u5934\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\th := rw.Header()\n\t\t\tcopyHeader(h, http.Header(header))\n\t\t\trw.WriteHeader(code)\n\n\t\t\t\/\/ \u5bf9\u4e8e 1xx \u54cd\u5e94\uff0cWriteHeader \u4e0d\u4f1a\u81ea\u52a8\u6e05\u9664\u4e4b\u524d\u7684\u5934\u90e8\uff0c\u9700\u8981\u624b\u52a8\u6e05\u9664\n\t\t\tclear(h)\n\t\t\treturn nil\n\t\t},\n\t}\n\t\/\/ \u5c06\u8ffd\u8e2a\u5bf9\u8c61\u7ed1\u5b9a\u5230\u51fa\u7ad9\u8bf7\u6c42\u7684\u4e0a\u4e0b\u6587\u4e2d\n\toutreq = outreq.WithContext(httptrace.WithClientTrace(outreq.Context(), trace))\n\n\t\/\/ \u4f7f\u7528 Transport \u53d1\u9001\u8bf7\u6c42\uff0c\u83b7\u5f97\u540e\u7aef\u54cd\u5e94\n\tres, err := transport.RoundTrip(outreq)\n\troundTripMutex.Lock()\n\troundTripDone = true\n\troundTripMutex.Unlock()\n\tif err != nil {\n\t\t\/\/ \u53d1\u751f\u9519\u8bef\uff08\u5982\u540e\u7aef\u4e0d\u53ef\u8fbe\uff09\uff0c\u8c03\u7528\u9519\u8bef\u5904\u7406\u5668\n\t\tp.getErrorHandler()(rw, outreq, err)\n\t\treturn\n\t}\n\n\t\/\/ \u5904\u7406\u534f\u8bae\u5347\u7ea7\u54cd\u5e94\uff08101 Switching Protocols\uff09\uff0c\u5982 WebSocket\n\tif res.StatusCode == http.StatusSwitchingProtocols {\n\t\tif !p.modifyResponse(rw, res, outreq) {\n\t\t\treturn\n\t\t}\n\t\tp.handleUpgradeResponse(rw, outreq, res)\n\t\treturn\n\t}\n\n\t\/\/ \u79fb\u9664\u54cd\u5e94\u4e2d\u7684\u9010\u8df3\u5934\u90e8\n\tremoveHopByHopHeaders(res.Header)\n\n\t\/\/ \u8c03\u7528 ModifyResponse \u94a9\u5b50\uff08\u5982\u679c\u8bbe\u7f6e\uff09\uff0c\u5141\u8bb8\u4fee\u6539\u54cd\u5e94\n\tif !p.modifyResponse(rw, res, outreq) {\n\t\treturn\n\t}\n\n\t\/\/ \u5c06\u540e\u7aef\u54cd\u5e94\u7684\u5934\u90e8\u590d\u5236\u5230\u5ba2\u6237\u7aef\u54cd\u5e94\u4e2d\n\tcopyHeader(rw.Header(), res.Header)\n\n\t\/\/ \u5904\u7406 trailer\uff08\u5c3e\u90e8\uff09\u5934\u90e8\uff1aTransport \u4e0d\u4f1a\u81ea\u52a8\u5305\u542b Trailer \u5934\u90e8\uff0c\u9700\u8981\u4ece res.Trailer \u6784\u5efa\n\tannouncedTrailers := len(res.Trailer)\n\tif announcedTrailers &gt; 0 {\n\t\ttrailerKeys := make(&#091;]string, 0, len(res.Trailer))\n\t\tfor k := range res.Trailer {\n\t\t\ttrailerKeys = append(trailerKeys, k)\n\t\t}\n\t\trw.Header().Add(\"Trailer\", strings.Join(trailerKeys, \", \"))\n\t}\n\n\t\/\/ \u5199\u5165\u54cd\u5e94\u72b6\u6001\u7801\n\trw.WriteHeader(res.StatusCode)\n\n\t\/\/ \u5c06\u54cd\u5e94\u4f53\u590d\u5236\u5230\u5ba2\u6237\u7aef\uff0c\u5e76\u6839\u636e flushInterval \u5237\u65b0\n\terr = p.copyResponse(rw, res.Body, p.flushInterval(res))\n\tif err != nil {\n\t\tdefer res.Body.Close()\n\t\t\/\/ \u590d\u5236\u8fc7\u7a0b\u4e2d\u51fa\u9519\uff0c\u6839\u636e\u662f\u5426\u5e94\u8be5 panic \u51b3\u5b9a\u5904\u7406\u65b9\u5f0f\n\t\tif !shouldPanicOnCopyError(req) {\n\t\t\tp.logf(\"\u6d4b\u8bd5\u4e2d\u6291\u5236 copyResponse \u9519\u8bef\u5bfc\u81f4\u7684 panic\uff1bcopy \u9519\u8bef\uff1a%v\", err)\n\t\t\treturn\n\t\t}\n\t\t\/\/ \u89e6\u53d1\u4e2d\u6b62\u5904\u7406\u5668\u7684 panic\uff0c\u4e2d\u65ad\u8bf7\u6c42\n\t\tpanic(http.ErrAbortHandler)\n\t}\n\tres.Body.Close() \/\/ \u73b0\u5728\u5173\u95ed Body \u4ee5\u586b\u5145 res.Trailer\n\n\t\/\/ \u5982\u679c\u54cd\u5e94\u4e2d\u5e26\u6709 trailer\uff0c\u5f3a\u5236\u5206\u5757\u4f20\u8f93\uff08\u907f\u514d\u6dfb\u52a0 Content-Length\uff09\n\tif len(res.Trailer) &gt; 0 {\n\t\thttp.NewResponseController(rw).Flush()\n\t}\n\n\t\/\/ \u5199\u5165 trailer \u5934\u90e8\n\tif len(res.Trailer) == announcedTrailers {\n\t\t\/\/ \u7b80\u5355\u60c5\u51b5\uff1a\u76f4\u63a5\u590d\u5236 trailer\n\t\tcopyHeader(rw.Header(), res.Trailer)\n\t\treturn\n\t}\n\n\t\/\/ \u590d\u6742\u60c5\u51b5\uff1a\u67d0\u4e9b trailer \u53ef\u80fd\u672a\u88ab\u9884\u5148\u58f0\u660e\uff0c\u4f7f\u7528 TrailerPrefix \u524d\u7f00\u5199\u5165\n\tfor k, vv := range res.Trailer {\n\t\tk = http.TrailerPrefix + k\n\t\tfor _, v := range vv {\n\t\t\trw.Header().Add(k, v)\n\t\t}\n\t}\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u4e4b\u524d\u7684\u7b80\u6613\u7248 HTTP \u4ee3\u7406\u4e0d\u5177\u5907\u4e0b\u9762\u8fd9\u4e9b\u529f\u80fd\uff1a \u5927\u7eb2\uff1a\u7528 ReverseProxy \u5b9e\u73b0\u4e00\u4e2a HTTP \u4ee3\u7406 &#8230; <a title=\"Go\u5f00\u53d1\u4f01\u4e1a\u7ea7\u5fae\u670d\u52a1\u7f51\u5173\uff086\uff09ReverseProxy\" class=\"read-more\" href=\"https:\/\/fableary.top\/?p=56\" aria-label=\"\u9605\u8bfb Go\u5f00\u53d1\u4f01\u4e1a\u7ea7\u5fae\u670d\u52a1\u7f51\u5173\uff086\uff09ReverseProxy\">\u9605\u8bfb\u66f4\u591a<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-56","post","type-post","status-publish","format-standard","hentry","category-gogateway"],"_links":{"self":[{"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/posts\/56","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/fableary.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=56"}],"version-history":[{"count":3,"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions"}],"predecessor-version":[{"id":60,"href":"https:\/\/fableary.top\/index.php?rest_route=\/wp\/v2\/posts\/56\/revisions\/60"}],"wp:attachment":[{"href":"https:\/\/fableary.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=56"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fableary.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=56"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fableary.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=56"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}