
カスタマイズ可能なHTTP/HTTPSプロキシライブラリ for Go。通常のフォワーディング、CONNECTトンネリング、MITM TLSインターセプション、プログラムによるリクエスト/レスポンスの変更をサポートしています。
GoProxy は、Go(別名 Golang)を使って カスタマイズ可能な HTTP/HTTPS プロキシサーバー を作成するためのライブラリで、設定可能なオプションがいくつか用意されています。
このプロジェクトの目標は、ある程度のトラフィックを処理できる 最適化された プロキシサーバーを提供しつつ、カスタマイズ可能 で プログラム可能 なものにすることです。
プロキシ自体は単なる net/http ハンドラなので、その上に複数のミドルウェア(パニックリカバリ、ロギング、圧縮など)を追加できます。また、他の任意の HTTP ネットワークライブラリと簡単に統合できます。
goproxy を使用するには、ブラウザ(またはその他のクライアント)が HTTP プロキシとして goproxy を使用するように設定する必要があります。
Chrome と Firefox での設定方法はこちらです。
base サンプルから始める場合、プロキシとして使用する URL は localhost:8080 です。これはサンプルでのデフォルト値です。
また、クライアント側での証明書の問題を避けるために、プロキシの CA 証明書を信頼する必要があります。
特定のホスト に対してのみ特定のアクションを実行リクエスト と レスポンス を操作カスタム http.Transport を使用MITM 証明書キャッシュ を指定して、同じホストへの後続のリクエストで再利用し、CPU を節約。デフォルトでは無効ですが、本番環境では使用すべきです!相対パス(例: /ping)の場合、通常の HTTP トラフィックを カスタムハンドラ にリダイレクトLogger インターフェースを実装することで、使用するロガーを選択可能PreventCanonicalization を true に設定することで、HTTP リクエストヘッダーの 正規化 を 無効化 可能あなたの会社は GoProxy を使用していますか?プロジェクトの維持と健全性の維持にご協力ください! GoProxy をサポートすることで、バグ修正と新機能により多くの時間を割くことができます。 その代わりに、Gold Supporter または Enterprise プランを選択していただければ、ここに会社のロゴを掲載させていただきます。
GoProxy をプロジェクトに統合する必要がある場合、またはフォークで維持したいカスタム機能がある場合は、現在のメンテナである Erik にメールで連絡してください。有料の独立コンサルタントとしてどのように支援できるかを一緒に話し合うことができます。
問題、提案、またはバグを見つけた場合は、遠慮なく GitHub の issue を開いてお知らせください。
これはボランティアによって管理されている オープンソース プロジェクトであり、プロジェクトを改善できるあらゆることについて話し合うことを歓迎します。
問題の背景や変更したい内容など、すべてを説明して、問題を理解しやすくしてください。
小さなコード変更であれば、直接 Pull Request を開くこともできますが、説明にはすべてを記載する必要があります。
5,000 行を変更した refactoring という名前のプルリクエストを開いても、マージしません...:D
このプロジェクトのコードは BSD 3-Clause ライセンスの下でリリースされており、商用 利用にも適しています。
あなたは GoProxy を個人プロジェクトまたは勤務先の会社のプロジェクトに導入・統合しました。
このライブラリで作られた新しい クリエイティブなソリューション についてぜひ知りたいので、上記のメンテナにメールで 連絡 して、このプロジェクトがあなたのニーズにどのように役立ったかを説明してください。
会社と 秘密保持契約 を結んでいる場合は、会社の公式ウェブサイトにこのトピックについての ブログ記事 を書くことを提案できます。そうすれば、その情報は会社の選択によって公開され、ブログ記事の リンクを共有 していただけます:)
ケーススタディの目的は、このプロジェクトの 貢献者 がその支援によってどのように世界を 改善 しているのか、そして人々がそれを使って何を構築しているのかを コミュニティ と共有することです。
コードベースは、プルリクエストのコードに対して自動 lint チェックを使用します。 プルリクエストを開く前に、ローカルマシンでリンターを実行して変更がリンターに適合しているか確認してください。そうすれば、驚くことはありません。
リンターをインストールするには:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
これにより、$GOPATH/bin フォルダに実行可能ファイルが作成されます
($GOPATH は環境変数で、通常その値は ~/go に相当します。不明な場合は、お使いのマシンで値を確認してください)。
シェルのパスに bin フォルダを含めて、golangci-lint run コマンドを直接使用できるようにしてください。
goproxy を試すために、データを宛先に転送するだけの基本的な HTTP/HTTPS プロキシを紹介します:
package main
import (
"log"
"net/http"
"github.com/elazarl/goproxy"
)
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = true
log.Fatal(http.ListenAndServe(":8080", proxy))
}
次の行は、プロキシを通過するすべてのリクエストに X-GoProxy: yxorPoG-X ヘッダーを追加してから、宛先に送信します:
proxy.OnRequest().DoFunc(
func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
r.Header.Set("X-GoProxy","yxorPoG-X")
return r,nil
})
OnRequest() の入力が空の場合、DoFunc で指定された関数がプロキシへのすべての受信リクエストを処理します。この場合、リクエストにヘッダーを追加して呼び出し元に返します。
プロキシは変更されたリクエストを宛先に送信します。
型に指定されたインターフェースを実装している場合は、DoFunc の代わりに Do を使用することもできます。
⚠️ レスポンスとして nil 値を返したことに注意してください。 返されたレスポンスが nil でない場合、goproxy はリクエストを破棄し、指定されたレスポンスをクライアントに送信します。
サーバーのローカルタイムゾーンで 8 時から 17 時の間、www.reddit.com への接続を拒否します:
proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
func(req *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
if h,_,_ := time.Now().Clock(); h >= 8 && h <= 17 {
resp := goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusForbidden, "Don't waste your time!")
return req, resp
}
return req, nil
})
DstHostIs は ReqCondition を返します。これは *http.Request を受け取り、リクエストが条件を満たすかどうかをチェックする boolean を返す関数です(そして処理されます)。
DstHostIs("www.reddit.com") は、リクエストが "www.reddit.com" に向けられたときに true を返す ReqCondition を返します。
ホストの等価チェックは DNS リゾルバの動作を反映して 大文字と小文字を区別しません。そのため、ユーザーが "www.rEdDit.com" と入力しても、比較は条件を満たします。
時刻が 8:00 から 17:59 の間の場合、DoFunc() で直接レスポンスを返すため、リモートの宛先はリクエストを受信せず、クライアントは "Don't waste your time!" というレスポンスを受信します。
import "github.com/elazarl/goproxy"
examples フォルダには、最も一般的なケースをカバーするプロキシの使用例がいくつかあります。それらを参照して、頑張ってください!
プロキシの動作を操作するためのハンドラには、次の 3 種類があります:
// handler called after receiving HTTP CONNECT from the client, and
// before proxy establishes connection with the destination host
httpsHandlers []HttpsHandler
// handler called before proxy sends HTTP request to destination host
reqHandlers []ReqHandler
// handler called after proxy receives HTTP Response from destination host,
// and before proxy forwards the Response to the client
respHandlers []RespHandler
操作したい内容に応じて、上記の各リストにハンドラを追加する方法は次のとおりです:
// Add handlers to httpsHandlers
proxy.OnRequest(some ReqConditions).HandleConnect(YourHandlerFunc())
// Add handlers to reqHandlers
proxy.OnRequest(some ReqConditions).Do(YourReqHandlerFunc())
// Add handlers to respHandlers
proxy.OnResponse(some RespConditions).Do(YourRespHandlerFunc())
例:
// This rejects the HTTPS request to *.reddit.com during HTTP CONNECT phase.
// Reddit URL check is case-insensitive because of (?i), so the block will work also if the user types something like rEdDit.com.
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("(?i)reddit.*:443$"))).HandleConnect(goproxy.AlwaysReject)
// Be careful about this example! It shows you a common error that you
// need to avoid.
// This will NOT reject the HTTPS request with URL ending with .gif because,
// if the scheme is HTTPS, the proxy will receive only URL.Hostname
// and URL.Port during the HTTP CONNECT phase.
proxy.OnRequest(goproxy.UrlMatches(regexp.MustCompile(`.*gif$`))).HandleConnect(goproxy.AlwaysReject)
// To fix the previous example, here there is the correct way to manipulate
// an HTTP request using URL.Path (target path) as a condition.
proxy.OnRequest(goproxy.UrlMatches(regexp.MustCompile(`.*gif$`))).Do(YourReqHandlerFunc())
プロキシでのリクエスト処理中にエラーが発生した場合、デフォルトではプロキシは HTTP エラー 500(Internal Server Error)を エラーメッセージ を ボディ コンテンツとして返します。
この動作を上書きしたい場合は、エラーレスポンスを変更する独自の RespHandler を定義できます。
コンテキストパラメータのうち、ctx.Error には発生した エラー が含まれます。エラーが発生しなかった場合は nil 値が含まれます。
必要に応じて、カスタム JSON をボディとして返すなど、自由に処理できます。 エラーハンドラの例:
proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
var dnsError *net.DNSError
if errors.As(ctx.Error, &dnsError) {
// Do not leak our DNS server's address
dnsError.Server = "<server-redacted>"
return goproxy.NewResponse(ctx.Req, goproxy.ContentTypeText, http.StatusBadGateway, dnsError.Error())
}
return resp
})
ターゲットのリモートサーバー(またはプロキシクライアント)へのデータ送信中にエラーが発生した場合、proxy.ConnectionErrHandler が呼び出されてエラーが処理されます。存在しない場合は デフォルトハンドラ が使用されます。
エラーは 関数パラメータ として渡され、プロキシコンテキスト内ではありません。そのため、このハンドラで ctx.Error フィールドを確認する必要はありません。
このハンドラでは、プロキシクライアントとの生の接続に(io.Writer として)アクセスできるため、必要に応じてエラーデータを含む任意の HTTP データをその接続に送信できます。
接続がすでに閉じられていないという保証はないため、Write() がエラーを返す可能性があります。
接続 はエラーハンドラの呼び出し後にプロキシライブラリによって 自動的に閉じられます。そのため、その点を心配する必要はありません。
このプロジェクトは 10 年前 に作成され、成熟 した段階に達しています。本番環境 で安全に使用でき、多くのプロジェクトですでに使用されています。
将来 破壊的な変更 がある場合は、Go モジュールの 新しいバージョン(例: v2)がリリースされます。