英文:
making go http client work with non-standard http servers
问题
Shoutcast服务器基本上使用HTTP协议,但有一个重要的区别:它们在响应GET
请求时使用ICY 200 OK
而不是HTTP/1.1 200 OK
。
Go语言对此不予支持,并且会正确地报错malformed HTTP version "ICY"
。
然而,我想让它正常工作,想知道最佳的解决方法是什么。我目前的想法有:
- 使用自定义的
http.Transport.Proxy
将ICY
在传输过程中改为HTTP/1.1
。 - 使用一个独立的代理进程来完成相同的操作。
- 重载
http.ParseHTTPVersion
(但是Go语言不支持函数重载)。 - 复制整个http包,只修改
ParseHTTPVersion
函数。
第一种方法似乎是最有吸引力的,但我不知道如何尊重HTTP的“范围”,并实际修改给定HTTP版本的所有响应。http.Transport.Proxy
能处理这种情况吗?
有人能给我一些建议吗?
英文:
Shoutcast servers basically speak http, with one important difference: they respond to GET
requests with ICY 200 OK
instead of HTTP/1.1 200 OK
.
Go won't have a bar of it, and correctly fails with the error malformed HTTP version "ICY"
.
However I would like to make things work and am wondering what the best approach is. My ideas so far:
- use a custom http.Transport.Proxy to change
ICY
toHTTP/1.1
in-flight - an out of process proxy that does the same thing
- overload
http.ParseHTTPVersion
(but golang doesn't have function overloading) - duplicate the entire http package, just to modify
ParseHTTPVersion
Number 1. seems the most attractive attractive, but I have no idea how to respect the http "scope" and actually modify all responses on a given http version. Is this the kind of thing http.Transport.Proxy
can handle?
Can anyone give me any pointers?
答案1
得分: 4
我通过创建一个自定义的Dial函数来实现了这个功能,该函数返回一个包装的连接。我的包装器拦截连接上的第一个读取操作,并将ICY替换为HTTP/1.1。这种方法并不是非常健壮,但证明了这个概念:
package main
import (
"fmt"
"net"
"net/http"
)
type IcyConnWrapper struct {
net.Conn
haveReadAny bool
}
func (i *IcyConnWrapper) Read(b []byte) (int, error) {
if i.haveReadAny {
return i.Conn.Read(b)
}
i.haveReadAny = true
//省略了边界检查。这里有几种可能出错的方式。
//始终检查数组大小和返回的n。
n, err := i.Conn.Read(b[:3])
if err != nil {
return n, err
}
if string(b[:3]) == "ICY" {
//将正确的HTTP响应写入缓冲区
copy(b, []byte("HTTP/1.1"))
return 8, nil
}
return n, nil
}
func main() {
tr := &http.Transport{
Dial: func(network, a string) (net.Conn, error) {
realConn, err := net.Dial(network, a)
if err != nil {
return nil, err
}
return &IcyConnWrapper{Conn: realConn}, nil
},
}
client := &http.Client{Transport: tr}
http.DefaultClient = client
resp, err := http.Get("http://178.33.230.189:8100") //在互联网上找到的随机URL
fmt.Println(err)
fmt.Println(resp.StatusCode)
}
希望对你有帮助!
英文:
I got this working by creating a custom Dial function that returns a wrapped connection. My wrapper intercepts the first read on the connection and replaces ICY with HTTP/1.1. Not super robust, but proves the concept:
package main
import (
"fmt"
"net"
"net/http"
)
type IcyConnWrapper struct {
net.Conn
haveReadAny bool
}
func (i *IcyConnWrapper) Read(b []byte) (int, error) {
if i.haveReadAny {
return i.Conn.Read(b)
}
i.haveReadAny = true
//bounds checking ommitted. There are a few ways this can go wrong.
//always check array sizes and returned n.
n, err := i.Conn.Read(b[:3])
if err != nil {
return n, err
}
if string(b[:3]) == "ICY" {
//write Correct http response into buffer
copy(b, []byte("HTTP/1.1"))
return 8, nil
}
return n, nil
}
func main() {
tr := &http.Transport{
Dial: func(network, a string) (net.Conn, error) {
realConn, err := net.Dial(network, a)
if err != nil {
return nil, err
}
return &IcyConnWrapper{Conn: realConn}, nil
},
}
client := &http.Client{Transport: tr}
http.DefaultClient = client
resp, err := http.Get("http://178.33.230.189:8100") //random url I found on the internet
fmt.Println(err)
fmt.Println(resp.StatusCode)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论