func serveError(req *web.Request, status int, reason os.Error, header web.Header) { header.Set(web.HeaderContentType, "text/plain; charset=utf-8") w := req.Responder.Respond(status, header) io.WriteString(w, web.StatusText(status)) if reason != nil { io.WriteString(w, "\n") io.WriteString(w, reason.String()) } }
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 }