| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- package speedtest
- //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg speedtest -path Proxy,SpeedTest
- import (
- "bufio"
- "context"
- "io"
- "net/http"
- "strconv"
- "strings"
- "v2ray.com/core"
- "v2ray.com/core/common"
- "v2ray.com/core/common/buf"
- "v2ray.com/core/common/serial"
- "v2ray.com/core/common/session"
- "v2ray.com/core/proxy"
- )
- var rndBytes = []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\\'()*+,-")
- type rndBytesReader struct{}
- func (rndBytesReader) Read(b []byte) (int, error) {
- totalBytes := 0
- for totalBytes < len(b) {
- nBytes := copy(b[totalBytes:], rndBytes[:])
- totalBytes += nBytes
- }
- return totalBytes, nil
- }
- func (rndBytesReader) Close() error {
- return nil
- }
- type SpeedTestHandler struct{}
- func New(ctx context.Context, config *Config) (*SpeedTestHandler, error) {
- return &SpeedTestHandler{}, nil
- }
- type noOpCloser struct {
- io.Reader
- }
- func (c *noOpCloser) Close() error {
- return nil
- }
- func defaultResponse() *http.Response {
- response := &http.Response{
- Status: "Not Found",
- StatusCode: 404,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: http.Header(make(map[string][]string)),
- Body: nil,
- ContentLength: 0,
- Close: true,
- }
- response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
- return response
- }
- func (h *SpeedTestHandler) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
- reader := link.Reader
- writer := link.Writer
- defer func() {
- common.Close(writer)
- }()
- bufReader := bufio.NewReader(&buf.BufferedReader{
- Reader: reader,
- Direct: true,
- })
- bufWriter := buf.NewBufferedWriter(writer)
- common.Must(bufWriter.SetBuffered(false))
- request, err := http.ReadRequest(bufReader)
- if err != nil {
- return newError("failed to read speedtest request").Base(err)
- }
- path := strings.ToLower(request.URL.Path)
- if len(path) > 0 && path[0] == '/' {
- path = path[1:]
- }
- switch path {
- case "hello":
- respBody := "hello 2.5 2017-08-15.1314.4ae12d5"
- response := &http.Response{
- Status: "OK",
- StatusCode: 200,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: http.Header(make(map[string][]string)),
- Body: &noOpCloser{strings.NewReader(respBody)},
- ContentLength: int64(len(respBody)),
- Close: true,
- }
- response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
- return response.Write(bufWriter)
- case "upload":
- switch strings.ToUpper(request.Method) {
- case "POST":
- var sc buf.SizeCounter
- buf.Copy(buf.NewReader(request.Body), buf.Discard, buf.CountSize(&sc)) // nolint: errcheck
- response := &http.Response{
- Status: "OK",
- StatusCode: 200,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: http.Header(make(map[string][]string)),
- Body: &noOpCloser{strings.NewReader(serial.Concat("size=", sc.Size))},
- Close: true,
- }
- response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
- return response.Write(bufWriter)
- case "OPTIONS":
- response := &http.Response{
- Status: "OK",
- StatusCode: 200,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: http.Header(make(map[string][]string)),
- Body: nil,
- ContentLength: 0,
- Close: true,
- }
- response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
- response.Header.Set("Connection", "Close")
- response.Header.Set("Access-Control-Allow-Methods", "OPTIONS, POST")
- response.Header.Set("Access-Control-Allow-Headers", "content-type")
- response.Header.Set("Access-Control-Allow-Origin", "http://www.speedtest.net")
- return response.Write(bufWriter)
- default:
- newError("unknown method for upload: ", request.Method).WriteToLog(session.ExportIDToError(ctx))
- return defaultResponse().Write(bufWriter)
- }
- case "download":
- query := request.URL.Query()
- sizeStr := query.Get("size")
- size, err := strconv.Atoi(sizeStr)
- if err != nil {
- return defaultResponse().Write(bufWriter)
- }
- response := &http.Response{
- Status: "OK",
- StatusCode: 200,
- Proto: "HTTP/1.1",
- ProtoMajor: 1,
- ProtoMinor: 1,
- Header: http.Header(make(map[string][]string)),
- Body: rndBytesReader{},
- ContentLength: int64(size),
- Close: true,
- }
- response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
- return response.Write(bufWriter)
- default:
- newError("unknown path: ", path).WriteToLog(session.ExportIDToError(ctx))
- return defaultResponse().Write(bufWriter)
- }
- }
- func init() {
- common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
- return New(ctx, config.(*Config))
- }))
- }
|