violet/servers/https.go

105 lines
3.2 KiB
Go
Raw Permalink Normal View History

2023-04-21 03:21:46 +01:00
package servers
2023-04-22 18:11:21 +01:00
import (
"crypto/tls"
"fmt"
2023-07-22 01:11:47 +01:00
"github.com/1f349/violet/favicons"
"github.com/1f349/violet/servers/conf"
"github.com/1f349/violet/utils"
2023-04-22 18:11:21 +01:00
"github.com/sethvargo/go-limiter/httplimit"
"github.com/sethvargo/go-limiter/memorystore"
"log"
"net/http"
2023-06-20 17:33:43 +01:00
"path"
"runtime"
2023-04-22 18:11:21 +01:00
"time"
)
// NewHttpsServer creates and runs a http server containing the public https
// endpoints for the reverse proxy.
func NewHttpsServer(conf *conf.Conf) *http.Server {
2023-08-12 15:58:41 +01:00
r := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
log.Printf("[Debug] Request: %s - '%s' - '%s' - '%s' - len: %d - thread: %d\n", req.Method, req.URL.String(), req.RemoteAddr, req.Host, req.ContentLength, runtime.NumGoroutine())
2023-08-12 15:58:41 +01:00
conf.Router.ServeHTTP(rw, req)
})
favMiddleware := setupFaviconMiddleware(conf.Favicons, r)
rateLimiter := setupRateLimiter(conf.RateLimit, favMiddleware)
2023-06-04 22:28:48 +01:00
return &http.Server{
2023-11-03 08:09:29 +00:00
Addr: conf.HttpsListen,
Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
rateLimiter.ServeHTTP(rw, req)
}),
2023-04-22 18:11:21 +01:00
TLSConfig: &tls.Config{GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
// error out on invalid domains
if !conf.Domains.IsValid(info.ServerName) {
return nil, fmt.Errorf("invalid hostname used: '%s'", info.ServerName)
}
// find a certificate
cert := conf.Certs.GetCertForDomain(info.ServerName)
if cert == nil {
return nil, fmt.Errorf("failed to find certificate for: '%s'", info.ServerName)
}
// time to return
return cert, nil
}},
ReadTimeout: 150 * time.Second,
ReadHeaderTimeout: 150 * time.Second,
WriteTimeout: 150 * time.Second,
IdleTimeout: 150 * time.Second,
MaxHeaderBytes: 4096000,
}
}
// setupRateLimiter is an internal function to create a middleware to manage
// rate limits.
2023-06-03 19:33:06 +01:00
func setupRateLimiter(rateLimit uint64, next http.Handler) http.Handler {
2023-04-22 18:11:21 +01:00
// create memory store
store, err := memorystore.New(&memorystore.Config{
Tokens: rateLimit,
Interval: time.Minute,
})
if err != nil {
log.Fatalln(err)
}
// create a middleware using ips as the key for rate limits
middleware, err := httplimit.NewMiddleware(store, httplimit.IPKeyFunc())
if err != nil {
log.Fatalln(err)
}
2023-06-03 19:33:06 +01:00
return middleware.Handle(next)
}
func setupFaviconMiddleware(fav *favicons.Favicons, next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
2023-08-30 11:34:53 +01:00
if req.Header.Get("X-Violet-Loop-Detect") == "1" {
rw.WriteHeader(http.StatusLoopDetected)
_, _ = rw.Write([]byte("Detected a routing loop\n"))
return
}
2023-06-03 19:33:06 +01:00
if req.Header.Get("X-Violet-Raw-Favicon") != "1" {
switch req.URL.Path {
2023-06-20 17:33:43 +01:00
case "/favicon.svg", "/favicon.png", "/favicon.ico":
2023-06-03 19:33:06 +01:00
icons := fav.GetIcons(req.Host)
if icons == nil {
break
}
2023-06-20 17:33:43 +01:00
raw, contentType, err := icons.ProduceForExt(path.Ext(req.URL.Path))
2023-06-03 19:33:06 +01:00
if err != nil {
2023-06-20 17:33:43 +01:00
utils.RespondVioletError(rw, http.StatusTeapot, "No icon available")
2023-06-03 19:33:06 +01:00
return
}
2023-06-20 17:33:43 +01:00
rw.Header().Set("Content-Type", contentType)
2023-06-03 19:33:06 +01:00
rw.WriteHeader(http.StatusOK)
_, _ = rw.Write(raw)
return
}
}
next.ServeHTTP(rw, req)
})
2023-04-22 18:11:21 +01:00
}