client.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package httpserver
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "strings"
  7. "github.com/gin-gonic/gin"
  8. )
  9. /*
  10. Retrieve the site audit config file from Semrush
  11. returns a byte array of the body, the content type of the resp, and an error
  12. */
  13. func (c *Controller) RetrieveStaticResource(method string, path string, ctx *gin.Context) ([]byte, string, int, error) {
  14. cacheResp := c.GetResource(path)
  15. if cacheResp != nil {
  16. return cacheResp.Data, cacheResp.Ctype, cacheResp.Rcode, nil
  17. }
  18. url := fmt.Sprintf("%s%s", c.Config.FullAltAllowedDomain, path)
  19. req, err := http.NewRequest(method, url, nil)
  20. if err != nil {
  21. return nil, "", 500, err
  22. }
  23. c.setHeaders(req, ctx)
  24. resp, err := c.Client.Do(req)
  25. if err != nil {
  26. return nil, "", 500, err
  27. }
  28. defer resp.Body.Close()
  29. b, err := io.ReadAll(resp.Body)
  30. if err != nil {
  31. return nil, "", 500, err
  32. }
  33. if resp.StatusCode == 200 {
  34. c.CacheResource(path, NewCachedResource(b, resp.Header.Get("content-type"), resp.StatusCode))
  35. }
  36. return c.pageMod(b), resp.Header.Get("content-type"), resp.StatusCode, nil
  37. }
  38. /*
  39. Perform a call against the siteaudit api
  40. :param path: the URI path with the query
  41. :param query: the query to add to the request
  42. :param body: an io.Reader to push into the request body
  43. :returns a byte array of the response, the content type and an error
  44. */
  45. func (c *Controller) SiteauditApiCall(method string, path string, query string, body io.Reader, ctx *gin.Context) ([]byte, string, int, error) {
  46. query = strings.ReplaceAll(query, c.Config.FullProxyDomain, c.Config.FullDomain)
  47. url := fmt.Sprintf("%s%s?%s", c.Config.FullDomain, path, query)
  48. req, err := http.NewRequest(method, url, body)
  49. if err != nil {
  50. return nil, "", 500, err
  51. }
  52. c.setHeaders(req, ctx)
  53. resp, err := c.Client.Do(req)
  54. if err != nil {
  55. return nil, "", 500, err
  56. }
  57. defer resp.Body.Close()
  58. b, err := io.ReadAll(resp.Body)
  59. if err != nil {
  60. return nil, "", 500, err
  61. }
  62. return c.pageMod(b), resp.Header.Get("content-type"), resp.StatusCode, nil
  63. }
  64. /*
  65. Generic site call to the semrush site
  66. */
  67. func (c *Controller) SemrushGeneric(ctx *gin.Context) ([]byte, string, int, error) {
  68. path := ctx.Param("ProxiedPath")
  69. method := ctx.Request.Method
  70. query := ctx.Request.URL.RawQuery
  71. body := ctx.Request.Body
  72. var reqUrl string
  73. if query != "" {
  74. reqUrl = fmt.Sprintf("%s%s?%s", c.Config.FullDomain, path, query)
  75. } else {
  76. reqUrl = fmt.Sprintf("%s%s", c.Config.FullDomain, path)
  77. }
  78. cacheResp := c.GetResource(path)
  79. if cacheResp != nil {
  80. return cacheResp.Data, cacheResp.Ctype, cacheResp.Rcode, nil
  81. }
  82. req, err := http.NewRequest(method, reqUrl, body)
  83. if err != nil {
  84. return nil, "", 500, err
  85. }
  86. c.setHeaders(req, ctx)
  87. resp, err := c.Client.Do(req)
  88. if err != nil {
  89. return nil, "", 500, err
  90. }
  91. defer resp.Body.Close()
  92. b, err := io.ReadAll(resp.Body)
  93. if err != nil {
  94. return nil, "", 500, err
  95. }
  96. for k, v := range resp.Header {
  97. _, ok := NonmutableHeaders[k]
  98. if !ok {
  99. ctx.Header(k, v[0])
  100. }
  101. }
  102. if resp.StatusCode == 200 {
  103. if query == "" {
  104. if method == "GET" {
  105. c.CacheResource(path, NewCachedResource(b, resp.Header.Get("content-type"), resp.StatusCode))
  106. }
  107. }
  108. }
  109. return c.pageMod(b), resp.Header.Get("content-type"), resp.StatusCode, nil
  110. }
  111. /*
  112. Sets the request headers to whatever is defined in this private method
  113. :param req: a pointer to an HTTP request
  114. */
  115. func (c *Controller) setHeaders(req *http.Request, ctx *gin.Context) {
  116. req.AddCookie(c.Config.PhpSession)
  117. req.AddCookie(c.Config.SsoToken)
  118. req.Header.Set("User-Agent", c.Config.UserAgent)
  119. req.Header.Set("Referer", c.Config.FullDomain)
  120. req.Header.Set("Origin", c.Config.FullDomain)
  121. for k, v := range ctx.Request.Header {
  122. _, ok := NonmutableHeaders[k]
  123. if !ok {
  124. req.Header.Add(k, v[0])
  125. }
  126. }
  127. }
  128. /*
  129. Rewrite all occurences of these values into the response body
  130. */
  131. func (c *Controller) pageMod(data []byte) []byte {
  132. newBody := strings.ReplaceAll(string(data), "\"srf-browser-unhappy\"", "\"srf-browser-unhappy\" style=\"display:none;\"")
  133. newBody = strings.ReplaceAll(newBody, "\"srf-navbar__right\"", "\"srf-navbar__right\" style=\"display:none;\"")
  134. newBody = strings.ReplaceAll(newBody, "<footer", "<footer style=\"display:none;\"")
  135. newBody = strings.ReplaceAll(newBody, "\"srf-report-sidebar-extras\"", "\"srf-report-sidebar-extra\" style=\"display:none;\"")
  136. newBody = strings.ReplaceAll(newBody, c.Config.AllowedDomain, c.Config.ProxyAddr)
  137. newBody = strings.ReplaceAll(newBody, c.Config.AltAllowedDomain, c.Config.ProxyAddr)
  138. return []byte(newBody)
  139. }