speedtest.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package speedtest
  2. //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg speedtest -path Proxy,SpeedTest
  3. import (
  4. "bufio"
  5. "context"
  6. "io"
  7. "net/http"
  8. "strconv"
  9. "strings"
  10. "v2ray.com/core"
  11. "v2ray.com/core/common"
  12. "v2ray.com/core/common/buf"
  13. "v2ray.com/core/common/serial"
  14. "v2ray.com/core/common/session"
  15. "v2ray.com/core/proxy"
  16. )
  17. var rndBytes = []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\\'()*+,-")
  18. type rndBytesReader struct{}
  19. func (rndBytesReader) Read(b []byte) (int, error) {
  20. totalBytes := 0
  21. for totalBytes < len(b) {
  22. nBytes := copy(b[totalBytes:], rndBytes[:])
  23. totalBytes += nBytes
  24. }
  25. return totalBytes, nil
  26. }
  27. func (rndBytesReader) Close() error {
  28. return nil
  29. }
  30. type SpeedTestHandler struct{}
  31. func New(ctx context.Context, config *Config) (*SpeedTestHandler, error) {
  32. return &SpeedTestHandler{}, nil
  33. }
  34. type noOpCloser struct {
  35. io.Reader
  36. }
  37. func (c *noOpCloser) Close() error {
  38. return nil
  39. }
  40. func defaultResponse() *http.Response {
  41. response := &http.Response{
  42. Status: "Not Found",
  43. StatusCode: 404,
  44. Proto: "HTTP/1.1",
  45. ProtoMajor: 1,
  46. ProtoMinor: 1,
  47. Header: http.Header(make(map[string][]string)),
  48. Body: nil,
  49. ContentLength: 0,
  50. Close: true,
  51. }
  52. response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
  53. return response
  54. }
  55. func (h *SpeedTestHandler) Process(ctx context.Context, link *core.Link, dialer proxy.Dialer) error {
  56. reader := link.Reader
  57. writer := link.Writer
  58. defer func() {
  59. common.Close(writer)
  60. }()
  61. bufReader := bufio.NewReader(&buf.BufferedReader{
  62. Reader: reader,
  63. Direct: true,
  64. })
  65. bufWriter := buf.NewBufferedWriter(writer)
  66. common.Must(bufWriter.SetBuffered(false))
  67. request, err := http.ReadRequest(bufReader)
  68. if err != nil {
  69. return newError("failed to read speedtest request").Base(err)
  70. }
  71. path := strings.ToLower(request.URL.Path)
  72. if len(path) > 0 && path[0] == '/' {
  73. path = path[1:]
  74. }
  75. switch path {
  76. case "hello":
  77. respBody := "hello 2.5 2017-08-15.1314.4ae12d5"
  78. response := &http.Response{
  79. Status: "OK",
  80. StatusCode: 200,
  81. Proto: "HTTP/1.1",
  82. ProtoMajor: 1,
  83. ProtoMinor: 1,
  84. Header: http.Header(make(map[string][]string)),
  85. Body: &noOpCloser{strings.NewReader(respBody)},
  86. ContentLength: int64(len(respBody)),
  87. Close: true,
  88. }
  89. response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
  90. return response.Write(bufWriter)
  91. case "upload":
  92. switch strings.ToUpper(request.Method) {
  93. case "POST":
  94. var sc buf.SizeCounter
  95. buf.Copy(buf.NewReader(request.Body), buf.Discard, buf.CountSize(&sc)) // nolint: errcheck
  96. response := &http.Response{
  97. Status: "OK",
  98. StatusCode: 200,
  99. Proto: "HTTP/1.1",
  100. ProtoMajor: 1,
  101. ProtoMinor: 1,
  102. Header: http.Header(make(map[string][]string)),
  103. Body: &noOpCloser{strings.NewReader(serial.Concat("size=", sc.Size))},
  104. Close: true,
  105. }
  106. response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
  107. return response.Write(bufWriter)
  108. case "OPTIONS":
  109. response := &http.Response{
  110. Status: "OK",
  111. StatusCode: 200,
  112. Proto: "HTTP/1.1",
  113. ProtoMajor: 1,
  114. ProtoMinor: 1,
  115. Header: http.Header(make(map[string][]string)),
  116. Body: nil,
  117. ContentLength: 0,
  118. Close: true,
  119. }
  120. response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
  121. response.Header.Set("Connection", "Close")
  122. response.Header.Set("Access-Control-Allow-Methods", "OPTIONS, POST")
  123. response.Header.Set("Access-Control-Allow-Headers", "content-type")
  124. response.Header.Set("Access-Control-Allow-Origin", "http://www.speedtest.net")
  125. return response.Write(bufWriter)
  126. default:
  127. newError("unknown method for upload: ", request.Method).WriteToLog(session.ExportIDToError(ctx))
  128. return defaultResponse().Write(bufWriter)
  129. }
  130. case "download":
  131. query := request.URL.Query()
  132. sizeStr := query.Get("size")
  133. size, err := strconv.Atoi(sizeStr)
  134. if err != nil {
  135. return defaultResponse().Write(bufWriter)
  136. }
  137. response := &http.Response{
  138. Status: "OK",
  139. StatusCode: 200,
  140. Proto: "HTTP/1.1",
  141. ProtoMajor: 1,
  142. ProtoMinor: 1,
  143. Header: http.Header(make(map[string][]string)),
  144. Body: rndBytesReader{},
  145. ContentLength: int64(size),
  146. Close: true,
  147. }
  148. response.Header.Set("Content-Type", "text/plain; charset=UTF-8")
  149. return response.Write(bufWriter)
  150. default:
  151. newError("unknown path: ", path).WriteToLog(session.ExportIDToError(ctx))
  152. return defaultResponse().Write(bufWriter)
  153. }
  154. }
  155. func init() {
  156. common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
  157. return New(ctx, config.(*Config))
  158. }))
  159. }