2014-10-16 20:01:43 +02:00
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
|
2017-03-23 00:02:36 +01:00
|
|
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
2021-07-10 20:20:19 +02:00
|
|
|
Copyright (c) 2018-2020 Andrea Spacca.
|
|
|
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
2014-10-16 20:01:43 +02:00
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2017-03-22 18:09:21 +01:00
|
|
|
package server
|
2014-10-16 20:01:43 +02:00
|
|
|
|
|
|
|
import (
|
2021-07-23 11:20:49 +02:00
|
|
|
"fmt"
|
2014-11-13 21:41:43 +01:00
|
|
|
"math"
|
2014-10-16 20:01:43 +02:00
|
|
|
"net/http"
|
2014-11-13 21:41:43 +01:00
|
|
|
"strconv"
|
2014-10-16 20:01:43 +02:00
|
|
|
"strings"
|
2023-05-19 12:01:54 +02:00
|
|
|
"time"
|
2016-05-19 23:33:44 +02:00
|
|
|
|
|
|
|
"github.com/golang/gddo/httputil/header"
|
2014-10-16 20:01:43 +02:00
|
|
|
)
|
|
|
|
|
2014-11-13 21:41:43 +01:00
|
|
|
func formatNumber(format string, s uint64) string {
|
2021-08-19 22:45:30 +02:00
|
|
|
return renderFloat(format, float64(s))
|
2014-11-13 21:41:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var renderFloatPrecisionMultipliers = [10]float64{
|
|
|
|
1,
|
|
|
|
10,
|
|
|
|
100,
|
|
|
|
1000,
|
|
|
|
10000,
|
|
|
|
100000,
|
|
|
|
1000000,
|
|
|
|
10000000,
|
|
|
|
100000000,
|
|
|
|
1000000000,
|
|
|
|
}
|
|
|
|
|
|
|
|
var renderFloatPrecisionRounders = [10]float64{
|
|
|
|
0.5,
|
|
|
|
0.05,
|
|
|
|
0.005,
|
|
|
|
0.0005,
|
|
|
|
0.00005,
|
|
|
|
0.000005,
|
|
|
|
0.0000005,
|
|
|
|
0.00000005,
|
|
|
|
0.000000005,
|
|
|
|
0.0000000005,
|
|
|
|
}
|
|
|
|
|
2021-08-19 22:45:30 +02:00
|
|
|
func renderFloat(format string, n float64) string {
|
2014-11-13 21:41:43 +01:00
|
|
|
// Special cases:
|
|
|
|
// NaN = "NaN"
|
|
|
|
// +Inf = "+Infinity"
|
|
|
|
// -Inf = "-Infinity"
|
|
|
|
if math.IsNaN(n) {
|
|
|
|
return "NaN"
|
|
|
|
}
|
|
|
|
if n > math.MaxFloat64 {
|
|
|
|
return "Infinity"
|
|
|
|
}
|
|
|
|
if n < -math.MaxFloat64 {
|
|
|
|
return "-Infinity"
|
|
|
|
}
|
|
|
|
|
|
|
|
// default format
|
|
|
|
precision := 2
|
|
|
|
decimalStr := "."
|
|
|
|
thousandStr := ","
|
|
|
|
positiveStr := ""
|
|
|
|
negativeStr := "-"
|
|
|
|
|
|
|
|
if len(format) > 0 {
|
|
|
|
// If there is an explicit format directive,
|
|
|
|
// then default values are these:
|
|
|
|
precision = 9
|
|
|
|
thousandStr = ""
|
|
|
|
|
|
|
|
// collect indices of meaningful formatting directives
|
|
|
|
formatDirectiveChars := []rune(format)
|
|
|
|
formatDirectiveIndices := make([]int, 0)
|
|
|
|
for i, char := range formatDirectiveChars {
|
|
|
|
if char != '#' && char != '0' {
|
|
|
|
formatDirectiveIndices = append(formatDirectiveIndices, i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(formatDirectiveIndices) > 0 {
|
|
|
|
// Directive at index 0:
|
|
|
|
// Must be a '+'
|
|
|
|
// Raise an error if not the case
|
|
|
|
// index: 0123456789
|
|
|
|
// +0.000,000
|
|
|
|
// +000,000.0
|
|
|
|
// +0000.00
|
|
|
|
// +0000
|
|
|
|
if formatDirectiveIndices[0] == 0 {
|
|
|
|
if formatDirectiveChars[formatDirectiveIndices[0]] != '+' {
|
2021-08-19 22:45:30 +02:00
|
|
|
panic("renderFloat(): invalid positive sign directive")
|
2014-11-13 21:41:43 +01:00
|
|
|
}
|
|
|
|
positiveStr = "+"
|
|
|
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Two directives:
|
|
|
|
// First is thousands separator
|
|
|
|
// Raise an error if not followed by 3-digit
|
|
|
|
// 0123456789
|
|
|
|
// 0.000,000
|
|
|
|
// 000,000.00
|
|
|
|
if len(formatDirectiveIndices) == 2 {
|
|
|
|
if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 {
|
2021-08-19 22:45:30 +02:00
|
|
|
panic("renderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
2014-11-13 21:41:43 +01:00
|
|
|
}
|
|
|
|
thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
|
|
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// One directive:
|
|
|
|
// Directive is decimal separator
|
|
|
|
// The number of digit-specifier following the separator indicates wanted precision
|
|
|
|
// 0123456789
|
|
|
|
// 0.00
|
|
|
|
// 000,0000
|
|
|
|
if len(formatDirectiveIndices) == 1 {
|
|
|
|
decimalStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
|
|
|
precision = len(formatDirectiveChars) - formatDirectiveIndices[0] - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate sign part
|
|
|
|
var signStr string
|
|
|
|
if n >= 0.000000001 {
|
|
|
|
signStr = positiveStr
|
|
|
|
} else if n <= -0.000000001 {
|
|
|
|
signStr = negativeStr
|
|
|
|
n = -n
|
|
|
|
} else {
|
|
|
|
signStr = ""
|
|
|
|
n = 0.0
|
|
|
|
}
|
|
|
|
|
|
|
|
// split number into integer and fractional parts
|
|
|
|
intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
|
|
|
|
|
|
|
|
// generate integer part string
|
|
|
|
intStr := strconv.Itoa(int(intf))
|
|
|
|
|
|
|
|
// add thousand separator if required
|
|
|
|
if len(thousandStr) > 0 {
|
|
|
|
for i := len(intStr); i > 3; {
|
|
|
|
i -= 3
|
|
|
|
intStr = intStr[:i] + thousandStr + intStr[i:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no fractional part, we can leave now
|
|
|
|
if precision == 0 {
|
|
|
|
return signStr + intStr
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate fractional part
|
|
|
|
fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
|
|
|
|
// may need padding
|
|
|
|
if len(fracStr) < precision {
|
|
|
|
fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
|
|
|
|
}
|
|
|
|
|
|
|
|
return signStr + intStr + decimalStr + fracStr
|
|
|
|
}
|
|
|
|
|
2014-10-16 20:01:43 +02:00
|
|
|
// Request.RemoteAddress contains port, which we want to remove i.e.:
|
|
|
|
// "[::1]:58292" => "[::1]"
|
|
|
|
func ipAddrFromRemoteAddr(s string) string {
|
|
|
|
idx := strings.LastIndex(s, ":")
|
|
|
|
if idx == -1 {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
return s[:idx]
|
|
|
|
}
|
|
|
|
|
2017-03-22 21:09:40 +01:00
|
|
|
func acceptsHTML(hdr http.Header) bool {
|
2014-11-13 21:41:43 +01:00
|
|
|
actual := header.ParseAccept(hdr, "Accept")
|
|
|
|
|
|
|
|
for _, s := range actual {
|
|
|
|
if s.Value == "text/html" {
|
2022-07-14 18:02:18 +02:00
|
|
|
return true
|
2014-11-13 21:41:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-14 18:02:18 +02:00
|
|
|
return false
|
2014-11-13 21:41:43 +01:00
|
|
|
}
|
2021-07-23 11:20:49 +02:00
|
|
|
|
|
|
|
func formatSize(size int64) string {
|
|
|
|
sizeFloat := float64(size)
|
2021-07-23 11:21:05 +02:00
|
|
|
base := math.Log(sizeFloat) / math.Log(1024)
|
2021-07-23 11:20:49 +02:00
|
|
|
|
2021-07-23 11:21:05 +02:00
|
|
|
sizeOn := math.Pow(1024, base-math.Floor(base))
|
2021-07-23 11:20:49 +02:00
|
|
|
|
|
|
|
var round float64
|
|
|
|
pow := math.Pow(10, float64(2))
|
|
|
|
digit := pow * sizeOn
|
|
|
|
round = math.Floor(digit)
|
|
|
|
|
|
|
|
newVal := round / pow
|
|
|
|
|
|
|
|
var suffixes [5]string
|
|
|
|
suffixes[0] = "B"
|
|
|
|
suffixes[1] = "KB"
|
|
|
|
suffixes[2] = "MB"
|
|
|
|
suffixes[3] = "GB"
|
|
|
|
suffixes[4] = "TB"
|
|
|
|
|
|
|
|
getSuffix := suffixes[int(math.Floor(base))]
|
|
|
|
return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix)
|
|
|
|
}
|
2023-05-19 12:01:54 +02:00
|
|
|
|
|
|
|
func formatDurationDays(durationDays time.Duration) string {
|
|
|
|
days := int(durationDays.Hours() / 24)
|
|
|
|
if days == 1 {
|
|
|
|
return fmt.Sprintf("%d day", days)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%d days", days)
|
|
|
|
}
|