Initial commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"greq/internal/storage"
|
||||
)
|
||||
|
||||
|
||||
// RunPreScript executes rf.PreScript before sending the request.
|
||||
// The script receives REQUEST_URL, REQUEST_METHOD, REQUEST_BODY as env vars.
|
||||
// Lines of stdout matching "Key: Value" are returned as extra headers to merge
|
||||
// into the request. A non-zero exit is treated as an error (request is aborted).
|
||||
func RunPreScript(script string, rf storage.RequestFile, envs map[string]string) (map[string]string, error) {
|
||||
if strings.TrimSpace(script) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
out, err := runScript(script, map[string]string{
|
||||
"REQUEST_URL": ResolveVariables(rf.URL, envs),
|
||||
"REQUEST_METHOD": rf.Method,
|
||||
"REQUEST_BODY": rf.Body,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pre_script failed: %w\nOutput: %s", err, out)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
for _, line := range strings.Split(out, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if k, v, ok := splitScriptHeader(line); ok {
|
||||
headers[k] = v
|
||||
}
|
||||
}
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
// RunTestScript executes the test_script field of a request using the Lua runner.
|
||||
// See RunLuaTestScript for available globals and helper functions.
|
||||
func RunTestScript(script string, resp *Response) ([]TestResult, error) {
|
||||
return RunLuaTestScript(script, resp)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func runScript(script string, vars map[string]string) (string, error) {
|
||||
f, err := os.CreateTemp("", "greq-script-*.sh")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if _, err := f.WriteString("#!/bin/sh\n" + script + "\n"); err != nil {
|
||||
f.Close()
|
||||
return "", err
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if err := os.Chmod(f.Name(), 0o700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "sh", f.Name())
|
||||
// Start with the current process env, then overlay our custom vars
|
||||
// so the script has access to PATH, HOME, etc.
|
||||
cmd.Env = os.Environ()
|
||||
for k, v := range vars {
|
||||
cmd.Env = append(cmd.Env, k+"="+v)
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &out
|
||||
|
||||
err = cmd.Run()
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
func splitScriptHeader(s string) (key, val string, ok bool) {
|
||||
idx := strings.Index(s, ": ")
|
||||
if idx < 1 {
|
||||
return "", "", false
|
||||
}
|
||||
k := strings.TrimSpace(s[:idx])
|
||||
v := strings.TrimSpace(s[idx+2:])
|
||||
// Reject lines that look like script output, not headers
|
||||
if strings.ContainsAny(k, " \t=/") {
|
||||
return "", "", false
|
||||
}
|
||||
return k, v, true
|
||||
}
|
||||
Reference in New Issue
Block a user