forked from richardlehane/webarchive
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decode.go
124 lines (112 loc) · 2.83 KB
/
decode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2015 Richard Lehane. 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 webarchive
import (
"compress/gzip"
"compress/zlib"
"io"
"net/http/httputil"
)
func isgzip(buf []byte) bool {
if buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8 {
return false
}
return true
}
const zlibDeflate = 8
func iszlib(buf []byte) bool {
h := uint(buf[0])<<8 | uint(buf[1])
if (buf[0]&0x0f != zlibDeflate) || (h%31 != 0) {
return false
}
return true
}
func ischunk(buf []byte) bool {
for i, c := range buf {
switch {
case '0' <= c && c <= '9':
continue
case 'a' <= c && c <= 'f':
continue
case 'A' <= c && c <= 'F':
continue
case c == '\r':
if i > 0 && i < len(buf)-1 && buf[i+1] == '\n' {
return true
}
return false
default:
return false
}
}
return false
}
type payloadDecoder struct {
Record
rdr io.Reader
}
func (pd *payloadDecoder) Read(b []byte) (int, error) {
return pd.rdr.Read(b)
}
func (pd *payloadDecoder) IsSlicer() bool {
return false
}
func newDecoder(rec Record, encodings []string) Record {
if len(encodings) == 0 {
return rec
}
pd := &payloadDecoder{Record: rec, rdr: rec}
for i, v := range encodings {
switch v {
case "chunked":
if i == 0 {
if peek, err := rec.peek(10); err != nil || !ischunk(peek) {
return rec
}
}
pd.rdr = httputil.NewChunkedReader(pd.rdr)
case "deflate":
if i == 0 {
if peek, err := rec.peek(2); err != nil || !iszlib(peek) {
return rec
}
}
rdr, err := zlib.NewReader(pd.rdr)
if err == nil {
pd.rdr = rdr
}
case "gzip":
if i == 0 {
if peek, err := rec.peek(3); err != nil || !isgzip(peek) {
return rec
}
}
rdr, err := gzip.NewReader(pd.rdr)
if err == nil {
pd.rdr = rdr
}
}
}
return pd
}
// DecodePayload decodes any encodings (transfer or content) declared in a record's HTTP header.
// Decodes chunked, deflate and gzip encodings.
func DecodePayload(r Record) Record {
return newDecoder(r, r.encodings())
}
// DecodePayloadT decodes any transfer encodings declared in a record's HTTP header.
// Decodes chunked, deflate and gzip encodings.
func DecodePayloadT(r Record) Record {
return newDecoder(r, r.transferEncodings())
}