1package h2quic
2
3import (
4	"bytes"
5	"errors"
6	"io/ioutil"
7	"net/http"
8	"net/textproto"
9	"strconv"
10	"strings"
11
12	"golang.org/x/net/http2"
13)
14
15// copied from net/http2/transport.go
16
17var errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
18var noBody = ioutil.NopCloser(bytes.NewReader(nil))
19
20// from the handleResponse function
21func responseFromHeaders(f *http2.MetaHeadersFrame) (*http.Response, error) {
22	if f.Truncated {
23		return nil, errResponseHeaderListSize
24	}
25
26	status := f.PseudoValue("status")
27	if status == "" {
28		return nil, errors.New("missing status pseudo header")
29	}
30	statusCode, err := strconv.Atoi(status)
31	if err != nil {
32		return nil, errors.New("malformed non-numeric status pseudo header")
33	}
34
35	// TODO: handle statusCode == 100
36
37	header := make(http.Header)
38	res := &http.Response{
39		Proto:      "HTTP/2.0",
40		ProtoMajor: 2,
41		Header:     header,
42		StatusCode: statusCode,
43		Status:     status + " " + http.StatusText(statusCode),
44	}
45	for _, hf := range f.RegularFields() {
46		key := http.CanonicalHeaderKey(hf.Name)
47		if key == "Trailer" {
48			t := res.Trailer
49			if t == nil {
50				t = make(http.Header)
51				res.Trailer = t
52			}
53			foreachHeaderElement(hf.Value, func(v string) {
54				t[http.CanonicalHeaderKey(v)] = nil
55			})
56		} else {
57			header[key] = append(header[key], hf.Value)
58		}
59	}
60
61	return res, nil
62}
63
64// continuation of the handleResponse function
65func setLength(res *http.Response, isHead, streamEnded bool) *http.Response {
66	if !streamEnded || isHead {
67		res.ContentLength = -1
68		if clens := res.Header["Content-Length"]; len(clens) == 1 {
69			if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
70				res.ContentLength = clen64
71			}
72		}
73	}
74	return res
75}
76
77// copied from net/http/server.go
78
79// foreachHeaderElement splits v according to the "#rule" construction
80// in RFC 2616 section 2.1 and calls fn for each non-empty element.
81func foreachHeaderElement(v string, fn func(string)) {
82	v = textproto.TrimString(v)
83	if v == "" {
84		return
85	}
86	if !strings.Contains(v, ",") {
87		fn(v)
88		return
89	}
90	for _, f := range strings.Split(v, ",") {
91		if f = textproto.TrimString(f); f != "" {
92			fn(f)
93		}
94	}
95}
96