使Go HTTP客户端与非标准的HTTP服务器配合工作

huangapple go评论115阅读模式
英文:

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"

然而,我想让它正常工作,想知道最佳的解决方法是什么。我目前的想法有:

  1. 使用自定义的http.Transport.ProxyICY在传输过程中改为HTTP/1.1
  2. 使用一个独立的代理进程来完成相同的操作。
  3. 重载http.ParseHTTPVersion(但是Go语言不支持函数重载)。
  4. 复制整个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:

  1. use a custom http.Transport.Proxy to change ICY to HTTP/1.1 in-flight
  2. an out of process proxy that does the same thing
  3. overload http.ParseHTTPVersion (but golang doesn't have function overloading)
  4. 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)
}

huangapple
  • 本文由 发表于 2015年11月2日 14:11:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/33471704.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定