client.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "os"
  9. "reflect"
  10. "time"
  11. "github.com/valyala/fasthttp"
  12. )
  13. var headerContentTypeJson = []byte("application/json")
  14. var client *fasthttp.Client
  15. type Entity struct {
  16. Id int
  17. Name string
  18. }
  19. func main() {
  20. // You may read the timeouts from some config
  21. readTimeout, _ := time.ParseDuration("500ms")
  22. writeTimeout, _ := time.ParseDuration("500ms")
  23. maxIdleConnDuration, _ := time.ParseDuration("1h")
  24. client = &fasthttp.Client{
  25. ReadTimeout: readTimeout,
  26. WriteTimeout: writeTimeout,
  27. MaxIdleConnDuration: maxIdleConnDuration,
  28. NoDefaultUserAgentHeader: true, // Don't send: User-Agent: fasthttp
  29. DisableHeaderNamesNormalizing: true, // If you set the case on your headers correctly you can enable this
  30. DisablePathNormalizing: true,
  31. // increase DNS cache time to an hour instead of default minute
  32. Dial: (&fasthttp.TCPDialer{
  33. Concurrency: 4096,
  34. DNSCacheDuration: time.Hour,
  35. }).Dial,
  36. }
  37. sendGetRequest()
  38. sendPostRequest()
  39. }
  40. func sendGetRequest() {
  41. req := fasthttp.AcquireRequest()
  42. req.SetRequestURI("http://localhost:8080/")
  43. req.Header.SetMethod(fasthttp.MethodGet)
  44. resp := fasthttp.AcquireResponse()
  45. err := client.Do(req, resp)
  46. fasthttp.ReleaseRequest(req)
  47. if err == nil {
  48. fmt.Printf("DEBUG Response: %s\n", resp.Body())
  49. } else {
  50. fmt.Fprintf(os.Stderr, "ERR Connection error: %v\n", err)
  51. }
  52. fasthttp.ReleaseResponse(resp)
  53. }
  54. func sendPostRequest() {
  55. // per-request timeout
  56. reqTimeout := time.Duration(100) * time.Millisecond
  57. reqEntity := &Entity{
  58. Name: "New entity",
  59. }
  60. reqEntityBytes, _ := json.Marshal(reqEntity)
  61. req := fasthttp.AcquireRequest()
  62. req.SetRequestURI("http://localhost:8080/")
  63. req.Header.SetMethod(fasthttp.MethodPost)
  64. req.Header.SetContentTypeBytes(headerContentTypeJson)
  65. req.SetBodyRaw(reqEntityBytes)
  66. resp := fasthttp.AcquireResponse()
  67. err := client.DoTimeout(req, resp, reqTimeout)
  68. fasthttp.ReleaseRequest(req)
  69. defer fasthttp.ReleaseResponse(resp)
  70. if err != nil {
  71. errName, known := httpConnError(err)
  72. if known {
  73. fmt.Fprintf(os.Stderr, "WARN conn error: %v\n", errName)
  74. } else {
  75. fmt.Fprintf(os.Stderr, "ERR conn failure: %v %v\n", errName, err)
  76. }
  77. return
  78. }
  79. statusCode := resp.StatusCode()
  80. respBody := resp.Body()
  81. fmt.Printf("DEBUG Response: %s\n", respBody)
  82. if statusCode != http.StatusOK {
  83. fmt.Fprintf(os.Stderr, "ERR invalid HTTP response code: %d\n", statusCode)
  84. return
  85. }
  86. respEntity := &Entity{}
  87. err = json.Unmarshal(respBody, respEntity)
  88. if err == nil || errors.Is(err, io.EOF) {
  89. fmt.Printf("DEBUG Parsed Response: %v\n", respEntity)
  90. } else {
  91. fmt.Fprintf(os.Stderr, "ERR failed to parse response: %v\n", err)
  92. }
  93. }
  94. func httpConnError(err error) (string, bool) {
  95. var (
  96. errName string
  97. known = true
  98. )
  99. switch {
  100. case errors.Is(err, fasthttp.ErrTimeout):
  101. errName = "timeout"
  102. case errors.Is(err, fasthttp.ErrNoFreeConns):
  103. errName = "conn_limit"
  104. case errors.Is(err, fasthttp.ErrConnectionClosed):
  105. errName = "conn_close"
  106. case reflect.TypeOf(err).String() == "*net.OpError":
  107. errName = "timeout"
  108. default:
  109. known = false
  110. }
  111. return errName, known
  112. }