-
Notifications
You must be signed in to change notification settings - Fork 0
/
gopkg.go
177 lines (152 loc) · 5.1 KB
/
gopkg.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Package gopkg implements quick & simple go vanity package import paths.
//
// Vanity go package import paths give a cleaner appearance to go projects by separating the source code location from
// the import path. It also gives flexibility to developers by allowing them to change a project's source code hosting
// platform without requiring the project to be renamed.
// Finally, it allows projects hosted on various platforms to be grouped under a common import path.
//
// Within a Caddyfile, new go packages are added using the gopkg directive:
//
// gopkg <path> [<vcs>] <uri>
//
// The <path> argument corresponds to the path component of the vanity import path, e.g. for "magnax.ca/caddy/gopkg",
// the path would be "/caddy/gopkg".
// The <vcs> argument is optional, and defaults to "git". If it is specified, it is used to indicate which version
// control system is being used to manage the source.
// The <uri> argument corresponds to the URL/URL of the source code repository. Any format supported by the given VCS
// and the "go get" tool is can be used, as gopkg does not attempt to validate it.
package gopkg
import (
"fmt"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"html/template"
"net/http"
)
// DefaultTemplate is the default HTML template used as a response.
const DefaultTemplate = `<html>
<head>
<meta name="go-import" content="{{.Host}}{{.Path}} {{.Vcs}} {{.URL}}">
</head>
<body>
go get {{.Host}}{{.Path}}
</body>
</html>
`
func init() {
caddy.RegisterModule(GoPackage{})
httpcaddyfile.RegisterDirective("gopkg", parseCaddyFile)
}
// GoPackage implements vanity go package import paths.
//
// Vanity go package import paths give a cleaner appearance to go projects by separating the source code location from
// the import path. It also gives flexibility to developers by allowing them to change a project's source code hosting
// platform without requiring the project to be renamed. Finally, it allows projects hosted on various platforms to be
// grouped under a common import path.
type GoPackage struct {
// Path is the HTTP path component of the vanity import path.
//
// Given a vanity import path of `web.site/package/name`, the path would be `/package/name`.
Path string `json:"path"`
// Vcs is the version control system used by the package.
//
// If empty, the default is `git`.
// Valid values include `git`, `hg`, `svn`, `bzr`, `cvs`. Basically, any version control system that go knows how to address.
Vcs string `json:"vcs,omitempty"`
// URL is the URL of the package's source.
//
// This is where the go tool will go to download the source code.
URL string `json:"url"`
// Template is the template used when returning a response (instead of redirecting).
Template *template.Template
}
func (m GoPackage) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.gopkg",
New: func() caddy.Module {
return new(GoPackage)
},
}
}
// parseCaddyFile parses the gopkg directive in a caddyfile.
//
// The module is automatically mounted at the path of the go package. This shortens the middleware chain for
// non-gopkg requests.
func parseCaddyFile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
if !h.Next() {
return nil, h.ArgErr()
}
// Pretend the lookahead never happened
h.Reset()
var m = new(GoPackage)
err := m.UnmarshalCaddyfile(h.Dispenser)
if err != nil {
return nil, err
}
matcher := caddy.ModuleMap{
"path": h.JSON(caddyhttp.MatchPath{m.Path, m.Path + "/", m.Path + "/*"}),
}
return h.NewRoute(matcher, m), nil
}
// UnmarshalCaddyfile implements caddyfile.Unmarshaler. Syntax:
//
// gopkg <path> [<vcs>] <uri>
//
func (m *GoPackage) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if !d.Args(&m.Path) {
return d.ArgErr()
}
args := d.RemainingArgs()
switch len(args) {
case 2:
m.Vcs = args[0]
args = args[1:]
fallthrough
case 1:
m.URL = args[0]
default:
return d.ArgErr()
}
}
return nil
}
func (m *GoPackage) Provision(ctx caddy.Context) error {
if m.Vcs == "" {
m.Vcs = "git"
}
if m.Template == nil {
tpl, err := template.New("Package").Parse(DefaultTemplate)
if err != nil {
return fmt.Errorf("parsing default gopkg template: %v", err)
}
m.Template = tpl
}
return nil
}
func (m GoPackage) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// If go-get is not present, it's most likely a browser request. So let's redirect.
if r.FormValue("go-get") != "1" {
http.Redirect(w, r, m.URL, http.StatusTemporaryRedirect)
return nil
}
err := m.Template.Execute(w, struct {
Host string
Path string
Vcs string
URL string
}{r.Host, m.Path, m.Vcs, m.URL})
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
w.Header().Set("Content-Type", "text/html")
return nil
}
// Interface guards
var (
_ caddy.Provisioner = (*GoPackage)(nil)
_ caddyhttp.MiddlewareHandler = (*GoPackage)(nil)
_ caddyfile.Unmarshaler = (*GoPackage)(nil)
)