func (c *conn) prepare() (err os.Error) { method, rawURL, version, err := parseRequestLine(c.br) if err != nil { return err } header, err := parseHeader(c.br) if err != nil { return err } url, err := http.ParseURL(rawURL) if err != nil { return err } if url.Host == "" { url.Host = header.GetDef(web.HeaderHost, "") if url.Host == "" { url.Host = c.serverName } } if c.secure { url.Scheme = "https" } else { url.Scheme = "http" } req, err := web.NewRequest(c.netConn.RemoteAddr().String(), method, url, version, header) if err != nil { return } c.req = req c.requestAvail = req.ContentLength if c.requestAvail < 0 { c.requestAvail = 0 } if s, found := req.Header.Get(web.HeaderExpect); found { c.write100Continue = strings.ToLower(s) == "100-continue" } connection := strings.ToLower(req.Header.GetDef(web.HeaderConnection, "")) if version >= web.ProtocolVersion(1, 1) { c.closeAfterResponse = connection == "close" } else if version == web.ProtocolVersion(1, 0) && req.ContentLength >= 0 { c.closeAfterResponse = connection != "keep-alive" } else { c.closeAfterResponse = true } req.Responder = c req.Body = requestReader{c} return nil }
func webRequestFromHTTPRequest(w http.ResponseWriter, r *http.Request) *web.Request { header := web.Header(map[string][]string(r.Header)) foo := header.Get("Cookie") if r.Referer != "" { header.Set(web.HeaderReferer, r.Referer) } if r.UserAgent != "" { header.Set(web.HeaderUserAgent, r.UserAgent) } req, _ := web.NewRequest( r.RemoteAddr, r.Method, r.URL, web.ProtocolVersion(r.ProtoMajor, r.ProtoMinor), header) req.Body = r.Body req.Responder = responder{w} req.ContentLength = int(r.ContentLength) if r.Form != nil { req.Param = web.Values(map[string][]string(r.Form)) } for _, c := range r.Cookie { req.Cookie.Add(c.Name, c.Value) } req.Env["foo"] = foo return req }
func webRequestFromHTTPRequest(w http.ResponseWriter, r *http.Request) *web.Request { header := web.Header(r.Header) url := *r.URL if url.Host == "" { url.Host = r.Host } req, _ := web.NewRequest( r.RemoteAddr, r.Method, url.RequestURI(), web.ProtocolVersion(r.ProtoMajor, r.ProtoMinor), &url, header) req.Body = r.Body req.Responder = responder{w} req.ContentLength = int(r.ContentLength) if r.Form != nil { req.Param = web.Values(r.Form) } req.Env["twister.adapter.request"] = r return req }
func readRequestLine(b *bufio.Reader) (method string, url string, version int, err os.Error) { p, err := b.ReadSlice('\n') if err != nil { if err == bufio.ErrBufferFull { err = web.ErrLineTooLong } return } m := requestLineRegexp.FindSubmatch(p) if m == nil { err = ErrBadRequestLine return } method = string(m[1]) major, err := strconv.Atoi(string(m[3])) if err != nil { return } minor, err := strconv.Atoi(string(m[4])) if err != nil { return } version = web.ProtocolVersion(major, minor) url = string(m[2]) return }
func readRequestLine(b *bufio.Reader) (method string, urlStr string, version int, err error) { var p []byte var isPrefix bool p, isPrefix, err = b.ReadLine() if isPrefix { err = web.ErrLineTooLong } if err != nil { return } method, p, err = nextWord(p) if err != nil { return } urlStr, p, err = nextWord(p) if err != nil { return } if !bytes.HasPrefix(p, httpslash) { err = errBadRequestLine return } var major int major, p, err = nextNum(p[len(httpslash):]) if err != nil { return } if len(p) == 0 || p[0] != '.' { err = errBadRequestLine return } var minor int minor, p, err = nextNum(p[1:]) if err != nil { return } if len(p) != 0 { err = errBadRequestLine return } version = web.ProtocolVersion(major, minor) return }
func readRequestLine(b *bufio.Reader) (method string, url string, version int, err os.Error) { var p []byte var isPrefix bool p, isPrefix, err = b.ReadLine() if isPrefix { err = web.ErrLineTooLong } if err != nil { return } m := requestLineRegexp.FindSubmatch(p) if m == nil { err = ErrBadRequestLine return } method = string(m[1]) major, err := strconv.Atoi(string(m[3])) if err != nil { return } minor, err := strconv.Atoi(string(m[4])) if err != nil { return } version = web.ProtocolVersion(major, minor) url = string(m[2]) return }
"testing" ) type parseTest struct { name string method string url string version int header web.StringsMap s string } var parseTests = []parseTest{ parseTest{"no body", "", "", 0, web.NewStringsMap(), "GET /foo HTTP/1.1\r\n"}, parseTest{"empty", "", "", 0, web.NewStringsMap(), " "}, parseTest{"simple", "GET", "/foo", web.ProtocolVersion(1, 1), web.NewStringsMap(), `GET /foo HTTP/1.1 `}, parseTest{"multihdr", "GET", "/foo", web.ProtocolVersion(1, 0), web.NewStringsMap( web.HeaderContentType, "text/html", web.HeaderCookie, "hello=world", web.HeaderCookie, "foo=bar"), `GET /foo HTTP/1.0 Content-Type: text/html CoOkie: hello=world Cookie: foo=bar `}, parseTest{"continuation", "GET", "/hello", web.ProtocolVersion(1, 1), web.NewStringsMap( web.HeaderContentType, "text/html",
func (c *conn) Respond(status int, header web.StringsMap) (body web.ResponseBody) { if c.hijacked { log.Stderr("twister: Respond called on hijacked connection") return nil } if c.respondCalled { log.Stderr("twister: multiple calls to Respond") return nil } c.respondCalled = true c.requestErr = web.ErrInvalidState if _, found := header.Get(web.HeaderTransferEncoding); found { log.Stderr("twister: transfer encoding not allowed") header[web.HeaderTransferEncoding] = nil, false } if c.requestAvail > 0 { c.closeAfterResponse = true } c.chunked = true c.responseAvail = 0 if status == web.StatusNotModified { header[web.HeaderContentType] = nil, false header[web.HeaderContentLength] = nil, false c.chunked = false } else if s, found := header.Get(web.HeaderContentLength); found { c.responseAvail, _ = strconv.Atoi(s) c.chunked = false } else if c.req.ProtocolVersion < web.ProtocolVersion(1, 1) { c.closeAfterResponse = true } if c.closeAfterResponse { header.Set(web.HeaderConnection, "close") c.chunked = false } if c.chunked { header.Set(web.HeaderTransferEncoding, "chunked") } proto := "HTTP/1.0" if c.req.ProtocolVersion >= web.ProtocolVersion(1, 1) { proto = "HTTP/1.1" } statusString := strconv.Itoa(status) text, found := web.StatusText[status] if !found { text = "status code " + statusString } var b bytes.Buffer b.WriteString(proto) b.WriteString(" ") b.WriteString(statusString) b.WriteString(" ") b.WriteString(text) b.WriteString("\r\n") for key, values := range header { for _, value := range values { b.WriteString(key) b.WriteString(": ") b.WriteString(cleanHeaderValue(value)) b.WriteString("\r\n") } } b.WriteString("\r\n") if c.chunked { c.bw = bufio.NewWriter(chunkedWriter{c}) _, c.responseErr = c.netConn.Write(b.Bytes()) } else { c.bw = bufio.NewWriter(identityWriter{c}) c.bw.Write(b.Bytes()) } return c.bw }
func (t *transaction) Respond(status int, header web.Header) (body io.Writer) { if t.hijacked { log.Println("twister: Respond called on hijacked connection") return &nullResponseBody{err: web.ErrInvalidState} } if t.respondCalled { log.Println("twister: Multiple calls to Respond") return &nullResponseBody{err: web.ErrInvalidState} } t.respondCalled = true t.requestErr = web.ErrInvalidState t.status = status t.header = header if te := header.Get(web.HeaderTransferEncoding); te != "" { log.Println("twister: transfer encoding not allowed") delete(header, web.HeaderTransferEncoding) } if !t.requestConsumed { t.closeAfterResponse = true } if header.Get(web.HeaderConnection) == "close" { t.closeAfterResponse = true } t.chunkedResponse = true contentLength := -1 if status == web.StatusNotModified { delete(header, web.HeaderContentType) delete(header, web.HeaderContentLength) t.chunkedResponse = false } else if s := header.Get(web.HeaderContentLength); s != "" { contentLength, _ = strconv.Atoi(s) t.chunkedResponse = false } else if t.req.ProtocolVersion < web.ProtocolVersion(1, 1) { t.closeAfterResponse = true } if t.closeAfterResponse { header.Set(web.HeaderConnection, "close") t.chunkedResponse = false } if t.req.Method == "HEAD" { t.chunkedResponse = false } if t.chunkedResponse { header.Set(web.HeaderTransferEncoding, "chunked") } proto := "HTTP/1.0" if t.req.ProtocolVersion >= web.ProtocolVersion(1, 1) { proto = "HTTP/1.1" } statusString := strconv.Itoa(status) text := web.StatusText(status) var b bytes.Buffer b.WriteString(proto) b.WriteString(" ") b.WriteString(statusString) b.WriteString(" ") b.WriteString(text) b.WriteString("\r\n") header.WriteHttpHeader(&b) t.headerSize = b.Len() const bufferSize = 4096 switch { case t.req.Method == "HEAD" || status == web.StatusNotModified: t.responseBody, _ = newNullResponseBody(t.conn, b.Bytes()) case t.chunkedResponse: t.responseBody, _ = newChunkedResponseBody(t.conn, b.Bytes(), bufferSize) default: t.responseBody, _ = newIdentityResponseBody(t.conn, b.Bytes(), bufferSize, contentLength) } return t.responseBody }
func (t *transaction) prepare() (err error) { method, requestURI, version, err := readRequestLine(t.br) if err != nil { return err } header := web.Header{} err = header.ParseHttpHeader(t.br) if err != nil { return err } u, err := url.ParseRequestURI(requestURI) if err != nil { return err } if u.Host == "" { u.Host = header.Get(web.HeaderHost) if u.Host == "" { u.Host = t.server.DefaultHost } } if t.server.Secure { u.Scheme = "https" } else { u.Scheme = "http" } req, err := web.NewRequest(t.conn.RemoteAddr().String(), method, requestURI, version, u, header) if err != nil { return } t.req = req if s := req.Header.Get(web.HeaderExpect); s != "" { t.write100Continue = strings.ToLower(s) == "100-continue" } connection := strings.ToLower(req.Header.Get(web.HeaderConnection)) if version >= web.ProtocolVersion(1, 1) { t.closeAfterResponse = connection == "close" } else if version == web.ProtocolVersion(1, 0) && req.ContentLength >= 0 { t.closeAfterResponse = connection != "keep-alive" } else { t.closeAfterResponse = true } req.Responder = t te := header.GetList(web.HeaderTransferEncoding) chunked := len(te) > 0 && te[0] == "chunked" switch { case req.Method == "GET" || req.Method == "HEAD": req.Body = identityReader{t} t.requestConsumed = true case chunked: req.Body = chunkedReader{t} case req.ContentLength >= 0: req.Body = identityReader{t} t.requestAvail = req.ContentLength t.requestConsumed = req.ContentLength == 0 default: req.Body = identityReader{t} t.closeAfterResponse = true } return nil }