Move routing to separate module

chubin/logging
Igor Chubin 2 years ago
parent ec264850a4
commit f27bf2d5b3

@ -1,2 +1,2 @@
srv: cmd/*.go srv: cmd/*.go internal/routing/*.go
go build -o srv cmd/*.go go build -o srv ./cmd/

@ -11,10 +11,12 @@ import (
"sync" "sync"
"time" "time"
"github.com/chubin/wttr.in/internal/routing"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
) )
type ResponseWithHeader struct { type responseWithHeader struct {
InProgress bool // true if the request is being processed InProgress bool // true if the request is being processed
Expires time.Time // expiration time of the cache entry Expires time.Time // expiration time of the cache entry
@ -29,7 +31,7 @@ type RequestProcessor struct {
peakRequest60 sync.Map peakRequest60 sync.Map
lruCache *lru.Cache lruCache *lru.Cache
stats *Stats stats *Stats
router Router router routing.Router
} }
// NewRequestProcessor returns new RequestProcessor. // NewRequestProcessor returns new RequestProcessor.
@ -55,9 +57,9 @@ func (rp *RequestProcessor) Start() {
rp.startPeakHandling() rp.startPeakHandling()
} }
func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader, error) { func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*responseWithHeader, error) {
var ( var (
response *ResponseWithHeader response *responseWithHeader
err error err error
) )
@ -67,7 +69,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
if rh := rp.router.Route(r); rh != nil { if rh := rp.router.Route(r); rh != nil {
result := rh.Response(r) result := rh.Response(r)
if result != nil { if result != nil {
return result, nil return fromCadre(result), nil
} }
} }
@ -90,7 +92,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
cacheBody, ok := rp.lruCache.Get(cacheDigest) cacheBody, ok := rp.lruCache.Get(cacheDigest)
if ok { if ok {
rp.stats.Inc("cache1") rp.stats.Inc("cache1")
cacheEntry := cacheBody.(ResponseWithHeader) cacheEntry := cacheBody.(responseWithHeader)
// if after all attempts we still have no answer, // if after all attempts we still have no answer,
// we try to make the query on our own // we try to make the query on our own
@ -101,7 +103,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
time.Sleep(30 * time.Millisecond) time.Sleep(30 * time.Millisecond)
cacheBody, ok = rp.lruCache.Get(cacheDigest) cacheBody, ok = rp.lruCache.Get(cacheDigest)
if ok && cacheBody != nil { if ok && cacheBody != nil {
cacheEntry = cacheBody.(ResponseWithHeader) cacheEntry = cacheBody.(responseWithHeader)
} }
} }
if cacheEntry.InProgress { if cacheEntry.InProgress {
@ -123,7 +125,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
} }
} }
rp.lruCache.Add(cacheDigest, ResponseWithHeader{InProgress: true}) rp.lruCache.Add(cacheDigest, responseWithHeader{InProgress: true})
response, err = get(r) response, err = get(r)
if err != nil { if err != nil {
@ -139,7 +141,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
return response, nil return response, nil
} }
func get(req *http.Request) (*ResponseWithHeader, error) { func get(req *http.Request) (*responseWithHeader, error) {
client := &http.Client{} client := &http.Client{}
@ -173,7 +175,7 @@ func get(req *http.Request) (*ResponseWithHeader, error) {
return nil, err return nil, err
} }
return &ResponseWithHeader{ return &responseWithHeader{
InProgress: false, InProgress: false,
Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second),
Body: body, Body: body,
@ -214,7 +216,7 @@ func dontCache(req *http.Request) bool {
// proxy_set_header X-Forwarded-Proto $scheme; // proxy_set_header X-Forwarded-Proto $scheme;
// //
// //
func redirectInsecure(req *http.Request) (*ResponseWithHeader, bool) { func redirectInsecure(req *http.Request) (*responseWithHeader, bool) {
if isPlainTextAgent(req.Header.Get("User-Agent")) { if isPlainTextAgent(req.Header.Get("User-Agent")) {
return nil, false return nil, false
} }
@ -236,7 +238,7 @@ The document has moved
</BODY></HTML> </BODY></HTML>
`, target)) `, target))
return &ResponseWithHeader{ return &responseWithHeader{
InProgress: false, InProgress: false,
Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second),
Body: body, Body: body,
@ -284,3 +286,14 @@ func ipFromAddr(s string) string {
} }
return s[:pos] return s[:pos]
} }
// fromCadre converts Cadre into a responseWithHeader.
func fromCadre(cadre *routing.Cadre) *responseWithHeader {
return &responseWithHeader{
Body: cadre.Body,
Expires: cadre.Expires,
StatusCode: 200,
InProgress: false,
}
}

@ -1,37 +0,0 @@
package main
import "net/http"
type Handler interface {
Response(*http.Request) *ResponseWithHeader
}
type routeFunc func(*http.Request) bool
type route struct {
routeFunc
Handler
}
type Router struct {
rt []route
}
func (r *Router) Route(req *http.Request) Handler {
for _, re := range r.rt {
if re.routeFunc(req) {
return re.Handler
}
}
return nil
}
func (r *Router) AddPath(path string, handler Handler) {
r.rt = append(r.rt, route{routePath(path), handler})
}
func routePath(path string) routeFunc {
return routeFunc(func(req *http.Request) bool {
return req.URL.Path == path
})
}

@ -6,6 +6,8 @@ import (
"net/http" "net/http"
"sync" "sync"
"time" "time"
"github.com/chubin/wttr.in/internal/routing"
) )
// Stats holds processed requests statistics. // Stats holds processed requests statistics.
@ -77,9 +79,8 @@ func (c *Stats) Show() []byte {
return b.Bytes() return b.Bytes()
} }
func (c *Stats) Response(*http.Request) *ResponseWithHeader { func (c *Stats) Response(*http.Request) *routing.Cadre {
return &ResponseWithHeader{ return &routing.Cadre{
Body: c.Show(), Body: c.Show(),
StatusCode: 200,
} }
} }

@ -1,9 +1,8 @@
module github.com/chubin/wttr.in/v2 module github.com/chubin/wttr.in
go 1.16 go 1.16
require ( require (
github.com/hashicorp/golang-lru v0.6.0 github.com/hashicorp/golang-lru v0.6.0
github.com/robfig/cron v1.2.0 github.com/robfig/cron v1.2.0
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

@ -2,6 +2,3 @@ github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o
github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -0,0 +1,71 @@
package routing
import (
"net/http"
"time"
)
// CadreFormat specifies how the shot data is formatted.
type CadreFormat int
const (
// CadreFormatANSI represents Terminal ANSI format.
CadreFormatANSI = iota
// CadreFormatHTML represents HTML.
CadreFormatHTML
// CadreFormatPNG represents PNG.
CadreFormatPNG
)
// Cadre contains result of a query execution.
type Cadre struct {
// Body contains the data of Cadre, formatted as Format.
Body []byte
// Format of the shot.
Format CadreFormat
// Expires contains the time of the Cadre expiration,
// or 0 if it does not expire.
Expires time.Time
}
// Handler can handle queries and return views.
type Handler interface {
Response(*http.Request) *Cadre
}
type routeFunc func(*http.Request) bool
type route struct {
routeFunc
Handler
}
// Router keeps a routing table, and finds queries handlers, based on its rules.
type Router struct {
rt []route
}
// Route returns a query handler based on its content.
func (r *Router) Route(req *http.Request) Handler {
for _, re := range r.rt {
if re.routeFunc(req) {
return re.Handler
}
}
return nil
}
// AddPath adds route for a static path.
func (r *Router) AddPath(path string, handler Handler) {
r.rt = append(r.rt, route{routePath(path), handler})
}
func routePath(path string) routeFunc {
return routeFunc(func(req *http.Request) bool {
return req.URL.Path == path
})
}
Loading…
Cancel
Save