| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package netclient
- import (
- "context"
- "fmt"
- "io"
- "log"
- // "log"
- "net/http"
- "strings"
- "sync"
- "time"
- "wartank/pkg/components/sectionnet/netstat"
- "wartank/pkg/types"
- )
- /*
- Объект сетевого соединения
- */
- // Ответ после запроса
- type response struct {
- lstString []string
- err error
- }
- // NetClient -- объект сетевого соединения
- type NetClient struct {
- server types.IServer
- conn *http.Client
- stat *netstat.NetStat
- ctx context.Context
- chRes chan *response // Канал ответа от http-клиента
- fnCancel func() // Отмена контекста приложения
- block sync.Mutex
- }
- // NewNetClient -- возвращает сетевого клиента
- func NewNetClient(server types.IServer, bot types.IServBot) *NetClient {
- sf := &NetClient{
- server: server,
- conn: bot.BotNet().Conn(),
- stat: netstat.NewNetStat(server),
- chRes: make(chan *response, 2),
- ctx: context.Background(),
- fnCancel: server.CancelApp,
- }
- return sf
- }
- // Теневая функция на блокировку
- func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
- sf.block.Lock()
- defer sf.block.Unlock()
- ctxCancel, fnCancel := context.WithTimeout(sf.ctx, time.Second*7)
- defer func() {
- fnCancel()
- if _panic := recover(); _panic != nil {
- err = fmt.Errorf("NetClient.get(): перехвачена паника для URL(%v), panic=%v", strLink, _panic)
- lstString = nil
- time.Sleep(time.Millisecond * 250) // Чтобы не насиловать не работающую сеть
- }
- }()
- go sf.get(strLink)
- select {
- case <-ctxCancel.Done(): // Таймаут по ожиданию
- err = fmt.Errorf("NetClient.get(): таймаут ожидания ответа")
- sf.fnCancel()
- return nil, err
- case resp := <-sf.chRes: // Получен ответ
- if resp.err != nil {
- return nil, resp.err
- }
- return resp.lstString, nil
- }
- }
- // Внутренний вызов для сокрытия под общей блокировкой
- func (sf *NetClient) get(strLink string) {
- req, err := http.NewRequest("GET", strLink, nil)
- if err != nil {
- log.Fatalln(err)
- }
- req.Header.Set("User-Agent", "Mozilla Firefox 86.0")
- httpResp, err := sf.conn.Do(req)
- resp := &response{}
- if err != nil {
- resp.err = fmt.Errorf("NetClient.get().fnGet(): при выполнении GET-запроса, err=\n\t%w", err)
- sf.stat.AddByte(-1) // Фиксируем ошибку
- return
- }
- defer sf.endGet(strLink, httpResp, resp)
- if httpResp.StatusCode != http.StatusOK {
- resp.err = fmt.Errorf("NetClient.get().fnGet(): code=%v, status=%v", httpResp.StatusCode, httpResp.Status)
- sf.stat.AddByte(-1) // Фиксируем ошибку
- return
- }
- binData, err := io.ReadAll(httpResp.Body)
- if err != nil {
- resp.err = fmt.Errorf("NetClient.get().fnGet(): при чтении тела ответа, err=\n\t%w", err)
- sf.stat.AddByte(-1) // Фиксируем ошибку
- return
- }
- if len(binData) == 0 {
- resp.err = fmt.Errorf("NetClient.get().fnGet(): пустое тело ответа, err=\n\t%w", err)
- sf.stat.AddByte(-1) // Фиксируем ошибку
- return
- }
- lenData := len(binData) + len(strLink)
- sf.stat.AddByte(lenData)
- lstString := strings.Split(string(binData), "\n")
- if len(lstString) == 0 {
- resp.err = fmt.Errorf("NetClient.get().fnGet(): lstString is empty")
- sf.stat.AddByte(-1) // Фиксируем ошибку
- return
- }
- resp.lstString = lstString
- }
- // Вызывается по завершению вызова
- func (sf *NetClient) endGet(strLink string, httpResp *http.Response, resp *response) {
- defer func() {
- if _panic := recover(); _panic != nil {
- // log._rintf("NetClient.sndGet(): strLink='%v', panic=%v\n", strLink, _panic)
- sf.fnCancel()
- }
- }()
- err := httpResp.Body.Close()
- if err != nil {
- switch resp.err == nil {
- case true:
- resp.err = fmt.Errorf("NetClient.get().fnGet(): ошибка при закрытии запроса URL(%v), err=\n\t%w", strLink, err)
- case false:
- resp.err = fmt.Errorf("NetClient.get().fnGet(): ошибка при закрытии запроса URL(%v), err=\n\t%v\n\toldErr=\n\t%w", strLink, err, resp.err)
- }
- resp.lstString = nil
- sf.stat.AddByte(-1) // Отметим ошибку закрытия клиента
- }
- sf.chRes <- resp
- }
|