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 }
// 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 }
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 }
// 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 }
// 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 }
// 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 }
// 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 }
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) } }
// 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 }
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 } }
// 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 } }
// 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 }
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 }
// 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) } }
func main() { fmt.Println(http.ParseHTTPVersion(strings.TrimSpace("HTTP/1.1 "))) }
// 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() } }
// 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 }
// 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 }