github.com/simonmittag/ws@v1.1.0-rc.5.0.20210419231947-82b846128245/wsflate/helper.go (about) 1 package wsflate 2 3 import ( 4 "bytes" 5 "compress/flate" 6 "fmt" 7 "io" 8 9 "github.com/simonmittag/ws" 10 ) 11 12 // DefaultHelper is a default helper instance holding standard library's 13 // `compress/flate` compressor and decompressor under the hood. 14 // 15 // Note that use of DefaultHelper methods assumes that DefaultParameters were 16 // used for extension negotiation during WebSocket handshake. 17 var DefaultHelper = Helper{ 18 Compressor: func(w io.Writer) Compressor { 19 // No error can be returned here as NewWriter() doc says. 20 f, _ := flate.NewWriter(w, 9) 21 return f 22 }, 23 Decompressor: func(r io.Reader) Decompressor { 24 return flate.NewReader(r) 25 }, 26 } 27 28 // DefaultParameters holds deflate extension parameters which are assumed by 29 // DefaultHelper to be used during WebSocket handshake. 30 var DefaultParameters = Parameters{ 31 ServerNoContextTakeover: true, 32 ClientNoContextTakeover: true, 33 } 34 35 // CompressFrame is a shortcut for DefaultHelper.CompressFrame(). 36 // 37 // Note that use of DefaultHelper methods assumes that DefaultParameters were 38 // used for extension negotiation during WebSocket handshake. 39 func CompressFrame(f ws.Frame) (ws.Frame, error) { 40 return DefaultHelper.CompressFrame(f) 41 } 42 43 // CompressFrameBuffer is a shortcut for DefaultHelper.CompressFrameBuffer(). 44 // 45 // Note that use of DefaultHelper methods assumes that DefaultParameters were 46 // used for extension negotiation during WebSocket handshake. 47 func CompressFrameBuffer(buf Buffer, f ws.Frame) (ws.Frame, error) { 48 return DefaultHelper.CompressFrameBuffer(buf, f) 49 } 50 51 // DecompressFrame is a shortcut for DefaultHelper.DecompressFrame(). 52 // 53 // Note that use of DefaultHelper methods assumes that DefaultParameters were 54 // used for extension negotiation during WebSocket handshake. 55 func DecompressFrame(f ws.Frame) (ws.Frame, error) { 56 return DefaultHelper.DecompressFrame(f) 57 } 58 59 // DecompressFrameBuffer is a shortcut for 60 // DefaultHelper.DecompressFrameBuffer(). 61 // 62 // Note that use of DefaultHelper methods assumes that DefaultParameters were 63 // used for extension negotiation during WebSocket handshake. 64 func DecompressFrameBuffer(buf Buffer, f ws.Frame) (ws.Frame, error) { 65 return DefaultHelper.DecompressFrameBuffer(buf, f) 66 } 67 68 // Helper is a helper struct that holds common code for compression and 69 // decompression bytes or WebSocket frames. 70 // 71 // Its purpose is to reduce boilerplate code in WebSocket applications. 72 type Helper struct { 73 Compressor func(w io.Writer) Compressor 74 Decompressor func(r io.Reader) Decompressor 75 } 76 77 // Buffer is an interface representing some bytes buffering object. 78 type Buffer interface { 79 io.Writer 80 Bytes() []byte 81 } 82 83 // CompressFrame returns compressed version of a frame. 84 // Note that it does memory allocations internally. To control those 85 // allocations consider using CompressFrameBuffer(). 86 func (h *Helper) CompressFrame(in ws.Frame) (f ws.Frame, err error) { 87 var buf bytes.Buffer 88 return h.CompressFrameBuffer(&buf, in) 89 } 90 91 // DecompressFrame returns decompressed version of a frame. 92 // Note that it does memory allocations internally. To control those 93 // allocations consider using DecompressFrameBuffer(). 94 func (h *Helper) DecompressFrame(in ws.Frame) (f ws.Frame, err error) { 95 var buf bytes.Buffer 96 return h.DecompressFrameBuffer(&buf, in) 97 } 98 99 // CompressFrameBuffer compresses a frame using given buffer. 100 // Returned frame's payload holds bytes returned by buf.Bytes(). 101 func (h *Helper) CompressFrameBuffer(buf Buffer, f ws.Frame) (ws.Frame, error) { 102 if !f.Header.Fin { 103 return f, fmt.Errorf("wsflate: fragmented messages are not allowed") 104 } 105 if err := h.CompressTo(buf, f.Payload); err != nil { 106 return f, err 107 } 108 var err error 109 f.Payload = buf.Bytes() 110 f.Header.Length = int64(len(f.Payload)) 111 f.Header, err = SetBit(f.Header) 112 if err != nil { 113 return f, err 114 } 115 return f, nil 116 } 117 118 // DecompressFrameBuffer decompresses a frame using given buffer. 119 // Returned frame's payload holds bytes returned by buf.Bytes(). 120 func (h *Helper) DecompressFrameBuffer(buf Buffer, f ws.Frame) (ws.Frame, error) { 121 if !f.Header.Fin { 122 return f, fmt.Errorf( 123 "wsflate: fragmented messages are not supported by helper", 124 ) 125 } 126 var ( 127 compressed bool 128 err error 129 ) 130 f.Header, compressed, err = UnsetBit(f.Header) 131 if err != nil { 132 return f, err 133 } 134 if !compressed { 135 return f, nil 136 } 137 if err := h.DecompressTo(buf, f.Payload); err != nil { 138 return f, err 139 } 140 141 f.Payload = buf.Bytes() 142 f.Header.Length = int64(len(f.Payload)) 143 144 return f, nil 145 } 146 147 // Compress compresses given bytes. 148 // Note that it does memory allocations internally. To control those 149 // allocations consider using CompressTo(). 150 func (h *Helper) Compress(p []byte) ([]byte, error) { 151 var buf bytes.Buffer 152 if err := h.CompressTo(&buf, p); err != nil { 153 return nil, err 154 } 155 return buf.Bytes(), nil 156 } 157 158 // Decompress decompresses given bytes. 159 // Note that it does memory allocations internally. To control those 160 // allocations consider using DecompressTo(). 161 func (h *Helper) Decompress(p []byte) ([]byte, error) { 162 var buf bytes.Buffer 163 if err := h.DecompressTo(&buf, p); err != nil { 164 return nil, err 165 } 166 return buf.Bytes(), nil 167 } 168 169 // CompressTo compresses bytes into given buffer. 170 func (h *Helper) CompressTo(w io.Writer, p []byte) (err error) { 171 c := NewWriter(w, h.Compressor) 172 if _, err = c.Write(p); err != nil { 173 return err 174 } 175 if err = c.Flush(); err != nil { 176 return err 177 } 178 if err = c.Close(); err != nil { 179 return err 180 } 181 return nil 182 } 183 184 // DecompressTo decompresses bytes into given buffer. 185 // Returned bytes are bytes returned by buf.Bytes(). 186 func (h *Helper) DecompressTo(w io.Writer, p []byte) (err error) { 187 fr := NewReader(bytes.NewReader(p), h.Decompressor) 188 if _, err = io.Copy(w, fr); err != nil { 189 return err 190 } 191 if err = fr.Close(); err != nil { 192 return err 193 } 194 return nil 195 }