hysteria/extras/outbounds/speedtest/protocol.go

152 lines
3.3 KiB
Go

package speedtest
import (
"encoding/binary"
"io"
"time"
)
const (
typeDownload = 0x1
typeUpload = 0x2
)
// DownloadRequest format:
// 0x1 (byte)
// Request data length (uint32 BE)
func readDownloadRequest(r io.Reader) (uint32, error) {
var l uint32
err := binary.Read(r, binary.BigEndian, &l)
return l, err
}
func writeDownloadRequest(w io.Writer, l uint32) error {
buf := make([]byte, 5)
buf[0] = typeDownload
binary.BigEndian.PutUint32(buf[1:], l)
_, err := w.Write(buf)
return err
}
// DownloadResponse format:
// Status (byte, 0=ok, 1=error)
// Message length (uint16 BE)
// Message (bytes)
func readDownloadResponse(r io.Reader) (bool, string, error) {
var status [1]byte
if _, err := io.ReadFull(r, status[:]); err != nil {
return false, "", err
}
var msgLen uint16
if err := binary.Read(r, binary.BigEndian, &msgLen); err != nil {
return false, "", err
}
// No message is fine
if msgLen == 0 {
return status[0] == 0, "", nil
}
msgBuf := make([]byte, msgLen)
_, err := io.ReadFull(r, msgBuf)
if err != nil {
return false, "", err
}
return status[0] == 0, string(msgBuf), nil
}
func writeDownloadResponse(w io.Writer, ok bool, msg string) error {
sz := 1 + 2 + len(msg)
buf := make([]byte, sz)
if ok {
buf[0] = 0
} else {
buf[0] = 1
}
binary.BigEndian.PutUint16(buf[1:], uint16(len(msg)))
copy(buf[3:], msg)
_, err := w.Write(buf)
return err
}
// UploadRequest format:
// 0x2 (byte)
// Upload data length (uint32 BE)
func readUploadRequest(r io.Reader) (uint32, error) {
var l uint32
err := binary.Read(r, binary.BigEndian, &l)
return l, err
}
func writeUploadRequest(w io.Writer, l uint32) error {
buf := make([]byte, 5)
buf[0] = typeUpload
binary.BigEndian.PutUint32(buf[1:], l)
_, err := w.Write(buf)
return err
}
// UploadResponse format:
// Status (byte, 0=ok, 1=error)
// Message length (uint16 BE)
// Message (bytes)
func readUploadResponse(r io.Reader) (bool, string, error) {
var status [1]byte
if _, err := io.ReadFull(r, status[:]); err != nil {
return false, "", err
}
var msgLen uint16
if err := binary.Read(r, binary.BigEndian, &msgLen); err != nil {
return false, "", err
}
// No message is fine
if msgLen == 0 {
return status[0] == 0, "", nil
}
msgBuf := make([]byte, msgLen)
_, err := io.ReadFull(r, msgBuf)
if err != nil {
return false, "", err
}
return status[0] == 0, string(msgBuf), nil
}
func writeUploadResponse(w io.Writer, ok bool, msg string) error {
sz := 1 + 2 + len(msg)
buf := make([]byte, sz)
if ok {
buf[0] = 0
} else {
buf[0] = 1
}
binary.BigEndian.PutUint16(buf[1:], uint16(len(msg)))
copy(buf[3:], msg)
_, err := w.Write(buf)
return err
}
// UploadSummary format:
// Duration (in milliseconds, uint32 BE)
// Received data length (uint32 BE)
func readUploadSummary(r io.Reader) (time.Duration, uint32, error) {
var duration uint32
if err := binary.Read(r, binary.BigEndian, &duration); err != nil {
return 0, 0, err
}
var l uint32
if err := binary.Read(r, binary.BigEndian, &l); err != nil {
return 0, 0, err
}
return time.Duration(duration) * time.Millisecond, l, nil
}
func writeUploadSummary(w io.Writer, duration time.Duration, l uint32) error {
buf := make([]byte, 8)
binary.BigEndian.PutUint32(buf, uint32(duration/time.Millisecond))
binary.BigEndian.PutUint32(buf[4:], l)
_, err := w.Write(buf)
return err
}