controller.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. package httpserver
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "net/http"
  7. "net/http/cookiejar"
  8. "net/url"
  9. "strings"
  10. "github.com/gin-gonic/gin"
  11. "golang.org/x/net/publicsuffix"
  12. )
  13. var staticRoutes = [...]string{
  14. "/siteaudit/i18n/messages_en",
  15. "/siteaudit/index/",
  16. "/siteaudit/review/",
  17. "/seo-dashboard/release/",
  18. "/competitive-list-widget/",
  19. "/backlink-audit/landing/",
  20. "/link-building-tool/landing/",
  21. "/keyword-overview/",
  22. "/keyword-gap/",
  23. "/oti/prod/organic_traffic_insights/",
  24. "/ajst/",
  25. "/listing-management/landings/",
  26. "/listing-management/landing-reviews/",
  27. "/messaging/apps/",
  28. }
  29. var apiRoutes = [...]string{
  30. "/projects/api/",
  31. "/siteaudit/api/",
  32. }
  33. // Implementing a 'set'
  34. var NonmutableHeaders = map[string]struct{}{
  35. "Cookie": struct{}{},
  36. "User-Agent": struct{}{},
  37. "Accept-Encoding": struct{}{},
  38. "Referer": struct{}{},
  39. "X-Proxy-Url": struct{}{},
  40. "Host": struct{}{},
  41. }
  42. type Controller struct {
  43. Config *HttpServerConfig
  44. Client *http.Client
  45. SiteUrl *url.URL
  46. }
  47. type ProxyCookies struct {
  48. ck map[*url.URL][]*http.Cookie
  49. }
  50. /*
  51. Returns a new Controller struct to register routes to the gin router
  52. :param cfg: A pointer to an HttpServerConfig struct
  53. */
  54. func NewController(cfg *HttpServerConfig) *Controller {
  55. jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
  56. if err != nil {
  57. log.Fatal(err)
  58. }
  59. sessCookies := cfg.CookieJar
  60. domain, err := url.Parse("https://www.semrush.com")
  61. if err != nil {
  62. log.Fatal(err)
  63. }
  64. jar.SetCookies(domain, sessCookies)
  65. return &Controller{Config: cfg, Client: &http.Client{Jar: jar}, SiteUrl: domain}
  66. }
  67. /*
  68. This handler will be responsible for proxying out the GET requests that the server recieves
  69. */
  70. func (c *Controller) Get(ctx *gin.Context) {
  71. incomingPath := ctx.Param("ProxiedPath")
  72. for idx := range staticRoutes {
  73. if strings.Contains(incomingPath, staticRoutes[idx]) {
  74. data, ctype, err := c.RetrieveStaticResource(incomingPath)
  75. if err != nil {
  76. log.Fatal(err, "failed to get site config")
  77. }
  78. ctx.Data(200, ctype, data)
  79. return
  80. }
  81. }
  82. for idx := range apiRoutes {
  83. if strings.Contains(incomingPath, apiRoutes[idx]) {
  84. data, ctype, err := c.SiteauditApiCall(ctx.Request.Method, incomingPath, ctx.Request.URL.RawQuery, ctx.Request.Body)
  85. if err != nil {
  86. log.Fatal(err, "failed to get site config")
  87. }
  88. ctx.Data(200, ctype, data)
  89. return
  90. }
  91. }
  92. reqUrl := fmt.Sprintf("https://%s%s", c.Config.AllowedDomain, incomingPath)
  93. req, err := http.NewRequest(ctx.Request.Method, reqUrl, ctx.Request.Body)
  94. if err != nil {
  95. ctx.JSON(500, map[string]string{
  96. "Error": "Error creating the request.",
  97. "Msg:": err.Error(),
  98. })
  99. return
  100. }
  101. req.URL.Path = incomingPath
  102. req.Header.Add("User-Agent", c.Config.UserAgent)
  103. req.Header.Set("Referer", c.Config.AllowedDomain)
  104. if incomingPath == "/" {
  105. ctx.Header("Location", "https://sem.bunnytools.shop/analytics/overview/")
  106. }
  107. if incomingPath == "/_compatibility/traffic/overview/" {
  108. ctx.Header("Location", "https://sem.bunnytools.shop/analytics/traffic/overview/ebay.com")
  109. }
  110. for k, v := range ctx.Request.Header {
  111. _, ok := NonmutableHeaders[k]
  112. if !ok {
  113. req.Header.Add(k, v[0])
  114. }
  115. }
  116. resp, err := c.Client.Do(req)
  117. if err != nil {
  118. ctx.JSON(500, map[string]string{
  119. "Error": "Error creating the request.",
  120. "Msg:": err.Error(),
  121. })
  122. return
  123. }
  124. defer resp.Body.Close()
  125. b, err := io.ReadAll(resp.Body)
  126. if err != nil {
  127. ctx.JSON(500, map[string]string{
  128. "Error": "Error creating the request.",
  129. "Msg:": err.Error(),
  130. })
  131. return
  132. }
  133. for k, v := range resp.Header {
  134. _, ok := NonmutableHeaders[k]
  135. if !ok {
  136. ctx.Header(k, v[0])
  137. }
  138. }
  139. newBody := strings.ReplaceAll(string(b), "\"srf-browser-unhappy\"", "\"srf-browser-unhappy\" style=\"display:none;\"")
  140. newBody = strings.ReplaceAll(newBody, "\"srf-navbar__right\"", "\"srf-navbar__right\" style=\"display:none;\"")
  141. newBody = strings.ReplaceAll(newBody, "<footer", "<footer style=\"display:none;\"")
  142. newBody = strings.ReplaceAll(newBody, "\"srf-report-sidebar-extras\"", "\"srf-report-sidebar-extra\" style=\"display:none;\"")
  143. newBody = strings.ReplaceAll(newBody, "www.semrush.com", "sem.bunnytools.shop")
  144. newBody = strings.ReplaceAll(newBody, "static.semrush.com", "sem.bunnytools.shop")
  145. ctx.Data(resp.StatusCode, resp.Header.Get("Content-Type"), []byte(newBody))
  146. }