-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.go
117 lines (102 loc) · 2.89 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package pocketic
import (
"fmt"
"io"
"os"
"os/exec"
"path"
"strconv"
"strings"
"time"
)
type printWriter struct{}
func (w printWriter) Write(p []byte) (n int, err error) {
fmt.Print(string(p))
return len(p), nil
}
type server struct {
port int
cmd *exec.Cmd
}
func newServer(opts ...serverOption) (*server, error) {
config := serverConfig{}
for _, fn := range opts {
fn(&config)
}
// Try to find the pocket-ic binary.
binPath, err := exec.LookPath("pocket-ic-server")
if err != nil {
if binPath, err = exec.LookPath("pocket-ic"); err != nil {
// If the binary is not found, try to find it in the POCKET_IC_BIN environment variable.
if pathEnv := os.Getenv("POCKET_IC_BIN"); pathEnv != "" {
binPath = pathEnv
} else {
binPath = "./pocket-ic"
if _, err := os.Stat(binPath); err != nil {
return nil, fmt.Errorf("pocket-ic binary not found: %v", err)
}
}
}
}
versionCmd := exec.Command(binPath, "--version")
rawVersion, err := versionCmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("failed to get pocket-ic version: %v", err)
}
version := strings.TrimPrefix(strings.TrimSpace(string(rawVersion)), "pocket-ic-server ")
if !strings.HasPrefix(version, "4.") {
return nil, fmt.Errorf("unsupported pocket-ic version, must be v4.x: %s", version)
}
pid := os.Getpid()
cmdArgs := []string{"--pid", strconv.Itoa(pid)}
if config.ttl != nil {
cmdArgs = append(cmdArgs, "--ttl", strconv.Itoa(*config.ttl))
}
cmd := exec.Command(binPath, cmdArgs...)
if os.Getenv("POCKET_IC_MUTE_SERVER") == "" {
cmd.Stdout = new(printWriter)
cmd.Stderr = new(printWriter)
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to start pocket-ic: %v", err)
}
tmpDir := os.TempDir()
readyFile := path.Join(tmpDir, fmt.Sprintf("pocket_ic_%d.ready", pid))
stopAt := time.Now().Add(5 * time.Second)
for _, err := os.Stat(readyFile); os.IsNotExist(err); _, err = os.Stat(readyFile) {
time.Sleep(100 * time.Millisecond)
if time.Now().After(stopAt) {
return nil, fmt.Errorf("pocket-ic did not start in time, %s not found", readyFile)
}
}
portFile := path.Join(tmpDir, fmt.Sprintf("pocket_ic_%d.port", pid))
f, err := os.OpenFile(portFile, os.O_RDONLY, 0644)
if err != nil {
return nil, fmt.Errorf("failed to open port file: %v", err)
}
rawPort, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("failed to read port file: %v", err)
}
port, err := strconv.Atoi(string(rawPort))
if err != nil {
return nil, fmt.Errorf("failed to convert port to int: %v", err)
}
return &server{
port: port,
cmd: cmd,
}, nil
}
func (s server) URL() string {
return fmt.Sprintf("http://127.0.0.1:%d", s.port)
}
type serverConfig struct {
ttl *int
}
type serverOption func(*serverConfig)
// withTTL sets the time-to-live for the pocket-ic server, in seconds.
func withTTL(ttl int) serverOption {
return func(c *serverConfig) {
c.ttl = &ttl
}
}