diff options
| author | Shulhan <ms@kilabit.info> | 2023-11-22 13:16:21 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-12-01 13:17:30 +0700 |
| commit | a73f0d5d0108e2e10d89f93c7867addbe073add9 (patch) | |
| tree | 042e69e2c1d10d836a006abca37417af02bba826 /http_server.go | |
| parent | a9701f66c2e38a3a7f3d12deed6ebba5144e208e (diff) | |
| download | awwan-a73f0d5d0108e2e10d89f93c7867addbe073add9.tar.xz | |
all: refactoring HTTP endpoint for Execute
Previously, the Execute endpoint wait for command execution to finish.
In case the command takes longer than proxy or server write timeout, it
will return with an timeout error to client.
In this changes, we generate an execution ID for each request and return
it immediately.
The next commit will implement HTTP endpoint to fetch the latest status
and/or output by execution ID.
References: https://todo.sr.ht/~shulhan/awwan/5
Diffstat (limited to 'http_server.go')
| -rw-r--r-- | http_server.go | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/http_server.go b/http_server.go index 04ac90a..a574cfa 100644 --- a/http_server.go +++ b/http_server.go @@ -4,7 +4,6 @@ package awwan import ( - "bytes" "encoding/json" "errors" "fmt" @@ -39,6 +38,9 @@ const DefListenAddress = `127.0.0.1:17600` type httpServer struct { *libhttp.Server + // idExecRes contains the execution ID and its response. + idExecRes map[string]*ExecResponse + aww *Awwan memfsBase *memfs.MemFS // The files caches. @@ -53,6 +55,8 @@ func newHttpServer(aww *Awwan, address string) (httpd *httpServer, err error) { ) httpd = &httpServer{ + idExecRes: make(map[string]*ExecResponse), + aww: aww, baseDir: aww.BaseDir, } @@ -168,7 +172,7 @@ func (httpd *httpServer) registerEndpoints() (err error) { Path: pathAwwanApiExecute, RequestType: libhttp.RequestTypeJSON, ResponseType: libhttp.ResponseTypeJSON, - Call: httpd.awwanApiExecute, + Call: httpd.Execute, }) if err != nil { return fmt.Errorf("%s: %w", logp, err) @@ -608,9 +612,33 @@ func (httpd *httpServer) awwanApiFsPut(epr *libhttp.EndpointRequest) (rawBody [] return json.Marshal(res) } -func (httpd *httpServer) awwanApiExecute(epr *libhttp.EndpointRequest) (resb []byte, err error) { +// Execute request to execute the script. +// +// Request format, +// +// POST /awwan/api/execute +// Content-Type: application/json +// +// { +// "mode": <string>, +// "script": <string>, +// "line_range": <string +// } +// +// On success it will return the state of execution, +// +// Content-Type: application/json +// +// { +// "code": 200, +// "data": <ExecResponse> +// } +// +// The ExecResponse contains ID that can be used to fetch the latest state +// of execution or to stream output. +func (httpd *httpServer) Execute(epr *libhttp.EndpointRequest) (resb []byte, err error) { var ( - logp = "awwanApiExecute" + logp = `Execute` req = &ExecRequest{} res = &libhttp.EndpointResponse{} ) @@ -629,29 +657,29 @@ func (httpd *httpServer) awwanApiExecute(epr *libhttp.EndpointRequest) (resb []b return nil, res } - var ( - data = &ExecResponse{ - ExecRequest: req, - } + var execRes = newExecResponse(req) - logw bytes.Buffer - ) + // Encode to JSON first to minimize data race. - req.registerLogWriter(`output`, &logw) + res.Code = http.StatusOK + res.Data = execRes - if req.Mode == CommandModeLocal { - err = httpd.aww.Local(req) - } else { - err = httpd.aww.Play(req) - } + resb, err = json.Marshal(res) if err != nil { - data.Error = err.Error() + res.Message = fmt.Sprintf(`%s: %s`, logp, err) + return nil, res } - data.Output = logw.Bytes() + httpd.idExecRes[execRes.ID] = execRes - res.Code = http.StatusOK - res.Data = data + go func() { + if req.Mode == CommandModeLocal { + err = httpd.aww.Local(req) + } else { + err = httpd.aww.Play(req) + } + execRes.end(err) + }() - return json.Marshal(res) + return resb, nil } |
