package mijia import ( "bytes" "compress/gzip" "fmt" "io" "net/http" "strings" ) const maxHTTPResponseBytes = 16 << 20 func readHTTPResponse(response *http.Response) ([]byte, error) { rawBody, err := readBounded(response.Body, maxHTTPResponseBytes) if err != nil { return nil, fmt.Errorf("read raw HTTP response: %w", err) } if !headerContainsToken(response.Header.Get("Content-Encoding"), "gzip") { return rawBody, nil } reader, err := gzip.NewReader(bytes.NewReader(rawBody)) if err != nil { return nil, fmt.Errorf("open gzip HTTP response: %w", err) } body, readErr := readBounded(reader, maxHTTPResponseBytes) closeErr := reader.Close() if readErr != nil { return nil, fmt.Errorf("decompress gzip HTTP response: %w", readErr) } if closeErr != nil { return nil, fmt.Errorf("close gzip HTTP response: %w", closeErr) } return body, nil } func readBounded(reader io.Reader, maximum int64) ([]byte, error) { body, err := io.ReadAll(io.LimitReader(reader, maximum+1)) if err != nil { return nil, err } if int64(len(body)) > maximum { return nil, fmt.Errorf("response exceeds %d bytes", maximum) } return body, nil } func headerContainsToken(value, token string) bool { for _, encoding := range strings.Split(value, ",") { if strings.EqualFold(strings.TrimSpace(encoding), token) { return true } } return false }