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,34 @@ 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 func () {
65
+ _ = run (ctx , listener , opts )
66
+ }()
67
+
68
+ return listener .Addr ().String (), nil
69
+ }
70
+
71
+ func (s * server ) close () {
72
+ s .client .Close (true )
73
+ s .events .Close ()
74
+ }
75
+
76
+ func newListener (opts Options ) (net.Listener , error ) {
77
+ return net .Listen ("tcp" , opts .ListenAddress )
78
+ }
79
+
80
+ func run (ctx context.Context , listener net.Listener , opts Options ) error {
43
81
if opts .Debug {
44
82
mvl .SetDebug ()
45
83
}
@@ -58,11 +96,6 @@ func Start(ctx context.Context, opts Options) error {
58
96
return err
59
97
}
60
98
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
99
s := & server {
67
100
gptscriptOpts : opts .Options ,
68
101
address : listener .Addr ().String (),
@@ -72,11 +105,11 @@ func Start(ctx context.Context, opts Options) error {
72
105
waitingToConfirm : make (map [string ]chan runner.AuthorizerResponse ),
73
106
waitingToPrompt : make (map [string ]chan map [string ]string ),
74
107
}
75
- defer s .Close ()
108
+ defer s .close ()
76
109
77
110
s .addRoutes (http .DefaultServeMux )
78
111
79
- server := http.Server {
112
+ httpServer := & http.Server {
80
113
Handler : apply (http .DefaultServeMux ,
81
114
contentType ("application/json" ),
82
115
addRequestID ,
@@ -86,25 +119,22 @@ func Start(ctx context.Context, opts Options) error {
86
119
),
87
120
}
88
121
89
- slog . Info ( "Starting server" , "addr" , s . address )
90
-
91
- context .AfterFunc (sigCtx , func () {
92
- ctx , cancel := context .WithTimeout (ctx , 15 * time .Second )
122
+ logger := mvl . Package ( )
123
+ done := make ( chan struct {})
124
+ context .AfterFunc (ctx , func () {
125
+ ctx , cancel := context .WithTimeout (context . Background () , 15 * time .Second )
93
126
defer cancel ()
94
127
95
- slog .Info ("Shutting down server" )
96
- _ = server .Shutdown (ctx )
97
- slog .Info ("Server stopped" )
128
+ logger .Infof ("Shutting down server" )
129
+ _ = httpServer .Shutdown (ctx )
130
+ logger .Infof ("Server stopped" )
131
+ close (done )
98
132
})
99
133
100
- if err := server .Serve (listener ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
134
+ if err = httpServer .Serve (listener ); ! errors .Is (err , http .ErrServerClosed ) {
101
135
return fmt .Errorf ("server error: %w" , err )
102
136
}
103
137
138
+ <- done
104
139
return nil
105
140
}
106
-
107
- func (s * server ) Close () {
108
- s .client .Close (true )
109
- s .events .Close ()
110
- }
0 commit comments