package io import ( "net" "time" "git.smesh.lol/torly/pkg/wire" "git.smesh.lol/torly/pkg/ws" ) const writeTimeout = 10 * time.Second // Writer is one writer worker domain. It owns the write side and the close of // the connection currently assigned to it. It never reads application data. func Writer(_ WriterData, ch WriterChans) { var sock *wire.Socket wsMode := false closeConn := func() { if sock != nil { sock.Conn.Close() sock = nil } wsMode = false } defer closeConn() for { select { case <-ch.Stop.Done(): return case ctl := <-ch.Ctl.Recv(): switch ctl.Kind { case wire.CtlBind: closeConn() sock = ctl.Socket wsMode = false sendAck(ch, wire.CtlBind) case wire.CtlUpgrade: if sock == nil { continue } if err := writeAll(sock.Conn, ctl.Payload); err != nil { closeConn() continue } wsMode = true sendAck(ch, wire.CtlUpgrade) case wire.CtlWrite: if sock != nil { if err := writeAll(sock.Conn, ctl.Payload); err != nil { closeConn() } } case wire.CtlPong: if sock != nil && wsMode { if err := writeAll(sock.Conn, ws.AppendFrame(nil, ws.OpPong, ctl.Payload)); err != nil { closeConn() } } case wire.CtlRespond: if sock != nil { _ = writeAll(sock.Conn, ctl.Payload) } closeConn() case wire.CtlClose: if sock != nil && wsMode { frame := ctl.Payload if len(frame) == 0 { frame = ws.AppendClose(nil, 1000, "") } _ = writeAll(sock.Conn, frame) } closeConn() } case out := <-ch.Out.Recv(): if sock == nil { continue } switch out.Kind { case wire.OutText: if wsMode { if err := writeAll(sock.Conn, ws.AppendFrame(nil, ws.OpText, out.Text)); err != nil { closeConn() } } case wire.OutHTTP: _ = writeAll(sock.Conn, SerializeHTTP(out.HTTP)) closeConn() case wire.OutClose: if wsMode { frame := ws.AppendClose(nil, out.Code, out.Reason) _ = writeAll(sock.Conn, frame) } closeConn() case wire.OutPing: if wsMode { if err := writeAll(sock.Conn, ws.AppendFrame(nil, ws.OpPing, nil)); err != nil { closeConn() } } } } } } func sendAck(ch WriterChans, kind uint8) { select { case ch.Ack.Tx <- kind: case <-ch.Stop.Done(): } } func writeAll(c net.Conn, p []byte) error { _ = c.SetWriteDeadline(time.Now().Add(writeTimeout)) for len(p) > 0 { n, err := c.Write(p) if err != nil { return err } p = p[n:] } return nil }