github.com/diamondburned/arikawa/v2@v2.1.0/internal/zlib/zlib.go (about)

     1  // Package zlib provides abstractions on top of compress/zlib to work with
     2  // Discord's method of compressing websocket packets.
     3  package zlib
     4  
     5  import (
     6  	"bytes"
     7  	"log"
     8  
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  var Suffix = [4]byte{'\x00', '\x00', '\xff', '\xff'}
    13  
    14  var ErrPartial = errors.New("only partial payload in buffer")
    15  
    16  type Inflator struct {
    17  	zlib Reader
    18  	wbuf bytes.Buffer // write buffer for writing compressed bytes
    19  	rbuf bytes.Buffer // read buffer for writing uncompressed bytes
    20  }
    21  
    22  func NewInflator() *Inflator {
    23  	return &Inflator{
    24  		wbuf: bytes.Buffer{},
    25  		rbuf: bytes.Buffer{},
    26  	}
    27  }
    28  
    29  func (i *Inflator) Write(p []byte) (n int, err error) {
    30  	log.Println(p)
    31  	// Write to buffer normally.
    32  	return i.wbuf.Write(p)
    33  }
    34  
    35  // CanFlush returns if Flush() should be called.
    36  func (i *Inflator) CanFlush() bool {
    37  	if i.wbuf.Len() < 4 {
    38  		return false
    39  	}
    40  	p := i.wbuf.Bytes()
    41  	return bytes.Equal(p[len(p)-4:], Suffix[:])
    42  }
    43  
    44  func (i *Inflator) Flush() ([]byte, error) {
    45  	// Check if close frames are there:
    46  	// if !i.CanFlush() {
    47  	// 	return nil, ErrPartial
    48  	// }
    49  
    50  	// log.Println(i.wbuf.Bytes())
    51  
    52  	// We should reset the write buffer after flushing.
    53  	// defer i.wbuf.Reset()
    54  
    55  	// We can reset the read buffer while returning its byte slice. This works
    56  	// as long as we copy the byte slice before resetting.
    57  	defer i.rbuf.Reset()
    58  
    59  	// Guarantee there's a zlib writer. Since Discord streams zlib, we have to
    60  	// reuse the same Reader. Only the first packet has the zlib header.
    61  	if i.zlib == nil {
    62  		r, err := zlibStreamer(&i.wbuf)
    63  		if err != nil {
    64  			return nil, errors.Wrap(err, "failed to make a FLATE reader")
    65  		}
    66  		// safe assertion
    67  		i.zlib = r
    68  		// } else {
    69  		// 	// Reset the FLATE reader for future use:
    70  		// 	if err := i.zlib.Reset(&i.wbuf, nil); err != nil {
    71  		// 		return nil, errors.Wrap(err, "failed to reset zlib reader")
    72  		// 	}
    73  	}
    74  
    75  	// We can ignore zlib.Read's error, as zlib.Close would return them.
    76  	_, err := i.rbuf.ReadFrom(i.zlib)
    77  
    78  	// ErrUnexpectedEOF happens because zlib tries to find the last 4 bytes
    79  	// to verify checksum. Discord doesn't send this.
    80  	if err != nil {
    81  		// Unexpected error, try and close.
    82  		return nil, errors.Wrap(err, "failed to read from FLATE reader")
    83  	}
    84  
    85  	// 	if err := i.zlib.Close(); err != nil && err != io.ErrUnexpectedEOF {
    86  	// 		// Try and close anyway.
    87  	// 		return nil, errors.Wrap(err, "failed to read from zlib reader")
    88  	// 	}
    89  
    90  	// Copy the bytes.
    91  	return bytecopy(i.rbuf.Bytes()), nil
    92  }
    93  
    94  // func (d *Deflator) TryFlush() ([]byte, error) {
    95  // 	// Check if the buffer ends with the zlib close suffix.
    96  // 	if d.wbuf.Len() < 4 {
    97  // 		return nil, nil
    98  // 	}
    99  // 	if p := d.wbuf.Bytes(); !bytes.Equal(p[len(p)-4:], Suffix[:]) {
   100  // 		return nil, nil
   101  // 	}
   102  
   103  // 	// Guarantee there's a zlib writer. Since Discord streams zlib, we have to
   104  // 	// reuse the same Reader. Only the first packet has the zlib header.
   105  // 	if d.zlib == nil {
   106  // 		r, err := zlib.NewReader(&d.wbuf)
   107  // 		if err != nil {
   108  // 			return nil, errors.Wrap(err, "failed to make a zlib reader")
   109  // 		}
   110  // 		// safe assertion
   111  // 		d.zlib = r
   112  // 	}
   113  
   114  // 	// We can reset the read buffer while returning its byte slice. This works
   115  // 	// as long as we copy the byte slice before resetting.
   116  // 	defer d.rbuf.Reset()
   117  
   118  // 	defer d.wbuf.Reset()
   119  
   120  // 	// We can ignore zlib.Read's error, as zlib.Close would return them.
   121  // 	_, err := d.rbuf.ReadFrom(d.zlib)
   122  // 	log.Println("Read:", err, d.rbuf.String())
   123  
   124  // 	// ErrUnexpectedEOF happens because zlib tries to find the last 4 bytes
   125  // 	// to verify checksum. Discord doesn't send this.
   126  // 	// if err != nil && err != io.ErrUnexpectedEOF {
   127  // 	// 	// Unexpected error, try and close.
   128  // 	// 	return nil, errors.Wrap(err, "failed to read from zlib reader")
   129  // 	// }
   130  
   131  // 	if err := d.zlib.Close(); err != nil && err != io.ErrUnexpectedEOF {
   132  // 		// Try and close anyway.
   133  // 		return nil, errors.Wrap(err, "failed to read from zlib reader")
   134  // 	}
   135  
   136  // 	// Copy the bytes.
   137  // 	return bytecopy(d.rbuf.Bytes()), nil
   138  // }
   139  
   140  func bytecopy(p []byte) []byte {
   141  	cpy := make([]byte, len(p))
   142  	copy(cpy, p)
   143  	return cpy
   144  }