package terse import ( "bufio" "fmt" "net/http" "net/url" "strings" "github.com/golang/groupcache/lru" ) type Handler struct { cache *lru.Cache } func (handler *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": handler.HandleGet(w, r) case "POST": handler.HandlePost(w, r) default: http.Error(w, fmt.Sprintf("Method \"%s\" Not Allowed", r.Method), http.StatusMethodNotAllowed) } } func (handler *Handler) HandleGet(w http.ResponseWriter, r *http.Request) { url := strings.Trim(r.URL.Path, "/") // If they gave us something like `//` // then jst grab the first part code := strings.SplitN(url, "/", 2)[0] value, ok := handler.cache.Get(code) // If the url exists in the cache and is a string, redirect to it if ok { switch url := value.(type) { case string: http.Redirect(w, r, url, http.StatusMovedPermanently) return } } http.NotFound(w, r) } func (handler *Handler) HandlePost(w http.ResponseWriter, r *http.Request) { // Parse url from body reader := bufio.NewScanner(r.Body) reader.Scan() rawurl := reader.Text() // Ensure url given is a real url cleanUrl, err := CleanURL(rawurl) if err != nil { http.Error(w, fmt.Sprintf("Invalid url \"%s\"", reader.Text()), http.StatusBadRequest) return } // Generate short code and store in cache code := GetShortCode([]byte(cleanUrl)) handler.cache.Add(code, cleanUrl) // Generate response url codeUrl := &url.URL{ Scheme: "https", Host: r.Host, Path: "/" + code, } fmt.Fprintf(w, codeUrl.String()) } func NewServer(bind string, maxEntries int) *http.Server { return &http.Server{ Addr: bind, Handler: &Handler{ cache: lru.New(maxEntries), }, } }