auto_https_conn.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  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. }
  44. resp.StatusCode = http.StatusTemporaryRedirect
  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. }