示例#1
0
文件: ssdp.go 项目: ronindev/dms
func ReadRequest(b *bufio.Reader) (req *http.Request, err error) {
	tp := textproto.NewReader(b)
	var s string
	if s, err = tp.ReadLine(); err != nil {
		return nil, err
	}
	defer func() {
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
	}()

	var f []string
	// TODO a split that only allows N values?
	if f = strings.SplitN(s, " ", 3); len(f) < 3 {
		return nil, &badStringError{"malformed request line", s}
	}
	if f[1] != "*" {
		return nil, &badStringError{"bad URL request", f[1]}
	}
	req = &http.Request{
		Method: f[0],
	}
	var ok bool
	if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(strings.TrimSpace(f[2])); !ok {
		return nil, &badStringError{"malformed HTTP version", f[2]}
	}

	mimeHeader, err := tp.ReadMIMEHeader()
	if err != nil {
		return nil, err
	}
	req.Header = http.Header(mimeHeader)
	return
}
示例#2
0
// newStream is used to create a new serverStream from a SYN_STREAM frame.
func (conn *connV2) newStream(frame *synStreamFrameV2, priority Priority) *serverStreamV2 {
	stream := new(serverStreamV2)
	stream.conn = conn
	stream.streamID = frame.StreamID
	stream.requestBody = new(bytes.Buffer)
	stream.state = new(StreamState)
	stream.output = conn.output[priority]
	// stream.request initialised below
	stream.handler = conn.server.Handler
	if stream.handler == nil {
		stream.handler = http.DefaultServeMux
	}
	stream.header = make(http.Header)
	stream.unidirectional = frame.Flags.UNIDIRECTIONAL()
	stream.responseCode = 0
	stream.ready = make(chan struct{})
	stream.stop = conn.stop
	stream.wroteHeader = false
	stream.priority = priority

	if frame.Flags.FIN() {
		close(stream.ready)
		stream.state.CloseThere()
	}

	header := frame.Header
	rawUrl := header.Get("scheme") + "://" + header.Get("host") + header.Get("url")

	url, err := url.Parse(rawUrl)
	if err != nil {
		log.Println("Error: Received SYN_STREAM with invalid request URL: ", err)
		return nil
	}

	vers := header.Get("version")
	major, minor, ok := http.ParseHTTPVersion(vers)
	if !ok {
		log.Println("Error: Invalid HTTP version: " + vers)
		return nil
	}

	method := header.Get("method")

	// Build this into a request to present to the Handler.
	stream.request = &http.Request{
		Method:     method,
		URL:        url,
		Proto:      vers,
		ProtoMajor: major,
		ProtoMinor: minor,
		RemoteAddr: conn.remoteAddr,
		Header:     header,
		Host:       url.Host,
		RequestURI: url.Path,
		TLS:        conn.tlsState,
		Body:       &readCloser{stream.requestBody},
	}

	return stream
}
示例#3
0
func makeRequest(params map[string]string) (*http.Request, error) {
	r := new(http.Request)
	r.Method = params["METHOD"]
	if r.Method == "" {
		return nil, errors.New("mongrel2: no METHOD")
	}

	r.Proto = params["VERSION"]
	var ok bool
	r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
	if !ok {
		return nil, errors.New("mongrel2: invalid protocol version")
	}

	r.Trailer = http.Header{}
	r.Header = http.Header{}

	r.Host = params["Host"]
	r.Header.Set("Referer", params["Referer"])
	r.Header.Set("User-Agent", params["User-Agent"])

	if lenstr := params["Content-Length"]; lenstr != "" {
		clen, err := strconv.ParseInt(lenstr, 10, 64)
		if err != nil {
			return nil, errors.New("mongrel2: bad Content-Length")
		}
		r.ContentLength = clen
	}

	for k, v := range params {
		if !skipHeader[k] {
			r.Header.Add(k, v)
		}
	}

	// TODO: cookies

	if r.Host != "" {
		url_, err := url.Parse("http://" + r.Host + params["URI"])
		if err != nil {
			return nil, errors.New("mongrel2: failed to parse host and URI into a URL")
		}
		r.URL = url_
	}
	if r.URL == nil {
		url_, err := url.Parse(params["URI"])
		if err != nil {
			return nil, errors.New("mongrel2: failed to parse URI into a URL")
		}
		r.URL = url_
	}

	// TODO: how do we know if we're using HTTPS?
	// TODO: fill in r.RemoteAddr

	return r, nil
}
示例#4
0
// WithProto sets HTTP protocol version.
//
// proto should have form of "HTTP/{major}.{minor}", e.g. "HTTP/1.1".
//
// Example:
//  req := NewRequest(config, "PUT", "http://example.com/path")
//  req.WithProto("HTTP/2.0")
func (r *Request) WithProto(proto string) *Request {
	major, minor, ok := http.ParseHTTPVersion(proto)
	if !ok {
		r.chain.fail(
			"\nunexpected protocol version %q, expected \"HTTP/{major}.{minor}\"",
			proto)
		return r
	}
	r.http.ProtoMajor = major
	r.http.ProtoMinor = minor
	return r
}
示例#5
0
文件: request.go 项目: kr/spdy
// ReadRequest reads an HTTP request. The header is taken from h,
// which must include the SPDY-specific fields starting with ':'.
// If r is not nil, the body will be read from r. If t is not nil,
// the trailer will be taken from t after the body is finished.
func ReadRequest(h, t http.Header, r io.Reader) (*http.Request, error) {
	req := new(http.Request)
	req.Header = make(http.Header)
	copyHeader(req.Header, h)
	path := h.Get(":path")
	if path == "" {
		return nil, errors.New("missing path")
	}
	if path[0] != '/' {
		return nil, errors.New("invalid path: " + path)
	}
	req.URL = &url.URL{
		Scheme: h.Get(":scheme"),
		Path:   path,
		Host:   h.Get(":host"),
	}
	req.Close = true
	req.Method = h.Get(":method")
	req.Host = h.Get(":host")
	req.Proto = h.Get(":version")
	var ok bool
	if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(req.Proto); !ok {
		return nil, errors.New("bad http version: " + req.Proto)
	}
	req.Header.Del("Host")

	cl := strings.TrimSpace(req.Header.Get("Content-Length"))
	if cl != "" {
		n, err := parseContentLength(cl)
		if err != nil {
			return nil, err
		}
		req.ContentLength = n
	} else {
		// Assume GET request has no body by default.
		if req.Method != "GET" {
			req.ContentLength = -1
		}
		req.Header.Del("Content-Length")
	}

	// TODO(kr): content length / limit reader?
	if r == nil {
		r = eofReader
	}
	if t != nil {
		req.Body = &body{r: r, hdr: req, trailer: t}
	} else {
		req.Body = &body{r: r}
	}
	return req, nil
}
示例#6
0
文件: httplib.go 项目: huyan/beego
// Set the protocol version for incoming requests.
// Client requests always use HTTP/1.1.
func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest {
	if len(vers) == 0 {
		vers = "HTTP/1.1"
	}

	major, minor, ok := http.ParseHTTPVersion(vers)
	if ok {
		b.req.Proto = vers
		b.req.ProtoMajor = major
		b.req.ProtoMinor = minor
	}

	return b
}
示例#7
0
文件: httplib.go 项目: nafrente/gogs
// Set the protocol version for incoming requests.
// Client requests always use HTTP/1.1.
func (r *Request) SetProtocolVersion(vers string) *Request {
	if len(vers) == 0 {
		vers = "HTTP/1.1"
	}

	major, minor, ok := http.ParseHTTPVersion(vers)
	if ok {
		r.req.Proto = vers
		r.req.ProtoMajor = major
		r.req.ProtoMinor = minor
	}

	return r
}
示例#8
0
文件: vlt.go 项目: raymondbutcher/vlt
func (req *Request) SendRequest(target_host string) {

	req_url, err := req.GetURL(target_host)
	if err != nil {
		log.Print(err)
		return
	}

	major, minor, ok := http.ParseHTTPVersion(req.Protocol)
	if !ok {
		log.Printf("Unknown protocol: %s\n", req.Protocol)
		return
	}

	original_host := req.GetHost()

	http_req := &http.Request{
		Method:     req.Method,
		URL:        req_url,
		Proto:      req.Protocol,
		ProtoMajor: major,
		ProtoMinor: minor,
		Header:     *req.Headers,
		Host:       original_host,
	}

	start := time.Now()

	// Use the lower level Transport.RoundTrip
	// to avoid http.Client's redirect handling.
	http_resp, err := http.DefaultTransport.RoundTrip(http_req)

	elapsed := time.Since(start) / time.Millisecond

	// Ensure that negative numbers are not displayed. This can happen in virtual
	// machines. There is no monononic clock functionality in Go at this time, so
	// for now I will just ensure that everything shows as 1 millisecond or more.
	if elapsed < 1 {
		elapsed = 1
	}

	if err == nil {
		req_url.Host = original_host
		log.Printf("[%dms] [%d] %s %s\n", elapsed, http_resp.StatusCode, req.Method, req_url)
	} else {
		log.Printf("[%dms] [%s] %s %s\n", elapsed, err, req.Method, req_url)
	}

}
示例#9
0
// newStream is used to create a new serverStream from a SYN_STREAM frame.
func (conn *connV3) newStream(frame *synStreamFrameV3, output chan<- Frame) *serverStreamV3 {
	stream := new(serverStreamV3)
	stream.conn = conn
	stream.streamID = frame.StreamID
	stream.state = new(StreamState)
	stream.output = output
	stream.header = make(http.Header)
	stream.unidirectional = frame.Flags.UNIDIRECTIONAL()
	stream.stop = conn.stop

	if frame.Flags.FIN() {
		stream.state.CloseThere()
	}

	header := frame.Header
	rawUrl := header.Get(":scheme") + "://" + header.Get(":host") + header.Get(":path")

	url, err := url.Parse(rawUrl)
	if err != nil {
		log.Println("Error: Received SYN_STREAM with invalid request URL: ", err)
		return nil
	}

	vers := header.Get(":version")
	major, minor, ok := http.ParseHTTPVersion(vers)
	if !ok {
		log.Println("Error: Invalid HTTP version: " + vers)
		return nil
	}

	method := header.Get(":method")

	// Build this into a request to present to the Handler.
	stream.request = &http.Request{
		Method:     method,
		URL:        url,
		Proto:      vers,
		ProtoMajor: major,
		ProtoMinor: minor,
		RemoteAddr: conn.remoteAddr,
		Header:     header,
		Host:       url.Host,
		RequestURI: url.Path,
		TLS:        conn.tlsState,
	}

	return stream
}
示例#10
0
文件: child.go 项目: zbzzbd/go
func  RequestFromMap(params  map[string]string) (*http.Request, error) {
	
	r:= new (http.Request)
	r.Method =params["REQUEST_METHOD"]
	if r.Method ==""{
		return nil ,errors.New("cgi:no REQUEST_METHOD in enviroment")
	}

	r.Proto = params["SERVER_PROTOCOL"]

	var ok bool
	r.ProtoMajor, r.ProtoMinor ,ok = http.ParseHTTPVersion(r.Proto)

    if  !ok {
    	return nil,errors.New("cgi:invalid SERVER_PROTOCOL version")
    }

    r.Close = true
    r.Trailer = http.Header{}
    r.Header = http.Header{}
    r.Host = params["HTTP_HOST"]

    if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
    	clen ,err := strconv.ParseInt(lenstr, 10, 64)
    	if  err !=nil {
    		return nil ,errors.New("cgi:bad CONTENT_LENTH in environment :"+ lenstr)
    	}
    	r.ContentLength = clen 
    }
    if ct := params["CONTENT_TYPE"];ct != ""{
    	r.Header.Set("Content-Type", ct)
    }

    for k,v:=range params {
    	if !strings.HasPrefix(k, "HTTP_"|| K == "HTTP_HOST"){
    		continue
    	}
    	r.Header.Add(strings.Replace(k[5:],"_","-", -1), v)
    }
    uriStr = params["SCRIPT_NAME"] +params["PATH_INFO"]
    s:=params["QUERY_STRING"]
    if s!=""{
    	uriStr +="?"+s
    }


}
示例#11
0
// HTTP1 parses the first line or upto 4096 bytes of the request to see if
// the conection contains an HTTP request.
func HTTP1() Matcher {
	return func(r io.Reader) bool {
		br := bufio.NewReader(&io.LimitedReader{R: r, N: maxHTTPRead})
		l, part, err := br.ReadLine()
		if err != nil || part {
			return false
		}

		_, _, proto, ok := parseRequestLine(string(l))
		if !ok {
			return false
		}

		v, _, ok := http.ParseHTTPVersion(proto)
		return ok && v == 1
	}
}
示例#12
0
文件: conn.go 项目: vonwenm/spdy
// newStream is used to create a new serverStream from a SYN_STREAM frame.
func (c *Conn) newStream(frame *frames.SYN_STREAM) *ResponseStream {
	header := frame.Header
	rawUrl := header.Get(":scheme") + "://" + header.Get(":host") + header.Get(":path")

	url, err := url.Parse(rawUrl)
	if c.check(err != nil, "Received SYN_STREAM with invalid request URL (%v)", err) {
		return nil
	}

	vers := header.Get(":version")
	major, minor, ok := http.ParseHTTPVersion(vers)
	if c.check(!ok, "Invalid HTTP version: "+vers) {
		return nil
	}

	method := header.Get(":method")

	// Build this into a request to present to the Handler.
	request := &http.Request{
		Method:     method,
		URL:        url,
		Proto:      vers,
		ProtoMajor: major,
		ProtoMinor: minor,
		RemoteAddr: c.remoteAddr,
		Header:     header,
		Host:       url.Host,
		RequestURI: url.RequestURI(),
		TLS:        c.tlsState,
	}

	output := c.output[frame.Priority]
	c.streamCreation.Lock()
	out := NewResponseStream(c, frame, output, c.server.Handler, request)
	c.streamCreation.Unlock()
	c.flowControlLock.Lock()
	f := c.flowControl
	c.flowControlLock.Unlock()
	out.AddFlowControl(f)

	return out
}
示例#13
0
func awaitRequestState(d *httpDecoder) httpState {
	log.V(2).Infoln(d.idtag + "await-request-state")
	tr := textproto.NewReader(d.rw.Reader)
	if !d.setReadTimeoutOrFail() {
		return terminateState
	}
	requestLine, err := tr.ReadLine()
	if requestLine == "" && isGracefulTermSignal(err) {
		// we're actually expecting this at some point, so don't react poorly
		return gracefulTerminateState
	}
	if next, ok := d.checkTimeoutOrFail(err, awaitRequestState); ok {
		return next
	}
	ss := strings.SplitN(requestLine, " ", 3)
	if len(ss) < 3 {
		if err == io.EOF {
			return gracefulTerminateState
		}
		d.sendError(errors.New(d.idtag + "illegal request line"))
		return terminateState
	}
	r := d.req
	r.Method = ss[0]
	r.RequestURI = ss[1]
	r.URL, err = url.ParseRequestURI(ss[1])
	if err != nil {
		d.sendError(err)
		return terminateState
	}
	major, minor, ok := http.ParseHTTPVersion(ss[2])
	if !ok {
		d.sendError(errors.New(d.idtag + "malformed HTTP version"))
		return terminateState
	}
	r.ProtoMajor = major
	r.ProtoMinor = minor
	r.Proto = ss[2]
	return readHeaderState
}
示例#14
0
// handlePush performs the processing of SYN_STREAM frames forming a server push.
func (c *Conn) handlePush(frame *frames.SYN_STREAM) {
	sid := frame.StreamID

	// Check stream creation is allowed.
	c.goawayLock.Lock()
	goaway := c.goawayReceived || c.goawaySent
	c.goawayLock.Unlock()
	if goaway || c.Closed() {
		return
	}

	if c.server != nil {
		log.Println("Error: Only clients can receive server pushes.")
		return
	}

	if c.check(sid&1 != 0, "Received SYN_STREAM with odd Stream ID %d", sid) {
		return
	}

	c.lastPushStreamIDLock.Lock()
	lsid := c.lastPushStreamID
	c.lastPushStreamIDLock.Unlock()
	if c.check(sid <= lsid, "Received SYN_STREAM with Stream ID %d, less than %d", sid, lsid) {
		return
	}

	if c.criticalCheck(!sid.Valid(), sid, "Received SYN_STREAM with excessive Stream ID %d", sid) {
		return
	}

	// Stream ID is fine.

	// Check stream limit would allow the new stream.
	if !c.pushStreamLimit.Add() {
		c._RST_STREAM(sid, common.RST_STREAM_REFUSED_STREAM)
		return
	}

	ok := frame.Priority.Valid(3)
	if c.criticalCheck(!ok, sid, "Received SYN_STREAM with invalid priority %d", frame.Priority) {
		return
	}

	// Parse the request.
	header := frame.Header
	rawUrl := header.Get(":scheme") + "://" + header.Get(":host") + header.Get(":path")
	url, err := url.Parse(rawUrl)
	if c.check(err != nil, "Received SYN_STREAM with invalid request URL (%v)", err) {
		return
	}

	vers := header.Get(":version")
	major, minor, ok := http.ParseHTTPVersion(vers)
	if c.check(!ok, "Invalid HTTP version: "+vers) {
		return
	}

	method := header.Get(":method")
	request := &http.Request{
		Method:     method,
		URL:        url,
		Proto:      vers,
		ProtoMajor: major,
		ProtoMinor: minor,
		RemoteAddr: c.remoteAddr,
		Header:     header,
		Host:       url.Host,
		RequestURI: url.RequestURI(),
		TLS:        c.tlsState,
	}

	// Check whether the receiver wants this resource.
	if c.PushReceiver != nil && !c.PushReceiver.ReceiveRequest(request) {
		c._RST_STREAM(sid, common.RST_STREAM_REFUSED_STREAM)
		return
	}

	if c.PushReceiver != nil {
		c.pushRequests[sid] = request
		c.lastPushStreamIDLock.Lock()
		c.lastPushStreamID = sid
		c.lastPushStreamIDLock.Unlock()
		c.PushReceiver.ReceiveHeader(request, frame.Header)
	}
}
示例#15
0
func main() {
	fmt.Println(http.ParseHTTPVersion(strings.TrimSpace("HTTP/1.1 ")))
}
示例#16
0
// handlePush performs the processing of SYN_STREAM frames forming a server push.
func (conn *connV3) handlePush(frame *synStreamFrameV3) {
	conn.Lock()

	// Check stream creation is allowed.
	if conn.goawayReceived || conn.goawaySent || conn.closed() {
		conn.Unlock()
		return
	}

	sid := frame.StreamID

	// Push.
	if conn.server != nil {
		log.Println("Error: Only clients can receive server pushes.")
		conn.Unlock()
		return
	}

	// Check Stream ID is even.
	if sid&1 != 0 {
		log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be even.\n", sid)
		conn.numBenignErrors++
		conn.Unlock()
		return
	}

	// Check Stream ID is the right number.
	lsid := conn.lastPushStreamID
	if sid <= lsid {
		log.Printf("Error: Received SYN_STREAM with Stream ID %d, which should be greater than %d.\n", sid, lsid)
		conn.numBenignErrors++
		conn.Unlock()
		return
	}

	// Check Stream ID is not out of bounds.
	if !sid.Valid() {
		log.Printf("Error: Received SYN_STREAM with Stream ID %d, which exceeds the limit.\n", sid)
		conn.Unlock()
		conn.protocolError(sid)
		return
	}

	// Stream ID is fine.

	// Check stream limit would allow the new stream.
	if !conn.pushStreamLimit.Add() {
		rst := new(rstStreamFrameV3)
		rst.StreamID = sid
		rst.Status = RST_STREAM_REFUSED_STREAM
		conn.output[0] <- rst
		conn.Unlock()
		return
	}

	if !frame.Priority.Valid(3) {
		log.Printf("Error: Received SYN_STREAM with invalid priority %d.\n", frame.Priority)
		conn.Unlock()
		conn.protocolError(sid)
		return
	}

	// Parse the request.
	header := frame.Header
	rawUrl := header.Get(":scheme") + "://" + header.Get(":host") + header.Get(":path")
	url, err := url.Parse(rawUrl)
	if err != nil {
		log.Println("Error: Received SYN_STREAM with invalid request URL: ", err)
		conn.Unlock()
		return
	}

	vers := header.Get(":version")
	major, minor, ok := http.ParseHTTPVersion(vers)
	if !ok {
		log.Println("Error: Invalid HTTP version: " + vers)
		conn.Unlock()
		return
	}

	method := header.Get(":method")

	request := &http.Request{
		Method:     method,
		URL:        url,
		Proto:      vers,
		ProtoMajor: major,
		ProtoMinor: minor,
		RemoteAddr: conn.remoteAddr,
		Header:     header,
		Host:       url.Host,
		RequestURI: url.Path,
		TLS:        conn.tlsState,
	}

	// Check whether the receiver wants this resource.
	if conn.pushReceiver != nil && !conn.pushReceiver.ReceiveRequest(request) {
		rst := new(rstStreamFrameV3)
		rst.StreamID = sid
		rst.Status = RST_STREAM_REFUSED_STREAM
		conn.output[0] <- rst
		conn.Unlock()
		return
	}

	// Create and start new stream.
	if conn.pushReceiver != nil {
		conn.pushRequests[sid] = request
		conn.lastPushStreamID = sid
		conn.Unlock()
		conn.pushReceiver.ReceiveHeader(request, frame.Header)
	} else {
		conn.Unlock()
	}
}
示例#17
0
// RequestFromMap creates an http.Request from CGI variables.
// The returned Request's Body field is not populated.
func RequestFromMap(params map[string]string) (*http.Request, error) {
	r := new(http.Request)
	r.Method = params["REQUEST_METHOD"]
	if r.Method == "" {
		return nil, errors.New("cgi: no REQUEST_METHOD in environment")
	}

	r.Proto = params["SERVER_PROTOCOL"]
	var ok bool
	r.ProtoMajor, r.ProtoMinor, ok = http.ParseHTTPVersion(r.Proto)
	if !ok {
		return nil, errors.New("cgi: invalid SERVER_PROTOCOL version")
	}

	r.Close = true
	r.Trailer = http.Header{}
	r.Header = http.Header{}

	r.Host = params["HTTP_HOST"]

	if lenstr := params["CONTENT_LENGTH"]; lenstr != "" {
		clen, err := strconv.ParseInt(lenstr, 10, 64)
		if err != nil {
			return nil, errors.New("cgi: bad CONTENT_LENGTH in environment: " + lenstr)
		}
		r.ContentLength = clen
	}

	if ct := params["CONTENT_TYPE"]; ct != "" {
		r.Header.Set("Content-Type", ct)
	}

	// Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers
	for k, v := range params {
		if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" {
			continue
		}
		r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v)
	}

	// TODO: cookies.  parsing them isn't exported, though.

	if r.Host != "" {
		// Hostname is provided, so we can reasonably construct a URL,
		// even if we have to assume 'http' for the scheme.
		rawurl := "http://" + r.Host + params["REQUEST_URI"]
		url, err := url.Parse(rawurl)
		if err != nil {
			return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl)
		}
		r.URL = url
	}
	// Fallback logic if we don't have a Host header or the URL
	// failed to parse
	if r.URL == nil {
		uriStr := params["REQUEST_URI"]
		url, err := url.Parse(uriStr)
		if err != nil {
			return nil, errors.New("cgi: failed to parse REQUEST_URI into a URL: " + uriStr)
		}
		r.URL = url
	}

	// There's apparently a de-facto standard for this.
	// http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636
	if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" {
		r.TLS = &tls.ConnectionState{HandshakeComplete: true}
	}

	// Request.RemoteAddr has its port set by Go's standard http
	// server, so we do here too. We don't have one, though, so we
	// use a dummy one.
	r.RemoteAddr = net.JoinHostPort(params["REMOTE_ADDR"], "0")

	return r, nil
}
示例#18
0
文件: response.go 项目: kr/spdy
// ReadResponse reads an HTTP response. The header is taken from h,
// which must include the SPDY-specific fields starting with ':'.
// If r is not nil, the body will be read from r. If t is not nil,
// the trailer will be taken from t after the body is finished.
func ReadResponse(h, t http.Header, r io.Reader, req *http.Request) (*http.Response, error) {
	for _, s := range badRespHeaderFields {
		if _, ok := h[s]; ok {
			return nil, &badStringError{"invalid header field", s}
		}
	}

	var err error
	resp := new(http.Response)
	resp.Request = req
	resp.Close = true
	resp.Header = make(http.Header)
	copyHeader(resp.Header, h)
	f := strings.SplitN(h.Get(":status"), " ", 2)
	var s string
	if len(f) > 1 {
		s = f[1]
	}
	resp.Status = f[0] + " " + s
	resp.StatusCode, err = strconv.Atoi(f[0])
	if err != nil {
		return nil, &badStringError{"malformed HTTP status code", f[0]}
	}
	resp.Proto = h.Get(":version")
	var ok bool
	resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto)
	if !ok {
		return nil, &badStringError{"malformed HTTP version", resp.Proto}
	}

	realLength, err := fixLength(true, resp.StatusCode, req.Method, resp.Header)
	if err != nil {
		return nil, err
	}
	if req.Method == "HEAD" {
		if n, err := parseContentLength(h.Get("Content-Length")); err != nil {
			return nil, err
		} else {
			resp.ContentLength = n
		}
	} else {
		resp.ContentLength = realLength
	}

	switch {
	case realLength == 0:
		r = eofReader
	case realLength > 0:
		if r == nil {
			// TODO(kr): return error
		}
		r = io.LimitReader(r, realLength)
	}
	if r == nil {
		r = eofReader
	}
	body := &body{r: r}
	resp.Body = body
	if t != nil {
		body.hdr = resp
		body.trailer = t
	}
	return resp, nil
}