112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
|
package h2quic
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"net/textproto"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/net/http2"
|
||
|
)
|
||
|
|
||
|
// copied from net/http2/transport.go
|
||
|
|
||
|
var errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
|
||
|
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
|
||
|
|
||
|
// from the handleResponse function
|
||
|
func responseFromHeaders(f *http2.MetaHeadersFrame) (*http.Response, error) {
|
||
|
if f.Truncated {
|
||
|
return nil, errResponseHeaderListSize
|
||
|
}
|
||
|
|
||
|
status := f.PseudoValue("status")
|
||
|
if status == "" {
|
||
|
return nil, errors.New("missing status pseudo header")
|
||
|
}
|
||
|
statusCode, err := strconv.Atoi(status)
|
||
|
if err != nil {
|
||
|
return nil, errors.New("malformed non-numeric status pseudo header")
|
||
|
}
|
||
|
|
||
|
if statusCode == 100 {
|
||
|
// TODO: handle this
|
||
|
|
||
|
// traceGot100Continue(cs.trace)
|
||
|
// if cs.on100 != nil {
|
||
|
// cs.on100() // forces any write delay timer to fire
|
||
|
// }
|
||
|
// cs.pastHeaders = false // do it all again
|
||
|
// return nil, nil
|
||
|
}
|
||
|
|
||
|
header := make(http.Header)
|
||
|
res := &http.Response{
|
||
|
Proto: "HTTP/2.0",
|
||
|
ProtoMajor: 2,
|
||
|
Header: header,
|
||
|
StatusCode: statusCode,
|
||
|
Status: status + " " + http.StatusText(statusCode),
|
||
|
}
|
||
|
for _, hf := range f.RegularFields() {
|
||
|
key := http.CanonicalHeaderKey(hf.Name)
|
||
|
if key == "Trailer" {
|
||
|
t := res.Trailer
|
||
|
if t == nil {
|
||
|
t = make(http.Header)
|
||
|
res.Trailer = t
|
||
|
}
|
||
|
foreachHeaderElement(hf.Value, func(v string) {
|
||
|
t[http.CanonicalHeaderKey(v)] = nil
|
||
|
})
|
||
|
} else {
|
||
|
header[key] = append(header[key], hf.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return res, nil
|
||
|
}
|
||
|
|
||
|
// continuation of the handleResponse function
|
||
|
func setLength(res *http.Response, isHead, streamEnded bool) *http.Response {
|
||
|
if !streamEnded || isHead {
|
||
|
res.ContentLength = -1
|
||
|
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
||
|
if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
||
|
res.ContentLength = clen64
|
||
|
} else {
|
||
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
||
|
// more safe smuggling-wise to ignore.
|
||
|
}
|
||
|
} else if len(clens) > 1 {
|
||
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
||
|
// more safe smuggling-wise to ignore.
|
||
|
}
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
// copied from net/http/server.go
|
||
|
|
||
|
// foreachHeaderElement splits v according to the "#rule" construction
|
||
|
// in RFC 2616 section 2.1 and calls fn for each non-empty element.
|
||
|
func foreachHeaderElement(v string, fn func(string)) {
|
||
|
v = textproto.TrimString(v)
|
||
|
if v == "" {
|
||
|
return
|
||
|
}
|
||
|
if !strings.Contains(v, ",") {
|
||
|
fn(v)
|
||
|
return
|
||
|
}
|
||
|
for _, f := range strings.Split(v, ",") {
|
||
|
if f = textproto.TrimString(f); f != "" {
|
||
|
fn(f)
|
||
|
}
|
||
|
}
|
||
|
}
|