|
@@ -4,9 +4,6 @@ import (
|
|
|
"context"
|
|
"context"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"io"
|
|
"io"
|
|
|
- "log"
|
|
|
|
|
-
|
|
|
|
|
- // "log"
|
|
|
|
|
"net/http"
|
|
"net/http"
|
|
|
"strings"
|
|
"strings"
|
|
|
"sync"
|
|
"sync"
|
|
@@ -28,37 +25,33 @@ type response struct {
|
|
|
|
|
|
|
|
// NetClient -- объект сетевого соединения
|
|
// NetClient -- объект сетевого соединения
|
|
|
type NetClient struct {
|
|
type NetClient struct {
|
|
|
- server types.IServer
|
|
|
|
|
- conn *http.Client
|
|
|
|
|
- stat *netstat.NetStat
|
|
|
|
|
- ctx context.Context
|
|
|
|
|
- chRes chan *response // Канал ответа от http-клиента
|
|
|
|
|
- fnCancel func() // Отмена контекста приложения
|
|
|
|
|
- block sync.Mutex
|
|
|
|
|
|
|
+ botNet types.IBotNet
|
|
|
|
|
+ conn *http.Client
|
|
|
|
|
+ stat *netstat.NetStat
|
|
|
|
|
+ chRes chan *response // Канал ответа от http-клиента
|
|
|
|
|
+ block sync.Mutex
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// NewNetClient -- возвращает сетевого клиента
|
|
// NewNetClient -- возвращает сетевого клиента
|
|
|
-func NewNetClient(server types.IServer, bot types.IBot) *NetClient {
|
|
|
|
|
|
|
+func NewNetClient(botNet types.IBotNet) *NetClient {
|
|
|
sf := &NetClient{
|
|
sf := &NetClient{
|
|
|
- server: server,
|
|
|
|
|
- conn: bot.BotNet().Conn(),
|
|
|
|
|
- stat: netstat.NewNetStat(server),
|
|
|
|
|
- chRes: make(chan *response, 2),
|
|
|
|
|
- ctx: context.Background(),
|
|
|
|
|
- fnCancel: server.CancelApp,
|
|
|
|
|
|
|
+ botNet: botNet,
|
|
|
|
|
+ conn: botNet.Conn(),
|
|
|
|
|
+ stat: netstat.NewNetStat(botNet),
|
|
|
|
|
+ chRes: make(chan *response, 2),
|
|
|
}
|
|
}
|
|
|
return sf
|
|
return sf
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Теневая функция на блокировку
|
|
|
|
|
|
|
+// Get -- выполняет безопасный GET-запрос в сеть
|
|
|
func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
|
|
func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
|
|
|
sf.block.Lock()
|
|
sf.block.Lock()
|
|
|
defer sf.block.Unlock()
|
|
defer sf.block.Unlock()
|
|
|
- ctxCancel, fnCancel := context.WithTimeout(sf.ctx, time.Second*7)
|
|
|
|
|
- defer func() {
|
|
|
|
|
- fnCancel()
|
|
|
|
|
|
|
+ ctxCancel, fnCancel := context.WithTimeout(sf.botNet.Ctx(), time.Second*7)
|
|
|
|
|
+ defer fnCancel()
|
|
|
|
|
+ defer func() { // Возможный перехват паники
|
|
|
if _panic := recover(); _panic != nil {
|
|
if _panic := recover(); _panic != nil {
|
|
|
- err = fmt.Errorf("NetClient.get(): перехвачена паника для URL(%v), panic=%v", strLink, _panic)
|
|
|
|
|
|
|
+ err = fmt.Errorf("NetClient.Get().defer(): перехвачена паника для URL(%v), panic=%v", strLink, _panic)
|
|
|
lstString = nil
|
|
lstString = nil
|
|
|
time.Sleep(time.Millisecond * 250) // Чтобы не насиловать не работающую сеть
|
|
time.Sleep(time.Millisecond * 250) // Чтобы не насиловать не работающую сеть
|
|
|
}
|
|
}
|
|
@@ -67,7 +60,7 @@ func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
|
|
|
select {
|
|
select {
|
|
|
case <-ctxCancel.Done(): // Таймаут по ожиданию
|
|
case <-ctxCancel.Done(): // Таймаут по ожиданию
|
|
|
err = fmt.Errorf("NetClient.get(): таймаут ожидания ответа")
|
|
err = fmt.Errorf("NetClient.get(): таймаут ожидания ответа")
|
|
|
- sf.fnCancel()
|
|
|
|
|
|
|
+ sf.botNet.CancelBotNet()
|
|
|
return nil, err
|
|
return nil, err
|
|
|
case resp := <-sf.chRes: // Получен ответ
|
|
case resp := <-sf.chRes: // Получен ответ
|
|
|
if resp.err != nil {
|
|
if resp.err != nil {
|
|
@@ -79,34 +72,36 @@ func (sf *NetClient) Get(strLink string) (lstString []string, err error) {
|
|
|
|
|
|
|
|
// Внутренний вызов для сокрытия под общей блокировкой
|
|
// Внутренний вызов для сокрытия под общей блокировкой
|
|
|
func (sf *NetClient) get(strLink string) {
|
|
func (sf *NetClient) get(strLink string) {
|
|
|
|
|
+ resp := &response{}
|
|
|
|
|
+ defer func() {
|
|
|
|
|
+ if resp.err != nil {
|
|
|
|
|
+ sf.stat.IncErr()
|
|
|
|
|
+ }
|
|
|
|
|
+ }()
|
|
|
req, err := http.NewRequest("GET", strLink, nil)
|
|
req, err := http.NewRequest("GET", strLink, nil)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- log.Fatalln(err)
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): при создании запроса, err=\n\t%w", err)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
- req.Header.Set("User-Agent", "Mozilla Firefox 86.0")
|
|
|
|
|
|
|
+ req.Header.Set("User-Agent", "Mozilla Firefox 94.1")
|
|
|
httpResp, err := sf.conn.Do(req)
|
|
httpResp, err := sf.conn.Do(req)
|
|
|
- resp := &response{}
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- resp.err = fmt.Errorf("NetClient.get().fnGet(): при выполнении GET-запроса, err=\n\t%w", err)
|
|
|
|
|
- sf.stat.AddByte(-1) // Фиксируем ошибку
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): при выполнении GET-запроса, err=\n\t%w", err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- defer sf.endGet(strLink, httpResp, resp)
|
|
|
|
|
|
|
+ defer sf.closeGetBody(strLink, httpResp, resp)
|
|
|
|
|
|
|
|
if httpResp.StatusCode != http.StatusOK {
|
|
if httpResp.StatusCode != http.StatusOK {
|
|
|
- resp.err = fmt.Errorf("NetClient.get().fnGet(): code=%v, status=%v", httpResp.StatusCode, httpResp.Status)
|
|
|
|
|
- sf.stat.AddByte(-1) // Фиксируем ошибку
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): code=%v, status=%v", httpResp.StatusCode, httpResp.Status)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
binData, err := io.ReadAll(httpResp.Body)
|
|
binData, err := io.ReadAll(httpResp.Body)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- resp.err = fmt.Errorf("NetClient.get().fnGet(): при чтении тела ответа, err=\n\t%w", err)
|
|
|
|
|
- sf.stat.AddByte(-1) // Фиксируем ошибку
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): при чтении тела ответа, err=\n\t%w", err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
if len(binData) == 0 {
|
|
if len(binData) == 0 {
|
|
|
- resp.err = fmt.Errorf("NetClient.get().fnGet(): пустое тело ответа, err=\n\t%w", err)
|
|
|
|
|
- sf.stat.AddByte(-1) // Фиксируем ошибку
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): пустое тело ответа, err=\n\t%w", err)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
lenData := len(binData) + len(strLink)
|
|
lenData := len(binData) + len(strLink)
|
|
@@ -114,32 +109,28 @@ func (sf *NetClient) get(strLink string) {
|
|
|
|
|
|
|
|
lstString := strings.Split(string(binData), "\n")
|
|
lstString := strings.Split(string(binData), "\n")
|
|
|
if len(lstString) == 0 {
|
|
if len(lstString) == 0 {
|
|
|
- resp.err = fmt.Errorf("NetClient.get().fnGet(): lstString is empty")
|
|
|
|
|
- sf.stat.AddByte(-1) // Фиксируем ошибку
|
|
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.get(): lstString is empty")
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
resp.lstString = lstString
|
|
resp.lstString = lstString
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Вызывается по завершению вызова
|
|
|
|
|
-func (sf *NetClient) endGet(strLink string, httpResp *http.Response, resp *response) {
|
|
|
|
|
|
|
+// Вызывается по завершению вызова, закрывает тело запроса
|
|
|
|
|
+func (sf *NetClient) closeGetBody(strLink string, httpResp *http.Response, resp *response) {
|
|
|
defer func() {
|
|
defer func() {
|
|
|
if _panic := recover(); _panic != nil {
|
|
if _panic := recover(); _panic != nil {
|
|
|
- // log._rintf("NetClient.sndGet(): strLink='%v', panic=%v\n", strLink, _panic)
|
|
|
|
|
- sf.fnCancel()
|
|
|
|
|
|
|
+ // log._rintf("NetClient.closeGetBody(): strLink='%v', panic=%v\n", strLink, _panic)
|
|
|
|
|
+ sf.botNet.CancelBotNet()
|
|
|
}
|
|
}
|
|
|
}()
|
|
}()
|
|
|
err := httpResp.Body.Close()
|
|
err := httpResp.Body.Close()
|
|
|
if err != nil {
|
|
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)
|
|
|
|
|
|
|
+ _err := fmt.Errorf("NetClient.closeGetBody(): ошибка при закрытии запроса URL(%q), err=\n\t%w", strLink, err)
|
|
|
|
|
+ if resp.err != nil { // Есть и ошибка в закрытии тела запроса и внутренняя
|
|
|
|
|
+ resp.err = fmt.Errorf("NetClient.closeGetBody(): двойная ошибка при закрытии запроса URL(%q), err=\n\t%w\n\tinternal err=\n\t%w", strLink, _err, resp.err)
|
|
|
}
|
|
}
|
|
|
resp.lstString = nil
|
|
resp.lstString = nil
|
|
|
-
|
|
|
|
|
- sf.stat.AddByte(-1) // Отметим ошибку закрытия клиента
|
|
|
|
|
|
|
+ sf.stat.IncErr()
|
|
|
}
|
|
}
|
|
|
sf.chRes <- resp
|
|
sf.chRes <- resp
|
|
|
}
|
|
}
|