@@ -13,6 +13,7 @@ import (
13
13
"os/signal"
14
14
"path"
15
15
"path/filepath"
16
+ "strconv"
16
17
"strings"
17
18
"syscall"
18
19
"time"
@@ -119,7 +120,7 @@ func runDirect(env *command.Env) error {
119
120
}
120
121
121
122
var serveFlags struct {
122
- Socket string `flag:"socket ,default=$GOCACHE_SOCKET,Socket path (required)"`
123
+ Plugin int `flag:"plugin ,default=$GOCACHE_PLUGIN,Plugin service port (required)"`
123
124
HTTP string `flag:"http,default=$GOCACHE_HTTP,HTTP service address ([host]:port)"`
124
125
ModProxy bool `flag:"modproxy,default=$GOCACHE_MODPROXY,Enable a Go module proxy (requires --http)"`
125
126
RevProxy string `flag:"revproxy,default=$GOCACHE_REVPROXY,Reverse proxy these hosts (comma-separated)"`
@@ -128,10 +129,10 @@ var serveFlags struct {
128
129
129
130
func noopClose (context.Context ) error { return nil }
130
131
131
- // runServe runs a cache communicating over a Unix-domain socket.
132
+ // runServe runs a cache communicating over a local TCP socket.
132
133
func runServe (env * command.Env ) error {
133
- if serveFlags .Socket == "" {
134
- return env .Usagef ("you must provide a --socket path " )
134
+ if serveFlags .Plugin <= 0 {
135
+ return env .Usagef ("you must provide a --plugin port " )
135
136
}
136
137
137
138
// Initialize the cache server. Unlike a direct server, only close down and
@@ -144,11 +145,11 @@ func runServe(env *command.Env) error {
144
145
s .Close = noopClose
145
146
146
147
// Listen for connections from the Go toolchain on the specified socket.
147
- lst , err := net .Listen ("unix " , serveFlags .Socket )
148
+ lst , err := net .Listen ("tcp " , fmt . Sprintf ( "127.0.0.1:%d" , serveFlags .Plugin ) )
148
149
if err != nil {
149
150
return fmt .Errorf ("listen: %w" , err )
150
151
}
151
- defer os . Remove ( serveFlags . Socket ) // best-effort
152
+ log . Printf ( "plugin listening at %q" , lst . Addr ())
152
153
153
154
ctx , cancel := signal .NotifyContext (env .Context (), syscall .SIGINT , syscall .SIGTERM )
154
155
defer cancel ()
@@ -171,7 +172,7 @@ func runServe(env *command.Env) error {
171
172
Handler : mux ,
172
173
}
173
174
g .Go (srv .ListenAndServe )
174
- vprintf ("started HTTP server at %q" , serveFlags .HTTP )
175
+ vprintf ("HTTP server listening at %q" , serveFlags .HTTP )
175
176
g .Go (taskgroup .NoError (func () {
176
177
<- ctx .Done ()
177
178
vprintf ("signal received, stopping HTTP service" )
@@ -271,30 +272,53 @@ func runServe(env *command.Env) error {
271
272
return nil
272
273
}
273
274
274
- // runConnect implements a direct cache proxy by connecting to a remote server
275
- // over a Unix-domain socket.
276
- func runConnect ( env * command. Env , socketPath string ) error {
277
- if socketPath == "" {
278
- return env . Usagef ( "you must provide a socket path" )
275
+ // runConnect implements a direct cache proxy by connecting to a remote server.
276
+ func runConnect ( env * command. Env , plugin string ) error {
277
+ port , err := strconv . Atoi ( plugin )
278
+ if err != nil {
279
+ return fmt . Errorf ( "invalid plugin port: %w" , err )
279
280
}
280
- conn , err := net .Dial ("unix" , socketPath )
281
+
282
+ conn , err := net .Dial ("tcp" , fmt .Sprintf (":%d" , port ))
281
283
if err != nil {
282
- return fmt .Errorf ("dial socket : %w" , err )
284
+ return fmt .Errorf ("dial: %w" , err )
283
285
}
284
286
start := time .Now ()
285
- vprintf ("connected to %q" , socketPath )
287
+ vprintf ("connected to %q" , conn . RemoteAddr () )
286
288
287
289
out := taskgroup .Go (func () error {
288
- _ , err := io . Copy ( os . Stdout , conn )
289
- return err
290
+ defer conn .( * net. TCPConn ). CloseWrite () // let the server finish
291
+ return copy ( conn , os . Stdin )
290
292
})
291
-
292
- _ , rerr := io .Copy (conn , os .Stdin )
293
- if rerr != nil {
294
- vprintf ("error sending: %v" , rerr )
293
+ if rerr := copy (os .Stdout , conn ); rerr != nil {
294
+ vprintf ("read responses: %v" , err )
295
295
}
296
- conn .Close ()
297
296
out .Wait ()
297
+ conn .Close ()
298
298
vprintf ("connection closed (%v elapsed)" , time .Since (start ))
299
299
return nil
300
300
}
301
+
302
+ // copy emulates the base case of io.Copy, but does not attempt to use the
303
+ // io.ReaderFrom or io.WriterTo implementations.
304
+ //
305
+ // TODO(creachadair): For some reason io.Copy does not work correctly when r is
306
+ // a pipe (e.g., stdin) and w is a TCP socket. Figure out why.
307
+ func copy (w io.Writer , r io.Reader ) error {
308
+ var buf [4096 ]byte
309
+ for {
310
+ nr , err := r .Read (buf [:])
311
+ if nr > 0 {
312
+ if nw , err := w .Write (buf [:nr ]); err != nil {
313
+ return fmt .Errorf ("copy to: %w" , err )
314
+ } else if nw < nr {
315
+ return fmt .Errorf ("wrote %d < %d bytes: %w" , nw , nr , io .ErrShortWrite )
316
+ }
317
+ }
318
+ if err == io .EOF {
319
+ return nil
320
+ } else if err != nil {
321
+ return fmt .Errorf ("copy from: %w" , err )
322
+ }
323
+ }
324
+ }
0 commit comments