111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Fun fact: the reference time is the specific time Jan 2 15:04:05 2006 MST, which reads:
|
|
// 01 02 03 04 05 06 -0700
|
|
// 1234567. Very clever, Google.
|
|
var timeLayout = "02/Jan/2006 15:04:05"
|
|
|
|
func StartServer(port string, path string, address string, protocolVer string, throttleRate int64) (error, int) {
|
|
var httpServer *http.Server
|
|
addressPort := address + ":" + port
|
|
fileServer := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
filePath := path + r.URL.Path
|
|
http.ServeFile(w, r, filePath)
|
|
var ip string
|
|
if strings.Contains(r.RemoteAddr, ":") {
|
|
ip = strings.Split(r.RemoteAddr, ":")[0]
|
|
} else {
|
|
ip = r.RemoteAddr
|
|
}
|
|
fmt.Println(ip + " - - [" + time.Now().Format(timeLayout) + "] \"" + r.Method + " " + r.URL.Path + " " + r.Proto + "\" " + "200" + " -")
|
|
})
|
|
|
|
var throttledFileServer http.Handler
|
|
if throttleRate != -1 {
|
|
throttledFileServer = ThrottleMiddleware(throttleRate)(fileServer)
|
|
} else {
|
|
throttledFileServer = fileServer
|
|
}
|
|
|
|
fmt.Println("Serving HTTP on", address, "port", port, "(http://"+address+":"+port+"/) ...")
|
|
if protocolVer == "2.0" || protocolVer == "2" {
|
|
httpServer = &http.Server{Addr: addressPort, Handler: throttledFileServer, TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler))}
|
|
} else {
|
|
httpServer = &http.Server{Addr: addressPort, Handler: throttledFileServer}
|
|
}
|
|
err := httpServer.ListenAndServe()
|
|
if err != nil {
|
|
if err.Error() == "permission denied" || err.Error() == "listen tcp "+addressPort+": bind: permission denied" {
|
|
return errors.New("permission denied"), 1
|
|
} else {
|
|
return err, 1
|
|
}
|
|
}
|
|
return nil, 0
|
|
}
|
|
|
|
func ThrottleMiddleware(rate int64, burstSize ...int64) func(http.Handler) http.Handler {
|
|
defaultBurstSize := int64(128)
|
|
if len(burstSize) > 0 && burstSize[0] > 0 {
|
|
defaultBurstSize = burstSize[0]
|
|
}
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
tw := &ThrottledResponseWriter{
|
|
writer: w,
|
|
rate: rate,
|
|
burstSize: defaultBurstSize,
|
|
}
|
|
next.ServeHTTP(tw, r)
|
|
})
|
|
}
|
|
}
|
|
|
|
type ThrottledResponseWriter struct {
|
|
writer http.ResponseWriter
|
|
rate int64
|
|
burstSize int64
|
|
}
|
|
|
|
func (tw *ThrottledResponseWriter) Write(p []byte) (int, error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var written int
|
|
for written < len(p) {
|
|
chunkSize := tw.burstSize
|
|
if int64(len(p)-written) < chunkSize {
|
|
chunkSize = int64(len(p) - written)
|
|
}
|
|
|
|
n, err := tw.writer.Write(p[written : written+int(chunkSize)])
|
|
if err != nil {
|
|
return written + n, err
|
|
}
|
|
written += n
|
|
|
|
time.Sleep(time.Duration(chunkSize*8*int64(time.Second)/tw.rate) * time.Nanosecond)
|
|
}
|
|
|
|
return written, nil
|
|
}
|
|
|
|
func (tw *ThrottledResponseWriter) Header() http.Header {
|
|
return tw.writer.Header()
|
|
}
|
|
|
|
func (tw *ThrottledResponseWriter) WriteHeader(statusCode int) {
|
|
tw.writer.WriteHeader(statusCode)
|
|
}
|