client_anonym.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // package client_anonym -- HTTP-клиент для выполнения анонимных запросов на сервер
  2. package client_anonym
  3. import (
  4. "fmt"
  5. "io"
  6. "net"
  7. "net/http"
  8. "net/url"
  9. "strings"
  10. "sync"
  11. "time"
  12. "git.p78su.freemyip.com/svi/gostore/pkg/msg_net"
  13. "git.p78su.freemyip.com/svi/gostore/pkg/types"
  14. )
  15. // ClientAnonym -- HTTP-клиент для выполнения анонимных запросов на сервер
  16. type ClientAnonym struct {
  17. servCtx types.IServCtx
  18. client *http.Client
  19. url string // Базовый адрес сервера
  20. urlPing string // URL для пинга сервера
  21. urlPut string // URL для записи данных
  22. urlRead string // URL для чтения данных
  23. urlDelete string // URL для удаления данных
  24. urlFind string // URL для поиска данных
  25. block sync.RWMutex // Блокировка проверки работы клиента
  26. isWork bool // Признак работы клиента
  27. }
  28. // NewClientAnonym -- конструктор
  29. func NewClientAnonym(serv types.IServCtx, url string) (*ClientAnonym, error) {
  30. { // Предусловия
  31. if serv == nil {
  32. return nil, fmt.Errorf("NewClientAnonym(): IServCtx==nil")
  33. }
  34. if url == "" {
  35. return nil, fmt.Errorf("NewClientAnonym(): url is empty")
  36. }
  37. }
  38. sf := &ClientAnonym{
  39. servCtx: serv,
  40. client: &http.Client{
  41. Transport: &http.Transport{
  42. Proxy: http.ProxyFromEnvironment,
  43. DialContext: (&net.Dialer{
  44. Timeout: 5 * time.Second,
  45. KeepAlive: 30 * time.Second,
  46. }).DialContext,
  47. ForceAttemptHTTP2: false,
  48. MaxIdleConns: 10,
  49. IdleConnTimeout: 30 * time.Second,
  50. TLSHandshakeTimeout: 10 * time.Second,
  51. ExpectContinueTimeout: 5 * time.Second,
  52. },
  53. },
  54. url: url,
  55. urlPing: url + "anonym/time",
  56. urlPut: url + "anonym/put",
  57. urlRead: url + "anonym/get",
  58. urlDelete: url + "anonym/del",
  59. urlFind: url + "anonym/find",
  60. }
  61. sf.servCtx.Wg().Add(1)
  62. go sf.close()
  63. sf.isWork = true
  64. return sf, nil
  65. }
  66. // Проверка на признак работы клиента
  67. func (sf *ClientAnonym) isClosed() bool {
  68. sf.block.RLock()
  69. defer sf.block.RUnlock()
  70. return !sf.isWork
  71. }
  72. // Find -- ищет ключи по префиксу
  73. func (sf *ClientAnonym) Find(prefixKey string) ([]string, error) {
  74. if sf.isClosed() {
  75. return nil, fmt.Errorf("ClientAnonym.Find(): client is closed")
  76. }
  77. if prefixKey == "" {
  78. return nil, fmt.Errorf("ClientAnonym.Find(): prefixKey is empty")
  79. }
  80. formPost := url.Values{
  81. "key": {prefixKey},
  82. }
  83. resp, err := sf.client.Post(sf.urlFind, "application/x-www-form-urlencoded", strings.NewReader(formPost.Encode()))
  84. if err != nil {
  85. return nil, fmt.Errorf("ClientAnonym.Find(): in get url(%q), err=\n\t%w", sf.urlFind, err)
  86. }
  87. defer resp.Body.Close()
  88. if resp.StatusCode != http.StatusOK {
  89. return nil, fmt.Errorf("ClientAnonym.Find(): err=%s", resp.Status)
  90. }
  91. binBody, err := io.ReadAll(resp.Body)
  92. if err != nil {
  93. return nil, fmt.Errorf("ClientAnonym.Find(): in read body, err=\n\t%w", err)
  94. }
  95. req, err := msg_net.UnmarshalFindRequest(prefixKey, binBody)
  96. if err != nil {
  97. return nil, fmt.Errorf("ClientAnonym.Find(): in unmarshal find request, err=\n\t%w", err)
  98. }
  99. return req.Values(), nil
  100. }
  101. // Delete -- удаление ключа из хранилища
  102. func (sf *ClientAnonym) Delete(key string) error {
  103. if sf.isClosed() {
  104. return fmt.Errorf("ClientAnonym.Delete(): client is closed")
  105. }
  106. if key == "" {
  107. return fmt.Errorf("ClientAnonym.Delete(): key is empty")
  108. }
  109. formPost := url.Values{
  110. "key": {key},
  111. }
  112. resp, err := sf.client.Post(sf.urlDelete, "application/x-www-form-urlencoded", strings.NewReader(formPost.Encode()))
  113. if err != nil {
  114. return fmt.Errorf("ClientAnonym.Delete(): in get url(%q), err=\n\t%w", sf.urlDelete, err)
  115. }
  116. defer resp.Body.Close()
  117. if resp.StatusCode != http.StatusOK {
  118. return fmt.Errorf("ClientAnonym.Delete(): err=%s", resp.Status)
  119. }
  120. return nil
  121. }
  122. // Read -- получает значение по ключу
  123. func (sf *ClientAnonym) Read(key string) (string, error) {
  124. if sf.isClosed() {
  125. return "", fmt.Errorf("ClientAnonym.Read(): client is closed")
  126. }
  127. if key == "" {
  128. return "", fmt.Errorf("ClientAnonym.Read(): key is empty")
  129. }
  130. formPost := url.Values{
  131. "key": {key},
  132. }
  133. resp, err := sf.client.Post(sf.urlRead, "application/x-www-form-urlencoded", strings.NewReader(formPost.Encode()))
  134. if err != nil {
  135. return "", fmt.Errorf("ClientAnonym.Read(): in get url(%q), err=\n\t%w", sf.urlRead, err)
  136. }
  137. defer resp.Body.Close()
  138. if resp.StatusCode != http.StatusOK {
  139. return "", fmt.Errorf("ClientAnonym.Read(): err=%s", resp.Status)
  140. }
  141. binBody, err := io.ReadAll(resp.Body)
  142. if err != nil {
  143. return "", fmt.Errorf("ClientAnonym.Read(): err=\n\t%w", err)
  144. }
  145. strResp := string(binBody)
  146. return strResp, nil
  147. }
  148. // Put -- помещает в хранилище ключ и значение
  149. func (sf *ClientAnonym) Put(key string, binData []byte) (string, error) {
  150. if sf.isClosed() {
  151. return "", fmt.Errorf("ClientAnonym.Ping(): client is closed")
  152. }
  153. if key == "" {
  154. return "", fmt.Errorf("ClientAnonym.Put(): key is empty")
  155. }
  156. postForm := url.Values{
  157. "key": {key},
  158. "val": {string(binData)},
  159. }
  160. resp, err := sf.client.Post(sf.urlPut, "application/x-www-form-urlencoded", strings.NewReader(postForm.Encode()))
  161. if err != nil {
  162. return "", fmt.Errorf("ClientAnonym.Put(): in get url(%q), err=\n\t%w", sf.url, err)
  163. }
  164. defer resp.Body.Close()
  165. if resp.StatusCode != http.StatusOK {
  166. return "", fmt.Errorf("ClientAnonym.Put(): err=%s", resp.Status)
  167. }
  168. binBody, err := io.ReadAll(resp.Body)
  169. if err != nil {
  170. return "", fmt.Errorf("ClientAnonym.Put(): err=\n\t%w", err)
  171. }
  172. strResp := string(binBody)
  173. return strResp, nil
  174. }
  175. // Ping -- пингует указанный сервис в реальном времени
  176. func (sf *ClientAnonym) Ping() error {
  177. if sf.isClosed() {
  178. return fmt.Errorf("ClientAnonym.Ping(): client is closed")
  179. }
  180. resp, err := sf.client.Post(sf.urlPing, "application/x-www-form-urlencoded", nil)
  181. if err != nil {
  182. return fmt.Errorf("ClientAnonym.Ping(): in get url(%q), err=\n\t%w", sf.urlPing, err)
  183. }
  184. defer resp.Body.Close()
  185. if resp.StatusCode != http.StatusOK {
  186. return fmt.Errorf("ClientAnonym.Ping(): url=`%v`, err=%s", sf.urlPing, resp.Status)
  187. }
  188. binBody, err := io.ReadAll(resp.Body)
  189. if err != nil {
  190. return fmt.Errorf("ClientAnonym.Ping(): err=\n\t%w", err)
  191. }
  192. _ = binBody
  193. return err
  194. }
  195. // Ожидает закрытия сервиса и освобождает сервис
  196. func (sf *ClientAnonym) close() {
  197. <-sf.servCtx.Ctx().Done()
  198. sf.block.Lock()
  199. defer sf.block.Unlock()
  200. if !sf.isWork {
  201. return
  202. }
  203. sf.isWork = false
  204. sf.servCtx.Wg().Done()
  205. }