-
Notifications
You must be signed in to change notification settings - Fork 1
/
vanity_server.go
118 lines (113 loc) · 2.82 KB
/
vanity_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
118
package main
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"path"
"strings"
"github.com/nofeaturesonlybugs/errors"
"github.com/nofeaturesonlybugs/routines"
)
// VanityServer is an http server that serves vanity urls for a domain.
type VanityServer struct {
routines.Service
//
conf DomainConf
httpd *http.Server
}
// NewVanityServer creates a new Server type from the configuration.
func NewVanityServer(conf DomainConf) (*VanityServer, error) {
var err error
var cert tls.Certificate
var tlsConfig *tls.Config
//
// Validate host:port configuration.
_, _, err = net.SplitHostPort(conf.Listen)
if err != nil {
return nil, errors.Errorf("Invalid host:port value @ %v", conf.Listen)
}
//
// Check for TLS
if conf.Certs.Private != "" && conf.Certs.Public != "" {
if cert, err = tls.LoadX509KeyPair(conf.Certs.Public, conf.Certs.Private); err != nil {
return nil, errors.Go(err)
}
tlsConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
}
}
//
// Map our modules configuration for faster lookups in the http handler.
modules := map[string]VanityTemplateData{}
for _, repo := range conf.Repos {
for _, module := range repo.Module {
modules["/"+module] = VanityTemplateData{
Root: path.Join(conf.Name, module),
RepoURL: repo.Repo,
VCS: repo.VCS,
}
}
}
//
// Our http handler.
handler := func(w http.ResponseWriter, r *http.Request) {
var module VanityTemplateData
var ok bool
var err error
//
// Make a local copy.
modules := modules
if !strings.Contains(r.URL.RawQuery, "go-get=1") {
http.NotFound(w, r)
return
} else if module, ok = modules[r.URL.Path]; !ok {
http.NotFound(w, r)
return
}
if err = VanityTemplate.Execute(w, module); err != nil {
fmt.Printf("error during render: %v", err.Error()) // TODO Logging facility would be better.
}
}
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(handler))
//
rv := &VanityServer{
conf: conf,
httpd: &http.Server{
Addr: conf.Listen,
Handler: mux,
TLSConfig: tlsConfig,
},
}
rv.Service = routines.NewService(rv.start)
//
return rv, nil
}
// start starts the httpd listener.
func (me *VanityServer) start(rtns routines.Routines) error {
var listener net.Listener
var err error
if me.httpd.TLSConfig == nil {
if listener, err = net.Listen("tcp", me.conf.Listen); err != nil {
return errors.Go(err)
}
} else {
if listener, err = tls.Listen("tcp", me.conf.Listen, me.httpd.TLSConfig); err != nil {
return errors.Go(err)
}
}
fmt.Printf("Listening on %v for %v\n", me.conf.Listen, me.conf.Name) //TODO log better
//
rtns.Go(func() {
defer fmt.Printf("Stopped %v for %v\n", me.conf.Listen, me.conf.Name) // TODO log better
me.httpd.Serve(listener)
})
rtns.Go(func() {
<-rtns.Done()
me.httpd.Shutdown(context.Background())
})
//
return nil
}