【问题标题】:Extending golang's http package扩展golang的http包
【发布时间】:2016-07-26 07:32:01
【问题描述】:

我需要扩展 http 包以实现包含状态错误描述的非标准响应,即: 400 缺少必需的参数 而不是标准状态描述的 400 Bad request。

这是我的实际实现:

package main

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

type GatewayHandler int

func main() {
    var gh GatewayHandler

    http.ListenAndServe(":9000", gh)
}

func (gh GatewayHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) {

    legacyApiUrl := "http://some-url.com" + req.URL.RequestURI()

    client := &http.Client{}
    request, _ := http.NewRequest(req.Method, legacyApiUrl, nil)
    response, _ := client.Do(request)
    res.Header().Set("Status", response.Status)
    for k, v := range response.Header {
        fmt.Println(k, ": ", v)
        i := ""
        for _, j := range v {
            i += j
        }
        res.Header().Set(k, i)
    }

    res.WriteHeader(response.StatusCode)

    if response.Status != "200 OK" {
        fmt.Println(response.Status)
    }

    result, _ := ioutil.ReadAll(response.Body)
    output := string(result)
    fmt.Println(output)

    io.WriteString(res, output)
}

一般来说,我需要从使用它的其他 URL 转发该状态,并且我需要保持它兼容。

非常感谢您。

约瑟夫

【问题讨论】:

  • 您的具体问题是什么?在您当前的实施中,什么对您不起作用?你想为一些遗留 API 编写代理吗?
  • 是的,有点...问题是标题需要像 状态消息应该包含错误描述,但不是标准的,例如 400 Bad request , 但是 400 缺少必需的参数,或者 400 No timestamp in request or so...

标签: http go http-status


【解决方案1】:

您可以使用http.Hijacker 接口https://golang.org/pkg/net/http/#Hijacker 来“劫持”(接管)服务器与客户端的TCP 连接,并向其写入自定义响应。这是对示例 https://golang.org/pkg/net/http/#example_Hijacker 的修改,以返回“400 所需参数缺失”而不是标准的“400 错误请求”响应给客户端:

package main

import "net/http"

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        hj, ok := w.(http.Hijacker)
        if !ok {
            http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
            return
        }
        conn, bufrw, err := hj.Hijack()
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        // Don't forget to close the connection:
        defer conn.Close()
        // non-standard HTTP status text and an HTTP header are written;
        // end of the Headers part of the messages is marked by extra \r\n
        bufrw.WriteString("HTTP/1.1 400 Required parameter is missing\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")
        // write the body of the HTTP response message
        bufrw.WriteString("400 Required parameter is missing\n")
        bufrw.Flush()
    })
    http.ListenAndServe(":9000", nil)
}

运行此程序并发送 curl 请求会产生所需的响应:

$ curl -i http://localhost:9000/ HTTP/1.1 400 缺少必需参数 内容类型:文本/html;字符集=utf-8 400 缺少必需的参数

扩展它以传播来自旧 API 服务器的其他响应应该很简单。

编辑
根据 HTTP 消息标准 (https://www.rfc-editor.org/rfc/rfc7230#section-3) 在示例程序中使用 \r\n\r\n 来终止 HTTP 响应的 Headers 部分;为了清晰起见,分隔 WriteString 调用 HTTP 响应的标头和正文。

【讨论】:

  • 直到!谢谢你。
  • 哇,这很漂亮,但是如何将标题和内容写入被劫持的连接?无论如何,这就是我要找的。在此先感谢:)
  • 我修改了上面的程序,对标题和正文进行单独的 bufrw.WriteString 调用,并用额外的 \r\n 空行分隔。当然,您可以添加更多标头或从代理连接中复制它们。请记住,通过劫持,您可以完全控制写入的内容并模仿 HTTP 服务器 - 例如,如果发送 Content-Length 标头很重要,您应该计算响应长度并“手动”发送标头,与示例中的 Content-Type 一样。
  • 如果你想要半代理功能,你可以在劫持后做response.Write(bufrw)
  • 这个解决方案是完美的。我只是添加了一些 \n s 和 \r s 以使其工作。非常感谢:)
猜你喜欢
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2018-02-28
  • 2018-08-06
  • 2011-10-21
  • 2012-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多