forked from
tangled.org/core
this repo has no description
1package state
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "net/http"
8
9 "github.com/bluesky-social/indigo/atproto/identity"
10 "github.com/go-chi/chi/v5"
11 "tangled.org/core/appview/models"
12)
13
14// allowedResponseHeaders is the set of headers we will forward from the knot
15// back to the client. everything else is stripped.
16var allowedResponseHeaders = map[string]bool{
17 "Content-Encoding": true,
18 "Transfer-Encoding": true,
19 "Cache-Control": true,
20 "Expires": true,
21 "Pragma": true,
22}
23
24func copyAllowedHeaders(dst, src http.Header) {
25 for k, vv := range src {
26 if allowedResponseHeaders[http.CanonicalHeaderKey(k)] {
27 for _, v := range vv {
28 dst.Add(k, v)
29 }
30 }
31 }
32}
33
34func setGitHeaders(w http.ResponseWriter, contentType string) {
35 w.Header().Set("Content-Type", contentType)
36 w.Header().Set("Content-Disposition", "attachment")
37 w.Header().Set("X-Content-Type-Options", "nosniff")
38}
39
40func (s *State) InfoRefs(w http.ResponseWriter, r *http.Request) {
41 repo := r.Context().Value("repo").(*models.Repo)
42
43 scheme := "https"
44 if s.config.Core.Dev {
45 scheme = "http"
46 }
47
48 service := r.URL.Query().Get("service")
49 var contentType string
50 switch service {
51 case "git-receive-pack":
52 contentType = "application/x-git-receive-pack-advertisement"
53 default:
54 contentType = "application/x-git-upload-pack-advertisement"
55 go s.notifier.Clone(context.Background(), repo)
56 }
57
58 targetURL := fmt.Sprintf("%s://%s/%s/info/refs?%s", scheme, repo.Knot, repo.RepoIdentifier(), r.URL.RawQuery)
59 s.proxyRequest(w, r, targetURL, contentType)
60}
61
62func (s *State) UploadArchive(w http.ResponseWriter, r *http.Request) {
63 repo := r.Context().Value("repo").(*models.Repo)
64
65 scheme := "https"
66 if s.config.Core.Dev {
67 scheme = "http"
68 }
69
70 targetURL := fmt.Sprintf("%s://%s/%s/git-upload-archive?%s", scheme, repo.Knot, repo.RepoIdentifier(), r.URL.RawQuery)
71 s.proxyRequest(w, r, targetURL, "application/x-git-upload-archive-result")
72}
73
74func (s *State) UploadPack(w http.ResponseWriter, r *http.Request) {
75 repo := r.Context().Value("repo").(*models.Repo)
76
77 scheme := "https"
78 if s.config.Core.Dev {
79 scheme = "http"
80 }
81
82 targetURL := fmt.Sprintf("%s://%s/%s/git-upload-pack?%s", scheme, repo.Knot, repo.RepoIdentifier(), r.URL.RawQuery)
83 s.proxyRequest(w, r, targetURL, "application/x-git-upload-pack-result")
84}
85
86func (s *State) ReceivePack(w http.ResponseWriter, r *http.Request) {
87 repo := r.Context().Value("repo").(*models.Repo)
88
89 scheme := "https"
90 if s.config.Core.Dev {
91 scheme = "http"
92 }
93
94 targetURL := fmt.Sprintf("%s://%s/%s/git-receive-pack?%s", scheme, repo.Knot, repo.RepoIdentifier(), r.URL.RawQuery)
95 s.proxyRequest(w, r, targetURL, "application/x-git-receive-pack-result")
96}
97
98func (s *State) proxyRequest(w http.ResponseWriter, r *http.Request, targetURL string, contentType string) {
99 client := &http.Client{}
100
101 proxyReq, err := http.NewRequest(r.Method, targetURL, r.Body)
102 if err != nil {
103 http.Error(w, err.Error(), http.StatusInternalServerError)
104 return
105 }
106
107 proxyReq.Header = r.Header.Clone()
108
109 repoOwnerHandle := chi.URLParam(r, "user")
110 if id, ok := r.Context().Value("resolvedId").(identity.Identity); ok && !id.Handle.IsInvalidHandle() {
111 repoOwnerHandle = id.Handle.String()
112 }
113 proxyReq.Header.Set("x-tangled-repo-owner-handle", repoOwnerHandle)
114
115 resp, err := client.Do(proxyReq)
116 if err != nil {
117 http.Error(w, err.Error(), http.StatusInternalServerError)
118 return
119 }
120 defer resp.Body.Close()
121
122 // selectively copy only allowed headers
123 copyAllowedHeaders(w.Header(), resp.Header)
124
125 setGitHeaders(w, contentType)
126
127 w.WriteHeader(resp.StatusCode)
128
129 if _, err := io.Copy(w, resp.Body); err != nil {
130 http.Error(w, err.Error(), http.StatusInternalServerError)
131 return
132 }
133}