• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

clarkezone / pocketshorten / 4266799365

24 Feb 2023 10:35PM UTC coverage: 37.701% (+0.1%) from 37.581%
4266799365

Pull #25

github

James Clarke
Return hash, version in liveness
Pull Request #25: Update liveness probe

6 of 6 new or added lines in 1 file covered. (100.0%)

351 of 931 relevant lines covered (37.7%)

1.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.68
/pkg/shortener/shortenhandler.go
1
package shortener
2

3
import (
4
        "fmt"
5
        "net/http"
6
        "strings"
7
        "time"
8

9
        "github.com/clarkezone/pocketshorten/pkg/config"
10
        clarkezoneLog "github.com/clarkezone/pocketshorten/pkg/log"
11
)
12

13
type storeLoader interface {
14
        Init(urlLookupService) error
15
}
16

17
type URLEntry struct {
18
        ShortLink       string
19
        DestinationLink string
20
        LinkGroup       string
21
        Created         time.Time
22
}
23

24
type urlLookupService interface {
25
        Store(string, *URLEntry) error
26
        Lookup(string) (*URLEntry, error)
27
        Count() int
28
        Ready() bool
29
}
30

31
// NewDictLookupHandler creates a new instance of type
32
//
33
//lint:ignore U1000 reason backend not selected
34
func NewDictLookupHandler(metricsprefix string) *ShortenHandler {
10✔
35
        clarkezoneLog.Debugf("newDictLookupHandler called with prefix %v", metricsprefix)
10✔
36
        vl := &viperLoader{}
10✔
37
        ds := newDictStore(vl)
10✔
38
        var ul urlLookupService = ds
10✔
39
        if metricsprefix != "" {
10✔
40
                ul = addMetrics(metricsprefix, ds)
×
41
        }
×
42
        lh := &ShortenHandler{storage: ul}
10✔
43
        return lh
10✔
44
}
45

46
// NewGrpcLookupHandler returns a new lookuphandler instance
47
func NewGrpcLookupHandler(metricsprefix string, s string) (*ShortenHandler, error) {
×
48
        // dictstore
×
49
        // grpcloader
×
50
        ds, err := newGrpcStore(s)
×
51
        ul := addMetrics(metricsprefix, ds)
×
52
        if err != nil {
×
53
                return nil, err
×
54
        }
×
55
        lh := &ShortenHandler{storage: ul}
×
56
        return lh, nil
×
57
}
58

59
// ShortenHandler core logic
60
type ShortenHandler struct {
61
        storage urlLookupService
62
}
63

64
// RegisterHandlers attaches handlers to Mux that is passed in
65
func (lh *ShortenHandler) RegisterHandlers(mux *http.ServeMux) {
1✔
66
        mux.HandleFunc("/", lh.redirectHandler)
1✔
67
        mux.HandleFunc("/ready", lh.readyHandler)
1✔
68
        mux.HandleFunc("/live", lh.liveHandler)
1✔
69
}
1✔
70

71
func (lh *ShortenHandler) redirectHandler(w http.ResponseWriter, r *http.Request) {
6✔
72
        if !lh.storage.Ready() {
6✔
73
                writeOutputError(w, "server error: not configured", http.StatusInternalServerError)
×
74
                return
×
75
        }
×
76
        //requested := r.URL.Query().Get("shortlink")
77
        requested, err := sanitize(r.URL.Path)
6✔
78

6✔
79
        // TODO update scalbility tests
6✔
80
        clarkezoneLog.Debugf("path: :%v: sanitized:%v:", requested)
6✔
81
        if err != nil {
7✔
82
                writeOutputError(w, fmt.Sprintf("input sanitization failed: unabled to process request %v", err), http.StatusBadRequest)
1✔
83
                return
1✔
84
        }
1✔
85

86
        if requested == "" {
6✔
87
                writeOutputError(w, "please supply shortlink query parameter", http.StatusNotFound)
1✔
88
                return
1✔
89
        }
1✔
90
        uri, err := lh.storage.Lookup(requested)
4✔
91
        if err != nil {
5✔
92
                writeOutputError(w, fmt.Sprintf("shortlink %v notfound", requested), http.StatusNotFound)
1✔
93
                return
1✔
94
        }
1✔
95
        clarkezoneLog.Debugf("redirecting to %v", uri)
3✔
96

3✔
97
        http.Redirect(w, r, uri.DestinationLink, http.StatusMovedPermanently)
3✔
98
}
99

100
func (lh *ShortenHandler) liveHandler(w http.ResponseWriter, r *http.Request) {
×
101
        w.WriteHeader(http.StatusOK)
×
102
        w.Header().Add("mime=type", "text/html")
×
103
        fmt.Fprintf(w, "Live. Version: %s, Hash: %s", config.VersionString, config.VersionHash)
×
104
}
×
105

106
func (lh *ShortenHandler) readyHandler(w http.ResponseWriter, r *http.Request) {
2✔
107
        if !lh.storage.Ready() {
3✔
108
                writeOutputError(w, "Service not available", http.StatusServiceUnavailable)
1✔
109
        } else {
2✔
110
                w.WriteHeader(http.StatusOK)
1✔
111
                w.Header().Add("mime=type", "text/html")
1✔
112
                fmt.Fprintf(w, "Ready with %d", lh.storage.Count())
1✔
113
        }
1✔
114
}
115

116
func writeOutputError(w http.ResponseWriter, message string, code int) {
4✔
117
        clarkezoneLog.Debugf("Error reported to user %v", message)
4✔
118
        http.Error(w, message, code)
4✔
119
}
4✔
120

121
func sanitize(input string) (string, error) {
6✔
122
        const maxinput int = 20
6✔
123
        sa := strings.TrimLeft(input, "/")
6✔
124
        clarkezoneLog.Debugf("sanitized path: %v", sa)
6✔
125
        le := len(sa)
6✔
126
        if len(sa) > maxinput {
7✔
127
                return "", fmt.Errorf("bad input expected < %v chars received %v chars", maxinput, le)
1✔
128
        }
1✔
129
        return sa, nil
5✔
130
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc