Skip to content

Commit

Permalink
short: auto create vcs alias if available
Browse files Browse the repository at this point in the history
For #4
  • Loading branch information
changkun committed Dec 16, 2020
1 parent 5b8274f commit 6433d09
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 21 deletions.
6 changes: 5 additions & 1 deletion db.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const (
prefixip = "redir:ip:"
)

var (
errExistedAlias = errors.New("alias is existed")
)

// arecord indicates an alias record that stores an short alias
// in data store with statistics regarding its UVs and PVs.
type arecord struct {
Expand Down Expand Up @@ -81,7 +85,7 @@ func (s *store) StoreAlias(ctx context.Context, a, l string) (err error) {
return
}
if !ok {
err = errors.New("alias already exists in data store")
err = errExistedAlias
return
}
return
Expand Down
82 changes: 62 additions & 20 deletions short.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ func shortCmd(ctx context.Context, operate op, alias, link string) (err error) {
return
}

// sHandler redirects ...
// sHandler redirects the current request to a known link if the alias
// is found in the redir store.
func (s *server) sHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

Expand All @@ -131,35 +132,24 @@ func (s *server) sHandler(w http.ResponseWriter, r *http.Request) {
}
}()

// statistic page
alias := strings.TrimSuffix(strings.TrimPrefix(r.URL.Path, conf.S.Prefix), "/")
if alias == "" {
err = s.stats(ctx, w)
return
}

checkdb := func(url string) (string, error) {
raw, err := s.db.FetchAlias(ctx, alias)
if err != nil {
return "", err
}
c := arecord{}
err = json.Unmarshal(StringToBytes(raw), &c)
if err != nil {
return "", err
}
if url != c.URL {
s.cache.Put(alias, c.URL)
url = c.URL
}
return url, nil
}

// figure out redirect location
url, ok := s.cache.Get(alias)
if !ok {
url, err = checkdb(url)
url, err = s.checkdb(ctx, alias)
if err != nil {
return
url, err = s.checkvcs(ctx, alias)
if err != nil {
return
}
}
s.cache.Put(alias, url)
}

// redirect the user immediate, but run pv/uv count in background
Expand All @@ -169,6 +159,58 @@ func (s *server) sHandler(w http.ResponseWriter, r *http.Request) {
go func() { s.visitCh <- visit{s.readIP(r), alias} }()
}

// checkdb checks whether the given alias is exsited in the redir database,
// and updates the in-memory cache if
func (s *server) checkdb(ctx context.Context, alias string) (string, error) {
raw, err := s.db.FetchAlias(ctx, alias)
if err != nil {
return "", err
}
c := arecord{}
err = json.Unmarshal(StringToBytes(raw), &c)
if err != nil {
return "", err
}
return c.URL, nil
}

// checkvcs checks whether the given alias is an repository on VCS, if so,
// then creates a new alias and returns url of the vcs repository.
func (s *server) checkvcs(ctx context.Context, alias string) (string, error) {

// construct the try path and make the request to vcs
repoPath := conf.X.RepoPath
if strings.HasSuffix(repoPath, "/*") {
repoPath = strings.TrimSuffix(repoPath, "/*")
}
tryPath := fmt.Sprintf("%s/%s", repoPath, alias)
resp, err := http.Get(tryPath)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK &&
resp.StatusCode != http.StatusMovedPermanently {
return "", fmt.Errorf("%s is not a repository", tryPath)
}

// figure out the new location
if resp.StatusCode == http.StatusMovedPermanently {
tryPath = resp.Header.Get("Location")
}

// store such a try path
err = s.db.StoreAlias(ctx, alias, tryPath)
if err != nil {
if errors.Is(err, errExistedAlias) {
return s.checkdb(ctx, alias)
}
return "", err
}

return tryPath, nil
}

// readIP implements a best effort approach to return the real client IP,
// it parses X-Real-IP and X-Forwarded-For in order to work properly with
// reverse-proxies such us: nginx or haproxy. Use X-Forwarded-For before
Expand Down

0 comments on commit 6433d09

Please sign in to comment.