X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=ucspi.go;fp=ucspi.go;h=8fce33db8e726227babf533a3b0d6a08473434ab;hb=168de6871afdd60f2bc1c529170033ef56307d7c;hp=0000000000000000000000000000000000000000;hpb=adfee4423269bf180abab3131a171f34931d7037;p=gocheese.git diff --git a/ucspi.go b/ucspi.go new file mode 100644 index 0000000..8fce33d --- /dev/null +++ b/ucspi.go @@ -0,0 +1,136 @@ +/* +GoCheese -- Python private package repository and caching proxy +Copyright (C) 2019-2021 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package main + +import ( + "io" + "net" + "net/http" + "os" + "sync" + "time" +) + +var ( + aLongTimeAgo = time.Unix(1, 0) + UCSPIJob sync.WaitGroup +) + +type UCSPIAddr struct { + ip string + port string +} + +func (addr *UCSPIAddr) Network() string { return "tcp" } + +func (addr *UCSPIAddr) String() string { return addr.ip + ":" + addr.port } + +type UCSPIConn struct { + eof chan struct{} +} + +type ReadResult struct { + n int + err error +} + +func (conn *UCSPIConn) Read(b []byte) (int, error) { + c := make(chan ReadResult) + go func() { + n, err := os.Stdin.Read(b) + c <- ReadResult{n, err} + }() + select { + case res := <-c: + return res.n, res.err + case <-conn.eof: + return 0, io.EOF + } +} + +func (conn *UCSPIConn) Write(b []byte) (int, error) { return os.Stdout.Write(b) } + +func (conn *UCSPIConn) Close() error { + if err := os.Stdin.Close(); err != nil { + return err + } + return os.Stdout.Close() +} + +func (conn *UCSPIConn) LocalAddr() net.Addr { + return &UCSPIAddr{ip: os.Getenv("TCPLOCALIP"), port: os.Getenv("TCPLOCALPORT")} +} + +func (conn *UCSPIConn) RemoteAddr() net.Addr { + return &UCSPIAddr{ip: os.Getenv("TCPREMOTEIP"), port: os.Getenv("TCPREMOTEPORT")} +} + +func (conn *UCSPIConn) SetDeadline(t time.Time) error { + if err := os.Stdin.SetReadDeadline(t); err != nil { + return err + } + return os.Stdout.SetWriteDeadline(t) +} + +func (conn *UCSPIConn) SetReadDeadline(t time.Time) error { + // An ugly hack to forcefully terminate pending read. + // net/http calls SetReadDeadline(aLongTimeAgo), but file + // descriptors are not capable to exit immediately that way. + if t.Equal(aLongTimeAgo) { + conn.eof <- struct{}{} + } + return os.Stdin.SetReadDeadline(t) +} + +func (conn *UCSPIConn) SetWriteDeadline(t time.Time) error { + return os.Stdout.SetWriteDeadline(t) +} + +type UCSPI struct{ accepted bool } + +type UCSPIAlreadyAccepted struct{} + +func (u UCSPIAlreadyAccepted) Error() string { + return "already accepted" +} + +func (ucspi *UCSPI) Accept() (net.Conn, error) { + if ucspi.accepted { + return nil, UCSPIAlreadyAccepted{} + } + ucspi.accepted = true + conn := UCSPIConn{eof: make(chan struct{}, 1)} + return &conn, nil +} + +func (ucspi *UCSPI) Close() error { + return nil +} + +func (ucspi *UCSPI) Addr() net.Addr { + return &UCSPIAddr{ip: os.Getenv("TCPLOCALIP"), port: os.Getenv("TCPLOCALPORT")} +} + +func connStater(conn net.Conn, connState http.ConnState) { + switch connState { + case http.StateNew: + UCSPIJob.Add(1) + case http.StateClosed: + UCSPIJob.Done() + } +}