Example #1
0
func GetMirrorMapUrl(mirrors Mirrors, clientInfo network.GeoIPRecord) string {
	var buffer bytes.Buffer
	buffer.WriteString("//maps.googleapis.com/maps/api/staticmap?size=520x320&sensor=false&visual_refresh=true")

	if clientInfo.IsValid() {
		buffer.WriteString(fmt.Sprintf("&markers=size:mid|color:red|%f,%f", clientInfo.Latitude, clientInfo.Longitude))
	}

	count := 1
	for i, mirror := range mirrors {
		if count > 9 {
			break
		}
		if i == 0 && clientInfo.IsValid() {
			// Draw a path between the client and the mirror
			buffer.WriteString(fmt.Sprintf("&path=color:0x17ea0bdd|weight:5|%f,%f|%f,%f",
				clientInfo.Latitude, clientInfo.Longitude,
				mirror.Latitude, mirror.Longitude))
		}
		color := "blue"
		if mirror.Weight > 0 {
			color = "green"
		}
		buffer.WriteString(fmt.Sprintf("&markers=color:%s|label:%d|%f,%f", color, count, mirror.Latitude, mirror.Longitude))
		count++
	}
	return buffer.String()
}
Example #2
0
func TestByRank_Less(t *testing.T) {
	rand.Seed(time.Now().UnixNano())

	/* */

	c := network.GeoIPRecord{}
	if c.IsValid() {
		t.Fatalf("GeoIPRecord is supposed to be invalid")
	}

	/* */

	// Generate two identical slices
	m1 := generateSimpleMirrorList(50)
	m2 := generateSimpleMirrorList(50)

	// Mirrors are indentical (besides name) so ByRank is expected
	// to randomize their order.
	sort.Sort(ByRank{m1, c})

	differences := 0
	for i, m := range m1 {
		if m.ID != m2[i].ID {
			differences++
		}
	}

	if differences == 0 {
		t.Fatalf("Result is supposed to be randomized")
	} else if differences < 10 {
		t.Fatalf("Too many similarities, something's wrong?")
	}

	// Sort again, just to be sure the result is different
	m3 := generateSimpleMirrorList(50)
	sort.Sort(ByRank{m3, c})

	differences = 0
	for i, m := range m3 {
		if m.ID != m1[i].ID {
			differences++
		}
	}

	if differences == 0 {
		t.Fatalf("Result is supposed to be different from previous run")
	} else if differences < 10 {
		t.Fatalf("Too many similarities, something's wrong?")
	}

	/* */

	c = network.GeoIPRecord{
		GeoIPRecord: &geoip.GeoIPRecord{
			CountryCode:   "FR",
			ContinentCode: "EU",
		},
		ASNum: 4444,
	}
	if !c.IsValid() {
		t.Fatalf("GeoIPRecord is supposed to be valid")
	}

	/* asnum */

	m := Mirrors{
		Mirror{
			ID:    "M0",
			Asnum: 6666,
		},
		Mirror{
			ID:    "M1",
			Asnum: 5555,
		},
		Mirror{
			ID:    "M2",
			Asnum: 4444,
		},
		Mirror{
			ID:    "M3",
			Asnum: 6666,
		},
	}

	sort.Sort(ByRank{m, c})

	if !matchingMirrorOrder(m, []string{"M2", "M0", "M1", "M3"}) {
		t.Fatalf("Order doesn't seem right: %s, expected M2, M0, M1, M3", formatMirrorOrder(m))
	}

	/* distance */

	m = Mirrors{
		Mirror{
			ID:       "M0",
			Distance: 1000.0,
		},
		Mirror{
			ID:       "M1",
			Distance: 999.0,
		},
		Mirror{
			ID:       "M2",
			Distance: 1000.0,
		},
		Mirror{
			ID:       "M3",
			Distance: 888.0,
		},
	}

	sort.Sort(ByRank{m, c})

	if !matchingMirrorOrder(m, []string{"M3", "M1", "M0", "M2"}) {
		t.Fatalf("Order doesn't seem right: %s, expected M3, M1, M0, M2", formatMirrorOrder(m))
	}

	/* countrycode */

	m = Mirrors{
		Mirror{
			ID:            "M0",
			CountryFields: []string{"IT", "UK"},
		},
		Mirror{
			ID:            "M1",
			CountryFields: []string{"IT", "UK"},
		},
		Mirror{
			ID:            "M2",
			CountryFields: []string{"IT", "FR"},
		},
		Mirror{
			ID:            "M3",
			CountryFields: []string{"FR", "UK"},
		},
	}

	sort.Sort(ByRank{m, c})

	if !matchingMirrorOrder(m, []string{"M2", "M3", "M0", "M1"}) {
		t.Fatalf("Order doesn't seem right: %s, expected M2, M3, M0, M1", formatMirrorOrder(m))
	}

	/* continentcode */

	c = network.GeoIPRecord{
		GeoIPRecord: &geoip.GeoIPRecord{
			ContinentCode: "EU",
		},
		ASNum: 4444,
	}

	m = Mirrors{
		Mirror{
			ID:            "M0",
			ContinentCode: "NA",
		},
		Mirror{
			ID:            "M1",
			ContinentCode: "NA",
		},
		Mirror{
			ID:            "M2",
			ContinentCode: "EU",
		},
		Mirror{
			ID:            "M3",
			ContinentCode: "NA",
		},
	}

	sort.Sort(ByRank{m, c})

	if !matchingMirrorOrder(m, []string{"M2", "M0", "M1", "M3"}) {
		t.Fatalf("Order doesn't seem right: %s, expected M2, M0, M1, M3", formatMirrorOrder(m))
	}

	/* */

	c = network.GeoIPRecord{
		GeoIPRecord: &geoip.GeoIPRecord{
			CountryCode:   "FR",
			ContinentCode: "EU",
		},
		ASNum: 4444,
	}

	m = Mirrors{
		Mirror{
			ID:            "M0",
			Distance:      100.0,
			CountryFields: []string{"IT", "FR"},
			ContinentCode: "EU",
		},
		Mirror{
			ID:            "M1",
			Distance:      200.0,
			CountryFields: []string{"FR", "CH"},
			ContinentCode: "EU",
		},
		Mirror{
			ID:            "M2",
			Distance:      1000.0,
			CountryFields: []string{"UK", "DE"},
			Asnum:         4444,
		},
	}

	sort.Sort(ByRank{m, c})

	if !matchingMirrorOrder(m, []string{"M2", "M0", "M1"}) {
		t.Fatalf("Order doesn't seem right: %s, expected M2, M0, M1", formatMirrorOrder(m))
	}
}
Example #3
0
func (h DefaultEngine) Selection(ctx *Context, cache *mirrors.Cache, fileInfo *filesystem.FileInfo, clientInfo network.GeoIPRecord) (mlist mirrors.Mirrors, excluded mirrors.Mirrors, err error) {
	// Get details about the requested file
	*fileInfo, err = cache.GetFileInfo(fileInfo.Path)
	if err != nil {
		return
	}

	// Prepare and return the list of all potential mirrors
	mlist, err = cache.GetMirrors(fileInfo.Path, clientInfo)
	if err != nil {
		return
	}

	// Filter
	safeIndex := 0
	excluded = make([]mirrors.Mirror, 0, len(mlist))
	var closestMirror float32
	var farthestMirror float32
	for i, m := range mlist {
		// Does it support http? Is it well formated?
		if !strings.HasPrefix(m.HttpURL, "http://") && !strings.HasPrefix(m.HttpURL, "https://") {
			m.ExcludeReason = "Invalid URL"
			goto delete
		}
		// Is it enabled?
		if !m.Enabled {
			m.ExcludeReason = "Disabled"
			goto delete
		}
		// Is it up?
		if !m.Up {
			if m.ExcludeReason == "" {
				m.ExcludeReason = "Down"
			}
			goto delete
		}
		// Is it the same size as source?
		if m.FileInfo != nil {
			if m.FileInfo.Size != fileInfo.Size {
				m.ExcludeReason = "File size mismatch"
				goto delete
			}
		}
		// Is it configured to serve its continent only?
		if m.ContinentOnly {
			if !clientInfo.IsValid() || clientInfo.ContinentCode != m.ContinentCode {
				m.ExcludeReason = "Continent only"
				goto delete
			}
		}
		// Is it configured to serve its country only?
		if m.CountryOnly {
			if !clientInfo.IsValid() || !utils.IsInSlice(clientInfo.CountryCode, m.CountryFields) {
				m.ExcludeReason = "Country only"
				goto delete
			}
		}
		// Is it in the same AS number?
		if m.ASOnly {
			if !clientInfo.IsValid() || clientInfo.ASNum != m.Asnum {
				m.ExcludeReason = "AS only"
				goto delete
			}
		}
		if safeIndex == 0 {
			closestMirror = m.Distance
		} else if closestMirror > m.Distance {
			closestMirror = m.Distance
		}
		if m.Distance > farthestMirror {
			farthestMirror = m.Distance
		}
		mlist[safeIndex] = mlist[i]
		safeIndex++
		continue
	delete:
		excluded = append(excluded, m)
	}

	// Reduce the slice to its new size
	mlist = mlist[:safeIndex]

	if !clientInfo.IsValid() {
		// Shuffle the list
		//XXX Should we use the fallbacks instead?
		for i := range mlist {
			j := rand.Intn(i + 1)
			mlist[i], mlist[j] = mlist[j], mlist[i]
		}

		// Shortcut
		if !ctx.IsMirrorlist() {
			// Reduce the number of mirrors to process
			mlist = mlist[:utils.Min(5, len(mlist))]
		}
		return
	}

	// We're not interested in divisions by zero
	if closestMirror == 0 {
		closestMirror = math.SmallestNonzeroFloat32
	}

	/* Weight distribution for random selection [Probabilistic weight] */

	// Compute score for each mirror and return the mirrors eligible for weight distribution.
	// This includes:
	// - mirrors found in a 1.5x (configurable) range from the closest mirror
	// - mirrors targeting the given country (as primary or secondary)
	// - mirrors being in the same AS number
	totalScore := 0
	baseScore := int(farthestMirror)
	weights := map[string]int{}
	for i := 0; i < len(mlist); i++ {
		m := &mlist[i]

		m.ComputedScore = baseScore - int(m.Distance) + 1

		if m.Distance <= closestMirror*GetConfig().WeightDistributionRange {
			score := (float32(baseScore) - m.Distance)
			if !utils.IsPrimaryCountry(clientInfo, m.CountryFields) {
				score /= 2
			}
			m.ComputedScore += int(score)
		} else if utils.IsPrimaryCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += int(float32(baseScore) - (m.Distance * 5))
		} else if utils.IsAdditionalCountry(clientInfo, m.CountryFields) {
			m.ComputedScore += int(float32(baseScore) - closestMirror)
		}

		if m.Asnum == clientInfo.ASNum {
			m.ComputedScore += baseScore / 2
		}

		floatingScore := float64(m.ComputedScore) + (float64(m.ComputedScore) * (float64(m.Score) / 100)) + 0.5

		// The minimum allowed score is 1
		m.ComputedScore = int(math.Max(floatingScore, 1))

		if m.ComputedScore > baseScore {
			// The weight must always be > 0 to not break the randomization below
			totalScore += m.ComputedScore - baseScore
			weights[m.ID] = m.ComputedScore - baseScore
		}
	}

	// Get the final number of mirrors selected for weight distribution
	selected := len(weights)

	// Sort mirrors by computed score
	sort.Sort(mirrors.ByComputedScore{mlist})

	if selected > 1 {

		if ctx.IsMirrorlist() {
			// Don't reorder the results, just set the percentage
			for i := 0; i < selected; i++ {
				id := mlist[i].ID
				for j := 0; j < len(mlist); j++ {
					if mlist[j].ID == id {
						mlist[j].Weight = float32(float64(weights[id]) * 100 / float64(totalScore))
						break
					}
				}
			}
		} else {
			// Randomize the order of the selected mirrors considering their weights
			weightedMirrors := make([]mirrors.Mirror, selected)
			rest := totalScore
			for i := 0; i < selected; i++ {
				var id string
				rv := rand.Int31n(int32(rest))
				s := 0
				for k, v := range weights {
					s += v
					if int32(s) > rv {
						id = k
						break
					}
				}
				for _, m := range mlist {
					if m.ID == id {
						m.Weight = float32(float64(weights[id]) * 100 / float64(totalScore))
						weightedMirrors[i] = m
						break
					}
				}
				rest -= weights[id]
				delete(weights, id)
			}

			// Replace the head of the list by its reordered counterpart
			mlist = append(weightedMirrors, mlist[selected:]...)

			// Reduce the number of mirrors to return
			v := math.Min(math.Min(5, float64(selected)), float64(len(mlist)))
			mlist = mlist[:int(v)]
		}
	} else if selected == 1 && len(mlist) > 0 {
		mlist[0].Weight = 100
	}
	return
}