auto_https_conn.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // Package network provides network utilities for the 3x-ui web panel,
  2. // including automatic HTTP to HTTPS redirection functionality.
  3. package network
  4. import (
  5. "bufio"
  6. "bytes"
  7. "fmt"
  8. "net"
  9. "net/http"
  10. "sync"
  11. )
  12. // AutoHttpsConn wraps a net.Conn to provide automatic HTTP to HTTPS redirection.
  13. // It intercepts the first read to detect HTTP requests and responds with a 307 redirect
  14. // to the HTTPS equivalent URL. Subsequent reads work normally for HTTPS connections.
  15. type AutoHttpsConn struct {
  16. net.Conn
  17. firstBuf []byte
  18. bufStart int
  19. readRequestOnce sync.Once
  20. }
  21. // NewAutoHttpsConn creates a new AutoHttpsConn that wraps the given connection.
  22. // It enables automatic redirection of HTTP requests to HTTPS.
  23. func NewAutoHttpsConn(conn net.Conn) net.Conn {
  24. return &AutoHttpsConn{
  25. Conn: conn,
  26. }
  27. }
  28. func (c *AutoHttpsConn) readRequest() bool {
  29. c.firstBuf = make([]byte, 2048)
  30. n, err := c.Conn.Read(c.firstBuf)
  31. c.firstBuf = c.firstBuf[:n]
  32. if err != nil {
  33. return false
  34. }
  35. reader := bytes.NewReader(c.firstBuf)
  36. bufReader := bufio.NewReader(reader)
  37. request, err := http.ReadRequest(bufReader)
  38. if err != nil {
  39. return false
  40. }
  41. resp := http.Response{
  42. Header: http.Header{},
  43. StatusCode: http.StatusTemporaryRedirect,
  44. }
  45. location := fmt.Sprintf("https://%v%v", request.Host, request.RequestURI)
  46. resp.Header.Set("Location", location)
  47. _ = resp.Write(c.Conn)
  48. c.Close()
  49. c.firstBuf = nil
  50. return true
  51. }
  52. // Read implements the net.Conn Read method with automatic HTTPS redirection.
  53. // On the first read, it checks if the request is HTTP and redirects to HTTPS if so.
  54. // Subsequent reads work normally.
  55. func (c *AutoHttpsConn) Read(buf []byte) (int, error) {
  56. c.readRequestOnce.Do(func() {
  57. c.readRequest()
  58. })
  59. if c.firstBuf != nil {
  60. n := copy(buf, c.firstBuf[c.bufStart:])
  61. c.bufStart += n
  62. if c.bufStart >= len(c.firstBuf) {
  63. c.firstBuf = nil
  64. }
  65. return n, nil
  66. }
  67. return c.Conn.Read(buf)
  68. }