💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Gemigit › files › 2689ad9cd5732be8684dcd47ab… captured on 2022-07-16 at 17:09:35. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 package httpgit
1
2 import (
3 "gemigit/db"
4 "log"
5 "net/http"
6 "strconv"
7 "strings"
8 "time"
9
10 githttpxfer "github.com/nulab/go-git-http-xfer/githttpxfer"
11 )
12
13 func Listen(path string, port int) {
14 ghx, err := githttpxfer.New(path, "git")
15 if err != nil {
16 log.Fatalln("GitHTTPXfer instance could not be created. ", err.Error())
17 }
18
19 chain := newChain()
20 chain.use(logging)
21 chain.use(basicAuth)
22 handler := chain.build(ghx)
23
24 log.Println("Http server started on port", port)
25 if err := http.ListenAndServe(":"+strconv.Itoa(port), handler); err != nil {
26 log.Fatalln("ListenAndServe: ", err.Error())
27 }
28 }
29
30 type middleware func(http.Handler) http.Handler
31
32 func newChain() *chain {
33 return &chain{[]middleware{}}
34 }
35
36 type chain struct {
37 middlewares []middleware
38 }
39
40 func (c *chain) use(m middleware) {
41 c.middlewares = append(c.middlewares, m)
42 }
43
44 func (c *chain) build(h http.Handler) http.Handler {
45 for i := range c.middlewares {
46 h = c.middlewares[len(c.middlewares)-1-i](h)
47 }
48 return h
49 }
50
51 func logging(next http.Handler) http.Handler {
52 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
53 t1 := time.Now()
54 next.ServeHTTP(w, r)
55 t2 := time.Now()
56 realIP := r.Header.Get("X-Real-IP")
57 log.Println("["+realIP+"]["+r.Method+"]", r.URL.String(), t2.Sub(t1))
58 })
59 }
60
61 func basicAuth(next http.Handler) http.Handler {
62 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63 params := strings.Split(r.URL.Path[1:], "/")
64 if len(params) < 2 {
65 renderNotFound(w)
66 return
67 }
68 if strings.Contains(r.URL.Path, "git-upload-pack") || strings.Contains(r.URL.RawQuery, "git-upload-pack") {
69 b, err := db.IsRepoPublic(params[1], params[0])
70 if err != nil {
71 renderNotFound(w)
72 return
73 }
74 if b {
75 next.ServeHTTP(w, r)
76 return
77 }
78 }
79 username, password, ok := r.BasicAuth()
80 if !ok {
81 renderUnauthorized(w)
82 return
83 }
84 ok, err := db.CheckAuth(username, password)
85 if err != nil {
86 log.Println(err.Error())
87 }
88 if !ok || err != nil {
89 renderUnauthorized(w)
90 return
91 }
92 next.ServeHTTP(w, r)
93 })
94 }
95
96 func renderNotFound(w http.ResponseWriter) {
97 w.WriteHeader(http.StatusNotFound)
98 w.Write([]byte(http.StatusText(http.StatusNotFound)))
99 w.Header().Set("Content-Type", "text/plain")
100 }
101
102 func renderUnauthorized(w http.ResponseWriter) {
103 w.Header().Set("WWW-Authenticate", `Basic realm="Please enter your username and password."`)
104 w.WriteHeader(http.StatusUnauthorized)
105 w.Write([]byte(http.StatusText(http.StatusUnauthorized)))
106 w.Header().Set("Content-Type", "text/plain")
107 }
108