| 788 | } |
| 789 | |
| 790 | func (w *websocket) decompress(b []byte, isClient bool) ([]byte, error) { |
| 791 | bCopy := append([]byte{}, b...) |
| 792 | |
| 793 | // ref: https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.2 |
| 794 | bCopy = append(bCopy, 0x00, 0x00, 0xff, 0xff) |
| 795 | |
| 796 | var inflaterPtr *io.ReadCloser |
| 797 | var noCtx bool |
| 798 | var bits int |
| 799 | var window *[]byte |
| 800 | |
| 801 | if isClient { |
| 802 | noCtx = w.clientNoContextTakeover |
| 803 | inflaterPtr = &w.clientInflater |
| 804 | bits = w.clientMaxWindowBits |
| 805 | window = &w.clientSlidingWindow |
| 806 | } else { |
| 807 | noCtx = w.serverNoContextTakeover |
| 808 | inflaterPtr = &w.serverInflater |
| 809 | bits = w.serverMaxWindowBits |
| 810 | window = &w.serverSlidingWindow |
| 811 | } |
| 812 | |
| 813 | var r io.ReadCloser |
| 814 | if noCtx { |
| 815 | r = flate.NewReader(bytes.NewReader(bCopy)) |
| 816 | defer r.Close() |
| 817 | } else { |
| 818 | if *inflaterPtr == nil { |
| 819 | r = flate.NewReaderDict(bytes.NewReader(bCopy), *window) |
| 820 | *inflaterPtr = r |
| 821 | } else { |
| 822 | r = *inflaterPtr |
| 823 | zr, ok := r.(flate.Resetter) |
| 824 | if !ok { |
| 825 | return nil, fmt.Errorf("inflater does not implement flate.Resetter") |
| 826 | } |
| 827 | if err := zr.Reset(bytes.NewReader(bCopy), *window); err != nil { |
| 828 | return nil, fmt.Errorf("reset: %w", err) |
| 829 | } |
| 830 | } |
| 831 | } |
| 832 | |
| 833 | data, err := io.ReadAll(r) |
| 834 | switch { |
| 835 | case err == nil || errors.Is(err, io.ErrUnexpectedEOF): |
| 836 | // Not all websocket implementations send a properly terminated DEFLATE stream, |
| 837 | // which might cause an io.ErrUnexpectedEOF. Go's flate.NewReader (correctly) |
| 838 | // treats these cases as errors, but that's just how real implementations behave. |
| 839 | if !noCtx { |
| 840 | maxSize := 1 << bits |
| 841 | if len(data) >= maxSize { |
| 842 | *window = append([]byte{}, data[len(data)-maxSize:]...) |
| 843 | } else { |
| 844 | totalLen := len(*window) + len(data) |
| 845 | if totalLen <= maxSize { |
| 846 | *window = append(*window, data...) |
| 847 | } else { |