1
2
3
4
5 package vcweb
6
7 import (
8 "io"
9 "log"
10 "net"
11 "net/http"
12 "os/exec"
13 "strings"
14 "sync"
15 )
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 type svnHandler struct {
35 svnRoot string
36 logger *log.Logger
37
38 pathOnce sync.Once
39 svnservePath string
40 svnserveErr error
41
42 listenOnce sync.Once
43 s chan *svnState
44 }
45
46
47 type svnState struct {
48 listener net.Listener
49 listenErr error
50 conns map[net.Conn]struct{}
51 closing bool
52 done chan struct{}
53 }
54
55 func (h *svnHandler) Available() bool {
56 h.pathOnce.Do(func() {
57 h.svnservePath, h.svnserveErr = exec.LookPath("svnserve")
58 })
59 return h.svnserveErr == nil
60 }
61
62
63
64
65
66 func (h *svnHandler) Handler(dir string, env []string, logger *log.Logger) (http.Handler, error) {
67 if !h.Available() {
68 return nil, ServerNotInstalledError{name: "svn"}
69 }
70
71
72
73
74 h.listenOnce.Do(func() {
75 h.s = make(chan *svnState, 1)
76 l, err := net.Listen("tcp", "localhost:0")
77 done := make(chan struct{})
78
79 h.s <- &svnState{
80 listener: l,
81 listenErr: err,
82 conns: map[net.Conn]struct{}{},
83 done: done,
84 }
85 if err != nil {
86 close(done)
87 return
88 }
89
90 h.logger.Printf("serving svn on svn://%v", l.Addr())
91
92 go func() {
93 for {
94 c, err := l.Accept()
95
96 s := <-h.s
97 if err != nil {
98 s.listenErr = err
99 if len(s.conns) == 0 {
100 close(s.done)
101 }
102 h.s <- s
103 return
104 }
105 if s.closing {
106 c.Close()
107 } else {
108 s.conns[c] = struct{}{}
109 go h.serve(c)
110 }
111 h.s <- s
112 }
113 }()
114 })
115
116 s := <-h.s
117 addr := ""
118 if s.listener != nil {
119 addr = s.listener.Addr().String()
120 }
121 err := s.listenErr
122 h.s <- s
123 if err != nil {
124 return nil, err
125 }
126
127 handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
128 if req.FormValue("vcwebsvn") != "" {
129 w.Header().Add("Content-Type", "text/plain; charset=UTF-8")
130 io.WriteString(w, "svn://"+addr+"\n")
131 return
132 }
133 http.NotFound(w, req)
134 })
135
136 return handler, nil
137 }
138
139
140 func (h *svnHandler) serve(c net.Conn) {
141 defer func() {
142 c.Close()
143
144 s := <-h.s
145 delete(s.conns, c)
146 if len(s.conns) == 0 && s.listenErr != nil {
147 close(s.done)
148 }
149 h.s <- s
150 }()
151
152
153
154
155
156
157 cmd := exec.Command(h.svnservePath, "--read-only", "--root="+h.svnRoot, "--inetd")
158 cmd.Stdin = c
159 cmd.Stdout = c
160 stderr := new(strings.Builder)
161 cmd.Stderr = stderr
162 err := cmd.Run()
163
164 var errFrag any = "ok"
165 if err != nil {
166 errFrag = err
167 }
168 stderrFrag := ""
169 if stderr.Len() > 0 {
170 stderrFrag = "\n" + stderr.String()
171 }
172 h.logger.Printf("%v: %s%s", cmd, errFrag, stderrFrag)
173 }
174
175
176
177 func (h *svnHandler) Close() error {
178 h.listenOnce.Do(func() {})
179 if h.s == nil {
180 return nil
181 }
182
183 var err error
184 s := <-h.s
185 s.closing = true
186 if s.listener == nil {
187 err = s.listenErr
188 } else {
189 err = s.listener.Close()
190 }
191 for c := range s.conns {
192 c.Close()
193 }
194 done := s.done
195 h.s <- s
196
197 <-done
198 return err
199 }
200
View as plain text