tcp_frames.go 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. package bwtest
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. "net"
  7. "sync"
  8. )
  9. // A single framed stream multiplexes data and reports without competing readers
  10. // or interleaved writes. The newline JSON handshake negotiates this format.
  11. const tcpFramingVersion = "frames-v1"
  12. const (
  13. tcpFrameData byte = 1
  14. tcpFrameControl byte = 2
  15. tcpFrameMax = 64 * 1024
  16. )
  17. type tcpFrameWriter struct {
  18. conn net.Conn
  19. mu sync.Mutex
  20. }
  21. func (w *tcpFrameWriter) write(kind byte, payload []byte) (int, error) {
  22. if len(payload) == 0 || len(payload) > tcpFrameMax {
  23. return 0, fmt.Errorf("invalid TCP bandwidth frame size")
  24. }
  25. w.mu.Lock()
  26. defer w.mu.Unlock()
  27. var h [5]byte
  28. h[0] = kind
  29. binary.BigEndian.PutUint32(h[1:], uint32(len(payload)))
  30. buffers := net.Buffers{h[:], payload}
  31. n, err := buffers.WriteTo(w.conn)
  32. if n < 5 {
  33. return 0, err
  34. }
  35. return int(n) - 5, err
  36. }
  37. func readTCPFrame(r io.Reader, buf []byte) (byte, []byte, error) {
  38. var h [5]byte
  39. if _, err := io.ReadFull(r, h[:]); err != nil {
  40. return 0, nil, err
  41. }
  42. n := int(binary.BigEndian.Uint32(h[1:]))
  43. if (h[0] != tcpFrameData && h[0] != tcpFrameControl) || n == 0 || n > tcpFrameMax || n > len(buf) {
  44. return 0, nil, fmt.Errorf("invalid TCP bandwidth frame")
  45. }
  46. _, err := io.ReadFull(r, buf[:n])
  47. return h[0], buf[:n], err
  48. }