proxy.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package middleware
  2. import (
  3. "context"
  4. "net/http"
  5. "net/http/httputil"
  6. "net/url"
  7. "github.com/0xJacky/Nginx-UI/internal/transport"
  8. "github.com/0xJacky/Nginx-UI/query"
  9. "github.com/gin-gonic/gin"
  10. "github.com/spf13/cast"
  11. "github.com/uozi-tech/cosy/logger"
  12. )
  13. func Proxy() gin.HandlerFunc {
  14. return func(c *gin.Context) {
  15. nodeID, ok := c.Get("ProxyNodeID")
  16. if !ok {
  17. c.Next()
  18. return
  19. }
  20. id := cast.ToUint64(nodeID)
  21. if id == 0 {
  22. c.Next()
  23. return
  24. }
  25. defer c.Abort()
  26. env := query.Environment
  27. environment, err := env.Where(env.ID.Eq(id)).First()
  28. if err != nil {
  29. logger.Error(err)
  30. c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{
  31. "message": err.Error(),
  32. })
  33. return
  34. }
  35. baseUrl, err := url.Parse(environment.URL)
  36. if err != nil {
  37. logger.Error(err)
  38. c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
  39. "message": err.Error(),
  40. })
  41. return
  42. }
  43. proxy := httputil.NewSingleHostReverseProxy(baseUrl)
  44. customTransport, err := transport.NewTransport()
  45. if err != nil {
  46. logger.Error(err)
  47. c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
  48. "message": err.Error(),
  49. })
  50. return
  51. }
  52. proxy.Transport = customTransport
  53. defaultDirector := proxy.Director
  54. proxy.Director = func(req *http.Request) {
  55. defaultDirector(req)
  56. req.Header.Del("X-Node-ID")
  57. req.Header.Set("X-Node-Secret", environment.Token)
  58. }
  59. // fix https://github.com/0xJacky/nginx-ui/issues/342
  60. proxy.ModifyResponse = func(resp *http.Response) error {
  61. if resp.StatusCode == http.StatusForbidden {
  62. resp.StatusCode = http.StatusServiceUnavailable
  63. }
  64. // fix CORS header duplication issue
  65. resp.Header.Del("Access-Control-Allow-Origin")
  66. resp.Header.Del("Access-Control-Allow-Methods")
  67. resp.Header.Del("Access-Control-Allow-Headers")
  68. resp.Header.Del("Access-Control-Expose-Headers")
  69. resp.Header.Del("Access-Control-Max-Age")
  70. resp.Header.Del("Access-Control-Allow-Credentials")
  71. return nil
  72. }
  73. proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, err error) {
  74. logger.Error(err)
  75. c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
  76. "message": err.Error(),
  77. })
  78. }
  79. logger.Debug("Proxy request", baseUrl.String()+c.Request.RequestURI)
  80. // fix proxy panic when client disconnect
  81. ctx := context.WithValue(
  82. c.Request.Context(),
  83. http.ServerContextKey,
  84. nil,
  85. )
  86. req := c.Request.Clone(ctx)
  87. proxy.ServeHTTP(c.Writer, req)
  88. }
  89. }