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  }