kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/util/httpencoding/httpencoding.go (about) 1 /* 2 * Copyright 2014 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package httpencoding provides functions to transparently encode/decode HTTP bodies 18 package httpencoding // import "kythe.io/kythe/go/util/httpencoding" 19 20 import ( 21 "compress/gzip" 22 "compress/zlib" 23 "fmt" 24 "io" 25 "net/http" 26 "strings" 27 ) 28 29 // CompressData returns a writer that writes encoded data to w. The chosen 30 // encoding is based on the Accept-Encoding header and defaults to the identity 31 // encoding. 32 func CompressData(w http.ResponseWriter, r *http.Request) io.WriteCloser { 33 encodings := strings.Split(r.Header.Get("Accept-Encoding"), ",") 34 for _, encoding := range encodings { 35 switch encoding { 36 case "gzip": 37 w.Header().Set("Content-Encoding", "gzip") 38 return gzip.NewWriter(w) 39 case "deflate": 40 w.Header().Set("Content-Encoding", "deflate") 41 return zlib.NewWriter(w) 42 case "identity": 43 return noopCloser{w} 44 } 45 } 46 return noopCloser{w} 47 } 48 49 // UncompressData returns a reads that decodes data from r.Body. The encoding is 50 // determined based on the Content-Encoding header and an error is returned if 51 // the encoding is unknown. 52 func UncompressData(r *http.Response) (io.ReadCloser, error) { 53 encoding := r.Header.Get("Content-Encoding") 54 var ( 55 cr io.ReadCloser 56 err error 57 ) 58 switch encoding { 59 case "gzip": 60 cr, err = gzip.NewReader(r.Body) 61 case "deflate": 62 cr, err = zlib.NewReader(r.Body) 63 case "identity": 64 case "": 65 return r.Body, nil 66 default: 67 return nil, fmt.Errorf("unknown encoding: %q", encoding) 68 } 69 if err != nil { 70 return nil, err 71 } 72 return &decodedReader{r.Body, cr}, nil 73 } 74 75 // noopCloser is a io.WriteCloser with a no-op Close 76 type noopCloser struct { 77 io.Writer 78 } 79 80 // Close implements Closer for noopClosers. 81 func (noopCloser) Close() error { 82 return nil 83 } 84 85 type decodedReader struct { 86 orig io.ReadCloser 87 r io.ReadCloser 88 } 89 90 func (r *decodedReader) Read(p []byte) (int, error) { 91 return r.r.Read(p) 92 } 93 94 func (r *decodedReader) Close() error { 95 if err := r.r.Close(); err != nil { 96 return err 97 } 98 return r.orig.Close() 99 }