【问题标题】:http to https proxy in gogo中的http到https代理
【发布时间】:2018-04-18 14:53:38
【问题描述】:

我已经开始研究缓存,以便将经常从各种 S3 服务器中提取的 S3 对象放入我们的数据中心。其中一些是 GB 大小,许多服务器正在请求相同的对象。所以为了提高性能,需要这个缓存。

但是,与我见过的其他 S3 缓存不同,我不需要 s3 身份验证部分。它已经包含在我的客户请求的标头中。

所以计划是解析请求,检查对象是否在本地完全存在,或者当前正在被获取。完成后,返回结果。 对象被方便地散列。因此,如果它的内容发生变化,它就是一个新的哈希和一个新的对象。

请求已经包含通过 S3 进行身份验证所需的所有内容。但我有点卡住的地方是我不熟悉 HTTP 的代理连接协议。我修改了一些我发现不使用 Hijack 的示例开始代码,因此我可以解释正在发生的事情并决定我是否需要实际上获取上游对象。但它不起作用。客户端吐槽:

获取https://example.com: tls: 第一条记录看起来不像是 TLS 握手

嗯。

这是侦听 HTTP 的代理(它将位于防火墙后面,因此为了简单起见,我故意将其设为不安全)。

package main

import (
    "crypto/tls"
    "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()

    log.Println(req.RemoteAddr, " ", resp.Status)

    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() {
    server := &http.Server{
        Addr: ":8080",
        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)),
    }

    log.Fatal(server.ListenAndServe())
}

客户端如下所示:

package main

import (
    "net/http"
    "fmt"
    "io/ioutil"
    "os"
    "net/url"
)

func main() {  
    args := os.Args[1:]
    var whereTo string
    if len(args) > 0 {
        whereTo = args[0]
    } else {
        fmt.Println("Usage: htclient [url]")
        os.Exit(1)
    }

    proxyUrl, err := url.Parse("http://localhost:8080")
    client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}

    req, err := http.NewRequest("GET", whereTo, nil)

    response, err := client.Do(req)
    if err != nil {
        fmt.Printf("%s", err)
        os.Exit(1)
    } else {
        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        if err != nil {
            fmt.Printf("%s", err)
            os.Exit(1)
        }
        fmt.Printf("%s\n", string(contents))
    }
}

建议?

【问题讨论】:

标签: go proxy


【解决方案1】:

您发送 200 OK 响应太早了;错误检查后发送。

https://www.rfc-editor.org/rfc/rfc7231#section-4.3.6(强调我的):

CONNECT 仅用于对代理的请求。一个起源 收到自己的 CONNECT 请求的服务器可以用 2xx(成功)状态码表示连接成功 成立

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
    }

    // too early: 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)
        return // missing in your sample code
    }

    w.WriteHeader(http.StatusOK) // respond 2xx here
    go transfer(dest_conn, client_conn)
    go transfer(client_conn, dest_conn)
}

【讨论】:

  • 嗯,对不起。我只是注意到我发布了错误的代码。明天将使用正确的代码更新。这个实际上是有效的,因为它执行连接的东西,但我想代表我的 s3 客户端执行请求,并且可能只是从缓存中响应。
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 2017-07-10
  • 2020-02-06
  • 1970-01-01
  • 2017-05-21
  • 2020-11-24
  • 2016-09-30
  • 2018-03-08
相关资源
最近更新 更多