我有一个很大的io.ReadCloser
是从HTTP处理程序函数中的http.Request
获得的。我需要将请求代理到另一台服务器,但是首先,我想在正文中找到与Title: (\w+)
之类的正则表达式匹配的字符串。这很困难-将整个主体复制到新的缓冲区中以进行操作会占用过多的内存,而我尝试使用regexp.FindReaderSubmatchIndex,但它只给我结果的索引,而不是实际的字符串。
执行此操作的最佳方法是什么?令牌生成器和JSON解码器等似乎可以在io流上运行,这是一个非常简单的用例。有人可以指出正确的方向吗?
答案 0 :(得分:1)
我将为此使用io.TeeReader并将特殊writer传递给TeeReader构造函数。考虑将以下代码作为伪代码,因为有些边缘情况我们在这里不处理:
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"regexp"
)
type Finder struct {
Regexp *regexp.Regexp
match string
}
//Write implements io.Writer interface
func (f *Finder) Write(p []byte) (int, error) {
if f.match == "" {
f.match = string(f.Regexp.Find(p))
}
return len(p), nil
}
func Handler(w http.ResponseWriter, r *http.Request) {
f := &Finder{
Regexp: regexp.MustCompile("Title: ([a-zA-Z0-9]+)"),
}
r.Body = ioutil.NopCloser(io.TeeReader(r.Body, f))
//pass request to another server
fmt.Println(f.match)
}
答案 1 :(得分:0)
这是我的解决方案。我在响应主体和它的阅读器之间放置了一个管道,并用io.TeeReader
包裹了阅读器,以便在我从它读取时将其写入管道。我将其包裹在bufio.Scanner
中并扫描了几行。完成扫描线后,我确定要消耗主体的其余部分(使用io.Copy(ioutil.Discard, body)
),以便将主体的其余部分写入管道。
if request.Body == nil {
proxy(request)
return
}
// The body is *not* nil,
// so we're going to process it line-by-line.
bodySrc := request.Body // Original io source of the request body.
pr, pw := io.Pipe() // Pipe between bodySrc and request.Body.
body := io.TeeReader(bodySrc, pw) // When you read from body, it will read from bodySrc and writes to the pipe.
request.Body = ioutil.NopCloser(pr) // The other end of the pipe is request.Body. That's what proxy() will read.
go func() {
scanner = bufio.NewScanner(body)
for scanner.Scan() {
x := scanner.Bytes()
if processLine(x) {
break
}
}
// We're done with the body,
// so consume the rest of it and close the source and the pipe.
io.Copy(ioutil.Discard, body)
bodySrc.Close()
pw.Close()
}()
// As proxy reads request.Body, it's actually keeping up
// with the scanning done in the above goroutine.
proxy(request)