Exemple #1
0
// 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
}
Exemple #2
0
// 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
}