From b3325f56b16fed1aa11b097bca7fefaef374eb52 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Thu, 20 Jun 2024 01:14:04 +0900 Subject: [PATCH] library: io: handle retryable errors Discord client being absent/disconnecting is not fatal to the RPC sender, in cases like that we return ErrAgain and in the case of broken pipe (Discord client going away) also reset the Client state so the caller can choose to retry the operation and therefore initiate a new connection attempt. Signed-off-by: Ophestra Umiker --- io.go | 13 +++++++++++++ io_unix.go | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/io.go b/io.go index 371bd8f..39b11ef 100644 --- a/io.go +++ b/io.go @@ -3,9 +3,11 @@ package rpcfetch import ( "encoding/binary" "encoding/json" + "errors" "net" "os" "strconv" + "syscall" ) type Client struct { @@ -33,6 +35,17 @@ func Raw[T any](d *Client, opcode uint32, payload any) (uint32, T, error) { // Raw writes a raw payload to Discord and returns the response opcode and payload func (d *Client) Raw(opcode uint32, payload any) (uint32, []byte, error) { + opcodeResp, payloadResp, err := d.raw(opcode, payload) + if errors.Is(err, syscall.EPIPE) { + // clean up as much as possible + _ = d.Close() + // advise retry + err = ErrAgain + } + return opcodeResp, payloadResp, err +} + +func (d *Client) raw(opcode uint32, payload any) (uint32, []byte, error) { if err := binary.Write(d.conn, binary.LittleEndian, opcode); err != nil { return 0, nil, err } diff --git a/io_unix.go b/io_unix.go index e646c76..32d3284 100644 --- a/io_unix.go +++ b/io_unix.go @@ -1,16 +1,25 @@ package rpcfetch import ( + "errors" + "io/fs" "net" "time" ) +var ( + ErrAgain = errors.New("operation not performed") +) + func (d *Client) dial() error { if d.dialed { panic("attempted to dial on open client") } if conn, err := net.DialTimeout("unix", sockPath()+"/discord-ipc-0", 5*time.Second); err != nil { + if errors.Is(err, fs.ErrNotExist) { + return ErrAgain + } return err } else { d.conn = conn