netclient.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package netclient
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "log"
  7. // "log"
  8. "net/http"
  9. "strings"
  10. "sync"
  11. "time"
  12. "wartank/pkg/components/sectionnet/netstat"
  13. "wartank/pkg/types"
  14. )
  15. /*
  16. Объект сетевого соединения
  17. */
  18. // Ответ после запроса
  19. type response struct {
  20. lstString []string
  21. err error
  22. }
  23. // NetClient -- объект сетевого соединения
  24. type NetClient struct {
  25. server types.IServer
  26. conn *http.Client
  27. stat *netstat.NetStat
  28. ctx context.Context
  29. chRes chan *response // Канал ответа от http-клиента
  30. fnCancel func() // Отмена контекста приложения
  31. block sync.Mutex
  32. }
  33. // NewNetClient -- возвращает сетевого клиента
  34. func NewNetClient(server types.IServer, bot types.IServBot) *NetClient {
  35. sf := &NetClient{
  36. server: server,
  37. conn: bot.BotNet().Conn(),
  38. stat: netstat.NewNetStat(server),
  39. chRes: make(chan *response, 2),
  40. ctx: context.Background(),
  41. fnCancel: server.CancelApp,
  42. }
  43. return sf
  44. }
  45. // Теневая функция на блокировку
  46. func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
  47. sf.block.Lock()
  48. defer sf.block.Unlock()
  49. ctxCancel, fnCancel := context.WithTimeout(sf.ctx, time.Second*7)
  50. defer func() {
  51. fnCancel()
  52. if _panic := recover(); _panic != nil {
  53. err = fmt.Errorf("NetClient.get(): перехвачена паника для URL(%v), panic=%v", strLink, _panic)
  54. lstString = nil
  55. time.Sleep(time.Millisecond * 250) // Чтобы не насиловать не работающую сеть
  56. }
  57. }()
  58. go sf.get(strLink)
  59. select {
  60. case <-ctxCancel.Done(): // Таймаут по ожиданию
  61. err = fmt.Errorf("NetClient.get(): таймаут ожидания ответа")
  62. sf.fnCancel()
  63. return nil, err
  64. case resp := <-sf.chRes: // Получен ответ
  65. if resp.err != nil {
  66. return nil, resp.err
  67. }
  68. return resp.lstString, nil
  69. }
  70. }
  71. // Внутренний вызов для сокрытия под общей блокировкой
  72. func (sf *NetClient) get(strLink string) {
  73. req, err := http.NewRequest("GET", strLink, nil)
  74. if err != nil {
  75. log.Fatalln(err)
  76. }
  77. req.Header.Set("User-Agent", "Mozilla Firefox 86.0")
  78. httpResp, err := sf.conn.Do(req)
  79. resp := &response{}
  80. if err != nil {
  81. resp.err = fmt.Errorf("NetClient.get().fnGet(): при выполнении GET-запроса, err=\n\t%w", err)
  82. sf.stat.AddByte(-1) // Фиксируем ошибку
  83. return
  84. }
  85. defer sf.endGet(strLink, httpResp, resp)
  86. if httpResp.StatusCode != http.StatusOK {
  87. resp.err = fmt.Errorf("NetClient.get().fnGet(): code=%v, status=%v", httpResp.StatusCode, httpResp.Status)
  88. sf.stat.AddByte(-1) // Фиксируем ошибку
  89. return
  90. }
  91. binData, err := io.ReadAll(httpResp.Body)
  92. if err != nil {
  93. resp.err = fmt.Errorf("NetClient.get().fnGet(): при чтении тела ответа, err=\n\t%w", err)
  94. sf.stat.AddByte(-1) // Фиксируем ошибку
  95. return
  96. }
  97. if len(binData) == 0 {
  98. resp.err = fmt.Errorf("NetClient.get().fnGet(): пустое тело ответа, err=\n\t%w", err)
  99. sf.stat.AddByte(-1) // Фиксируем ошибку
  100. return
  101. }
  102. lenData := len(binData) + len(strLink)
  103. sf.stat.AddByte(lenData)
  104. lstString := strings.Split(string(binData), "\n")
  105. if len(lstString) == 0 {
  106. resp.err = fmt.Errorf("NetClient.get().fnGet(): lstString is empty")
  107. sf.stat.AddByte(-1) // Фиксируем ошибку
  108. return
  109. }
  110. resp.lstString = lstString
  111. }
  112. // Вызывается по завершению вызова
  113. func (sf *NetClient) endGet(strLink string, httpResp *http.Response, resp *response) {
  114. defer func() {
  115. if _panic := recover(); _panic != nil {
  116. // log._rintf("NetClient.sndGet(): strLink='%v', panic=%v\n", strLink, _panic)
  117. sf.fnCancel()
  118. }
  119. }()
  120. err := httpResp.Body.Close()
  121. if err != nil {
  122. switch resp.err == nil {
  123. case true:
  124. resp.err = fmt.Errorf("NetClient.get().fnGet(): ошибка при закрытии запроса URL(%v), err=\n\t%w", strLink, err)
  125. case false:
  126. resp.err = fmt.Errorf("NetClient.get().fnGet(): ошибка при закрытии запроса URL(%v), err=\n\t%v\n\toldErr=\n\t%w", strLink, err, resp.err)
  127. }
  128. resp.lstString = nil
  129. sf.stat.AddByte(-1) // Отметим ошибку закрытия клиента
  130. }
  131. sf.chRes <- resp
  132. }