Skip to content

Commit

Permalink
Merge pull request #14 from LinuxSuRen/server-reachable-check
Browse files Browse the repository at this point in the history
Reject unreachbale candidate servers
  • Loading branch information
LinuxSuRen authored Oct 25, 2021
2 parents ac6fcd3 + 38c7222 commit 8b740d5
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 9 deletions.
1 change: 1 addition & 0 deletions cmd/server/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func createServerCommand() (cmd *cobra.Command) {
func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
switch o.mode {
case "server":
http.HandleFunc("/health", server.HealthHandler)
http.HandleFunc("/", server.GogetHandler)
if err = server.IntervalSelfRegistry(o.proxyCenter, o.externalAddress, time.Minute*1); err != nil {
err = fmt.Errorf("failed to self registry to the center proxy, error: %v", err)
Expand Down
32 changes: 28 additions & 4 deletions pkg/proxy/candidate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package proxy

import (
"github.com/stretchr/testify/assert"
"sort"
"testing"
"time"
)

func TestCandidate(t *testing.T) {
func TestCandidateSlice(t *testing.T) {
candidates := candidateSlice{
[]candidate{{
address: "fake",
Expand All @@ -30,6 +31,7 @@ func TestCandidate(t *testing.T) {
assert.True(t, ok, "should be able to find the alive candidate which was updated")

emptyCandidates := candidateSlice{}
assert.Nil(t, emptyCandidates.first())
emptyCandidates.addCandidate("fake")
_, ok = emptyCandidates.findAlive()
assert.True(t, ok, "should be able to find the alive candidate")
Expand All @@ -39,7 +41,7 @@ func TestCandidate(t *testing.T) {

expiredCandidates := candidateSlice{
candidates: []candidate{{
address: "fake",
address: "fake",
heartBeat: time.Now().Add(time.Minute * -10),
}},
}
Expand All @@ -60,7 +62,7 @@ func TestCandidatesHelper(t *testing.T) {
// valid candidates array
candidatesArray = []interface{}{
map[interface{}]interface{}{
"address": "fake",
"address": "fake",
"heartBeat": time.Now().Format(timeFormat),
},
}
Expand All @@ -73,7 +75,7 @@ func TestCandidatesHelper(t *testing.T) {
// from map
candidatesMap := []map[interface{}]interface{}{
{
"address": "fake",
"address": "fake",
"heartBeat": time.Now(),
},
}
Expand All @@ -83,3 +85,25 @@ func TestCandidatesHelper(t *testing.T) {
assert.True(t, ok)
assert.Equal(t, "fake", aliveCandidate.address)
}

func TestCandidateSliceSort(t *testing.T) {
candidates := &candidateSlice{candidates: []candidate{{
address: "one",
heartBeat: time.Now().Add(time.Minute),
}, {
address: "two",
heartBeat: time.Now().Add(time.Minute * 2),
}}}
sort.Sort(candidates)
firstCandidate := candidates.first()
assert.NotNil(t, firstCandidate)
assert.Equal(t, firstCandidate.address, "two")
}

func TestCandidate(t *testing.T) {
candidate := NewCandidate("http://fake")
assert.Equal(t, "fake", candidate.getHost())

candidate = NewCandidate("https://fake")
assert.Equal(t, "fake", candidate.getHost())
}
13 changes: 8 additions & 5 deletions pkg/proxy/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ func isValid(uri string) bool {

// RegistryHandler receive the proxy registry request
func RegistryHandler(w http.ResponseWriter, r *http.Request) {
address := r.URL.Query().Get("address")
address = strings.ReplaceAll(address, "http://", "")
address = strings.ReplaceAll(address, "https://", "")
candidate := NewCandidate(r.URL.Query().Get("address"))
if !candidate.reachable() {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(fmt.Sprintf("%s is not reachable", candidate.address)))
return
}

var (
candidates *candidateSlice
Expand All @@ -59,9 +62,9 @@ func RegistryHandler(w http.ResponseWriter, r *http.Request) {
candidates = newFromArray(candidatesRaw)
}

candidates.addCandidate(address)
candidates.addCandidate(candidate.getHost())

fmt.Println("receive candidate server", address)
fmt.Println("receive candidate server", candidate.getHost())
if err := saveCandidates(candidates); err == nil {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
Expand Down
56 changes: 56 additions & 0 deletions pkg/proxy/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package proxy

import (
"fmt"
"net/http"
"sort"
"strings"
"time"
)

Expand All @@ -11,6 +14,36 @@ type candidate struct {
expired bool
}

// NewCandidate creates a new candidate instance
func NewCandidate(address string) *candidate {
return &candidate{address: address}
}

func (c *candidate) reachable() bool {
if c.address == "" {
return false
}

var address string
if strings.HasPrefix(c.address, "https://") || strings.HasPrefix(c.address, "http://") {
address = c.address
} else {
address = fmt.Sprintf("http://%s", c.address)
}

client := http.DefaultClient
client.Timeout = time.Second * 3
resp, err := client.Get(address)
return err == nil && resp.StatusCode == http.StatusOK
}

func (c *candidate) getHost() (address string) {
address = c.address
address = strings.ReplaceAll(address, "http://", "")
address = strings.ReplaceAll(address, "https://", "")
return
}

var aliveDuration = time.Minute * 2

const timeFormat = time.RFC3339
Expand All @@ -19,7 +52,16 @@ type candidateSlice struct {
candidates []candidate
}

func (c *candidateSlice) first() *candidate {
if len(c.candidates) > 0 {
return &c.candidates[0]
}
return nil
}

func (c *candidateSlice) findAlive() (candidate, bool) {
sort.Sort(c)

for i, _ := range c.candidates {
candidateItem := c.candidates[i]
if candidateItem.address != "" && candidateItem.heartBeat.Add(aliveDuration).After(time.Now()) {
Expand Down Expand Up @@ -59,6 +101,20 @@ func (c *candidateSlice) size() int {
return len(c.candidates)
}

func (c *candidateSlice) Len() int {
return c.size()
}

func (c *candidateSlice) Less(i, j int) bool {
left := c.candidates[i].heartBeat
right := c.candidates[j].heartBeat
return left.After(right)
}

func (c *candidateSlice) Swap(i, j int) {
c.candidates[i], c.candidates[j] = c.candidates[j], c.candidates[i]
}

func (c *candidateSlice) getMap() (result []map[interface{}]interface{}) {
result = make([]map[interface{}]interface{}, 0)
for i, _ := range c.candidates {
Expand Down
9 changes: 9 additions & 0 deletions pkg/server/health.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package server

import "net/http"

// HealthHandler is the handler of the server health request
func HealthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
}

0 comments on commit 8b740d5

Please sign in to comment.