HTTP (S) proxy implemented in less than 100 lines of Golang code

Source: Internet
Author: User
Tags ssl connection
This is a creation in Article, where the information may have evolved or changed. [] (HTTPS://RAW.GITHUBUSERCONTENT.COM/STUDYGOLANG/GCTT-IMAGES/MASTER/HTTP-S-PROXY/HEADER.JPEG) Our goal is to achieve a process that can handle HTTP and HTTPS [proxy Server] (https://en.wikipedia.org/wiki/Proxy_server). The process of proxy HTTP requests is actually a process of parsing the request, forwarding the request to the destination server, reading the destination server response, and transmitting it back to the original client. This process is only possible with the built-in HTTP server and client ([Net/http] (https://golang.org/pkg/net/http/)). The difference between HTTPS is the use of a technology called "HTTP CONNECT Tunnel". First, the client sends the request with the HTTP CONNECT method to establish a tunnel to the destination server. When the tunnel is ready with two TCP connections, the client starts a regular handshake with the destination server to establish a secure connection, and then sends the request and receives the response. # # Certificate Our agent is an HTTPS server (when using the '--proto HTTPS ' parameter) and therefore requires a certificate and private key. We use self-signed certificates. Generated using the following script: "' bash#!/usr/bin/env bashcase ' uname-s ' in Linux *) sslconfig=/etc/ssl/openssl.cnf;; darwin*) sslconfig=/system/library/openssl/openssl.cnf;; Esacopenssl req \-newkey rsa:2048 \-x509 \-nodes \-keyout server.key \-new \-out server.pem \-subj/cn=localhost \ -reqexts san \-extensions san \-config < (cat $sslConfig \ < (printf ' [san]\nsubjectaltname=dns:localhost ')) \-SHA2 -days \ 3650 "needs to have your operating system trust the certificate. OS X systems can be processed using Keychain Access,See https://tosbourn.com/getting-os-x-to-trust-self-signed-ssl-certificates## HTTP we use [built-in HTTP server and client] (https:// golang.org/pkg/net/http/) for HTTP support. The role of the agent is to process the HTTP request, forward the request to the destination server, and return the response to the original client. [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/http-s-proxy/http_proxy.png) # # HTTP CONNECT The tunnel assumes that the client and the server may interact with the server using HTTPS or WebSocket, and the client will find that the proxy is being used. In some scenarios, a simple HTTP request/response flow cannot be used, such as when a client needs to establish a secure connection with the server (HTTPS) or if it wants to use another TCP-based protocol such as WebSockets. At this point, the HTTP [CONNECT] (Https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT) method has appeared. The HTTP Connect method tells the proxy server to establish a TCP connection with the destination server, and the proxy ends at the client's TCP stream after the connection is successfully established. In this way, the proxy server does not terminate the SSL connection, but simply passes the data between the client and the destination server. So the connection between the client and the destination server is secure. [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/http-s-proxy/http_connect_tunneling.png) # # Implement the "Gopackage Mainimport (" Crypto/tls "" Flag "" IO "" Log "" Net "" Net/http "" Time ") Func handletunneling (w http. Responsewriter, R *http. Request) {dest_conn, err: = Net. Dialtimeout ("TCP", R.host, 10*time. Second) If Err ! = Nil {http. Error (W, err. Error (), HTTP. statusserviceunavailable) return} w.writeheader (http. Statusok) hijacker, OK: = W. (http. Hijacker) if!ok {http. Error (W, "hijacking not supported", HTTP. Statusinternalservererror) Return} Client_conn, _, Err: = hijacker. Hijack () if err! = Nil {http. Error (W, err. Error (), HTTP. statusserviceunavailable)} Go Transfer (Dest_conn, client_conn) go transfer (Client_conn, dest_conn)}func transfer ( Destination IO. Writecloser, source IO. Readcloser) {defer destination. Close () defer source. Close () Io. Copy (destination, source)}func Handlehttp (w http. Responsewriter, req *http. Request) {resp, err: = http. Defaulttransport.roundtrip (req) If err! = Nil {http. Error (W, err. Error (), HTTP. statusserviceunavailable) return} Defer resp. Body.close () Copyheader (W.header (), resp. Header) W.writeheader (resp. StatusCode) io. Copy (W, resp. Body)}func copyheader (DST, src http. Header) {for k, vv: = Range src {For _, V: = range VV {DST. Add (k, v)}}}func main () {var pempathString flag. Stringvar (&pempath, "Pem", "Server.pem", "Path to Pem file") var keypath string flag. Stringvar (&keypath, "key", "Server.key", "Path to key file") var proto string flag. Stringvar (&proto, "Proto", "https", "Proxy Protocol (HTTP or HTTPS)") flag. Parse () if proto! = "http" && proto! = "https" {log. Fatal ("Protocol must be either HTTP or https")} server: = &http. server{Addr: ": 8888", handler:http. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {if R.method = = http. Methodconnect {handletunneling (W, R)} else {handlehttp (W, R)}}),//Disable HTTP/2. Tlsnextproto:make (Map[string]func (*http. Server, *tls. Conn, http. Handler)),} if proto = = "http" {log. Fatal (server. Listenandserve ())} else {log. Fatal (server. Listenandservetls (Pempath, KeyPath)}} ' > The code shown above is not a production-level solution. Lack of processing of [Hop-by-hop header information] (HTTPS://DEVELOPER.MOZILLA.ORG/EN-US/DOCS/WEB/HTTP/HEADERS#HBH), in two connections or by ' net/http ' The expiration time is not set during the process of replicating data between exposed service ports (for more information see: ["The Complete Guide to Go net/httP Timeouts "] (https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/)) (translation: [Go] Net/http Full manual for timeout mechanism] (https://studygolang.com/articles/9339). When our server receives the request, it will choose between processing the HTTP request and the HTTP CONNECT tunnel request, which is implemented by the following code: "Gohttp. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {if R.method = = http. Methodconnect {handletunneling (W, R)} else {handlehttp (W, R)}}) ' The Handlehttp function that handles the HTTP request as shown in its name, we will focus on the handletu of the processing tunnel The Nneling function. The first part of the Handletunneling function sets the connection to the destination server: ' Godest_conn, err: = Net. Dialtimeout ("TCP", R.host, 10*time. Second) If err! = Nil {http. Error (W, err. Error (), HTTP. statusserviceunavailable) Return}w.writeheader (http. Statusok) "is immediately followed by the portion of the hijacked connection maintained by the HTTP server:" Gohijacker, OK: = W. (http. Hijacker) if!ok {http. Error (W, "hijacking not supported", HTTP. Statusinternalservererror) Return}client_conn, _, Err: = hijacker. Hijack () if err! = Nil {http. Error (W, err. Error (), HTTP. statusserviceunavailable)} ' [Hijacker interface] (https://golang.org/pkg/net/http/#Hijacker) allows the connection to be taken over. Then by the initiatorResponsible for managing the connection (HTTP is no longer processed). Once we have two TCP connections (client-to-agent, proxy-to-destination server), you need to start the tunnel: ' ' Gogo Transfer (Dest_conn, client_conn) go transfer (Client_conn, Dest_conn) ' The data in the two Goroutine is copied in two directions: from the client to the destination server and its inverse direction. # # test can test our agents in chrome using the following configuration: ' Go> chrome--proxy-server=https://localhost:8888 ' or with [Curl] (https:// Github.com/curl/curl): ' > Curl-lv--proxy https://localhost:8888--proxy-cacert server.pem https://google.com ' > Curl requires native support for Https-proxy (introduced in 7.52.0). # # HTTP/2 Our servers deliberately remove support for HTTP/2 because hijacking is not possible. For more information, see [#14797] (https://github.com/golang/go/issues/14797#issuecomment-196103814).

via:https://medium.com/@mlowicki/http-s-proxy-in-golang-in-less-than-100-lines-of-code-6a51c2f2c38c

Author: Michałłowicki Translator: dongkui0712 proofreading: Rxcai

This article by GCTT original compilation, go language Chinese network honor launches

This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove

1666 reads ∙1 likes
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.