5
5
"errors"
6
6
"fmt"
7
7
"io"
8
- "log/slog"
9
8
"net"
10
9
"net/http"
11
10
"os"
@@ -29,7 +28,18 @@ type Options struct {
29
28
Debug bool
30
29
}
31
30
32
- func Start (ctx context.Context , opts Options ) error {
31
+ // Run will start the server and block until the server is shut down.
32
+ func Run (ctx context.Context , opts Options ) error {
33
+ listener , err := newListener (opts )
34
+ if err != nil {
35
+ return err
36
+ }
37
+
38
+ _ , err = io .WriteString (os .Stderr , listener .Addr ().String ()+ "\n " )
39
+ if err != nil {
40
+ return fmt .Errorf ("failed to write to address to stderr: %w" , err )
41
+ }
42
+
33
43
sigCtx , cancel := signal .NotifyContext (ctx , syscall .SIGTERM , syscall .SIGQUIT , syscall .SIGKILL )
34
44
defer cancel ()
35
45
go func () {
@@ -40,6 +50,32 @@ func Start(ctx context.Context, opts Options) error {
40
50
cancel ()
41
51
}()
42
52
53
+ return run (sigCtx , listener , opts )
54
+ }
55
+
56
+ // EmbeddedStart allows running the server as an embedded process that may use Stdin for input.
57
+ // It returns the address the server is listening on.
58
+ func EmbeddedStart (ctx context.Context , opts Options ) (string , error ) {
59
+ listener , err := newListener (opts )
60
+ if err != nil {
61
+ return "" , err
62
+ }
63
+
64
+ go run (ctx , listener , opts )
65
+
66
+ return listener .Addr ().String (), nil
67
+ }
68
+
69
+ func (s * server ) close () {
70
+ s .client .Close (true )
71
+ s .events .Close ()
72
+ }
73
+
74
+ func newListener (opts Options ) (net.Listener , error ) {
75
+ return net .Listen ("tcp" , opts .ListenAddress )
76
+ }
77
+
78
+ func run (ctx context.Context , listener net.Listener , opts Options ) error {
43
79
if opts .Debug {
44
80
mvl .SetDebug ()
45
81
}
@@ -58,11 +94,6 @@ func Start(ctx context.Context, opts Options) error {
58
94
return err
59
95
}
60
96
61
- listener , err := net .Listen ("tcp" , opts .ListenAddress )
62
- if err != nil {
63
- return fmt .Errorf ("failed to listen on %s: %w" , opts .ListenAddress , err )
64
- }
65
-
66
97
s := & server {
67
98
gptscriptOpts : opts .Options ,
68
99
address : listener .Addr ().String (),
@@ -72,11 +103,11 @@ func Start(ctx context.Context, opts Options) error {
72
103
waitingToConfirm : make (map [string ]chan runner.AuthorizerResponse ),
73
104
waitingToPrompt : make (map [string ]chan map [string ]string ),
74
105
}
75
- defer s .Close ()
106
+ defer s .close ()
76
107
77
108
s .addRoutes (http .DefaultServeMux )
78
109
79
- server := http.Server {
110
+ httpServer := & http.Server {
80
111
Handler : apply (http .DefaultServeMux ,
81
112
contentType ("application/json" ),
82
113
addRequestID ,
@@ -86,25 +117,22 @@ func Start(ctx context.Context, opts Options) error {
86
117
),
87
118
}
88
119
89
- slog . Info ( "Starting server" , "addr" , s . address )
90
-
91
- context .AfterFunc (sigCtx , func () {
92
- ctx , cancel := context .WithTimeout (ctx , 15 * time .Second )
120
+ logger := mvl . Package ( )
121
+ done := make ( chan struct {})
122
+ context .AfterFunc (ctx , func () {
123
+ ctx , cancel := context .WithTimeout (context . Background () , 15 * time .Second )
93
124
defer cancel ()
94
125
95
- slog .Info ("Shutting down server" )
96
- _ = server .Shutdown (ctx )
97
- slog .Info ("Server stopped" )
126
+ logger .Infof ("Shutting down server" )
127
+ _ = httpServer .Shutdown (ctx )
128
+ logger .Infof ("Server stopped" )
129
+ close (done )
98
130
})
99
131
100
- if err := server .Serve (listener ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
132
+ if err = httpServer .Serve (listener ); ! errors .Is (err , http .ErrServerClosed ) {
101
133
return fmt .Errorf ("server error: %w" , err )
102
134
}
103
135
136
+ <- done
104
137
return nil
105
138
}
106
-
107
- func (s * server ) Close () {
108
- s .client .Close (true )
109
- s .events .Close ()
110
- }
0 commit comments