blob: ab51c89322b643e32c74f28ac07c3c61e662bfe5 [file] [log] [blame]
/*
* Copyright 2014 The Kythe Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package httpencoding provides functions to transparently encode/decode HTTP bodies
package httpencoding
import (
"compress/gzip"
"compress/zlib"
"fmt"
"io"
"net/http"
"strings"
)
// CompressData returns a writer that writes encoded data to w. The chosen
// encoding is based on the Accept-Encoding header and defaults to the identity
// encoding.
func CompressData(w http.ResponseWriter, r *http.Request) io.WriteCloser {
encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",")
for _, encoding := range encodings {
switch encoding {
case "gzip":
w.Header().Set("Content-Encoding", "gzip")
return gzip.NewWriter(w)
case "deflate":
w.Header().Set("Content-Encoding", "deflate")
return zlib.NewWriter(w)
case "identity":
return noopCloser{w}
}
}
return noopCloser{w}
}
// UncompressData returns a reads that decodes data from r.Body. The encoding is
// determined based on the Content-Encoding header and an error is returned if
// the encoding is unknown.
func UncompressData(r *http.Response) (io.ReadCloser, error) {
encoding := r.Header.Get("Content-Encoding")
var (
cr io.ReadCloser
err error
)
switch encoding {
case "gzip":
cr, err = gzip.NewReader(r.Body)
case "deflate":
cr, err = zlib.NewReader(r.Body)
case "identity":
case "":
return r.Body, nil
default:
return nil, fmt.Errorf("unknown encoding: %q", encoding)
}
if err != nil {
return nil, err
}
return &decodedReader{r.Body, cr}, nil
}
// noopCloser is a io.WriteCloser with a no-op Close
type noopCloser struct {
io.Writer
}
// Close implements Closer for noopClosers.
func (noopCloser) Close() error {
return nil
}
type decodedReader struct {
orig io.ReadCloser
r io.ReadCloser
}
func (r *decodedReader) Read(p []byte) (int, error) {
return r.r.Read(p)
}
func (r *decodedReader) Close() error {
if err := r.r.Close(); err != nil {
return err
}
return r.orig.Close()
}