// ReadRequest reads the data from the request into the raw.Data. func (data *Data) ReadRequest(ctx context.Context, r *http.Request) error { if cl := r.Header.Get("Content-Length"); cl != "" { v, err := strconv.ParseInt(cl, 10, 64) if err != nil || v < 0 { return log.Warn("invalid content-length", log.WithValue("content-length", cl), log.WithContext(ctx), log.WithStatusBadRequest()) } if v >= int64(MaxLen) { return log.Warn("max length excceeded", log.WithContext(ctx), log.WithStatusBadRequest(), log.WithValue("MaxLen", MaxLen)) } buf := make([]byte, v) _, err = io.ReadFull(r.Body, buf) if err != nil { return log.Warn("cannot read content", log.WithContext(ctx), log.WithError(err), log.WithStatusBadRequest()) } data.Content = buf } else { reader := io.LimitReader(r.Body, int64(MaxLen)) content, err := ioutil.ReadAll(reader) if err != nil { return err } if len(content) >= MaxLen { return log.Warn("max size exceeded", log.WithContext(ctx), log.WithStatusBadRequest(), log.WithValue("MaxLen", MaxLen)) } data.Content = content } // The HTTP specification does not mention Content-Encoding for // requests, but sometimes it is handy to allow the client to do // so. if ce := r.Header.Get("Content-Encoding"); ce != "" { data.ContentEncoding = ce data.UncompressedLength = 0 // not known } else { data.UncompressedLength = len(data.Content) data.ContentEncoding = ceIdentity } data.ContentType = r.Header.Get("Content-Type") if data.ContentType == "" { data.ContentType = "application/octet-stream" } return nil }
func ExampleWithValue(n1, n2 int) error { if err := doSomethingWith(n1, n2); err != nil { return log.Error("doSomethingWith failed", log.WithValue("n1", n1), log.WithValue("n2", n2)) } // ... more processing and then ... return nil }
func ExampleOption(ctx context.Context, n1, n2 int) error { if err := doSomethingWith(n1, n2); err != nil { return log.Error("cannot doSomething", log.WithValue("n1", n1), log.WithValue("n2", n2), log.WithError(err), log.WithContext(ctx)) } // .. more processing and then ... return nil }
func (data *Data) Decompress() error { if !data.IsCompressed() { return nil } input := bytes.NewBuffer(data.Content) var reader io.Reader if data.ContentEncoding == ceDeflate { reader = flate.NewReader(input) } else if data.ContentEncoding == ceGzip { var err error if reader, err = gzip.NewReader(input); err != nil { return err } } else { return log.Error("unknown content-encoding", log.WithValue("content-encoding", data.ContentEncoding)) } writer := bytes.Buffer{} _, err := io.Copy(&writer, reader) if err != nil { return err } data.Content = writer.Bytes() data.ContentEncoding = "" data.UncompressedLength = len(data.Content) return nil }
// WriteResponse writes the contents to the client as a response. func (data *Data) WriteResponse(ctx context.Context, w http.ResponseWriter, r *http.Request) error { // TODO: this is a very naive handling of the Accept-Encoding // header. In particular it does not handle deflate;q=0, which is // a valid way of saying that deflate is not acceptable. if data.IsCompressed() { if ae := r.Header.Get("Accept-Encoding"); !strings.Contains(ae, data.ContentEncoding) { // the user agent does not accept the content encoding, so we // have to decompress before sending err := data.Decompress() if err != nil { return err } } } if len(data.Content) == 0 { w.Header().Set("Content-Length", "0") w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") w.WriteHeader(http.StatusNoContent) return nil } if data.IsCompressed() { w.Header().Set("Content-Encoding", data.ContentEncoding) } else { w.Header().Del("Content-Encoding") } w.Header().Set("Content-Type", data.ContentType) w.Header().Set("Content-Length", strconv.Itoa(len(data.Content))) n, err := w.Write(data.Content) if err != nil { // Failed to write, but do not return an error code, as there // is no way to return an error message to the client after // writing has started. log.Warn("cannot write response", log.WithError(err), log.WithContext(ctx)) } if n != len(data.Content) { log.Warn("not all bytes sent", log.WithValue("expected", len(data.Content)), log.WithValue("actual", n), log.WithContext(ctx)) } return nil }