github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/quic/gquic-go/h2quic/response.go (about)

     1  package h2quic
     2  
     3  import (
     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  
    17  var errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
    18  var noBody = ioutil.NopCloser(bytes.NewReader(nil))
    19  
    20  // from the handleResponse function
    21  func 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
    65  func 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.
    81  func 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  }