tile38/controller/endpoint/http.go

84 lines
1.5 KiB
Go

package endpoint
import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"sync"
"time"
)
const (
httpExpiresAfter = time.Second * 30
httpRequestTimeout = time.Second * 5
httpMaxIdleConnections = 20
)
type HTTPEndpointConn struct {
mu sync.Mutex
ep Endpoint
ex bool
t time.Time
client *http.Client
}
func newHTTPEndpointConn(ep Endpoint) *HTTPEndpointConn {
return &HTTPEndpointConn{
ep: ep,
t: time.Now(),
}
}
func (conn *HTTPEndpointConn) Expired() bool {
conn.mu.Lock()
defer conn.mu.Unlock()
if !conn.ex {
if time.Now().Sub(conn.t) > httpExpiresAfter {
conn.ex = true
conn.client = nil
}
}
return conn.ex
}
func (conn *HTTPEndpointConn) Send(msg string) error {
conn.mu.Lock()
defer conn.mu.Unlock()
if conn.ex {
return errors.New("expired")
}
conn.t = time.Now()
if conn.client == nil {
conn.client = &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: httpMaxIdleConnections,
},
Timeout: httpRequestTimeout,
}
}
req, err := http.NewRequest("POST", conn.ep.Original, bytes.NewBufferString(msg))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := conn.client.Do(req)
if err != nil {
return err
}
// close the connection to reuse it
defer resp.Body.Close()
// discard response
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
return err
}
// we only care about the 200 response
if resp.StatusCode != 200 {
return fmt.Errorf("invalid status: %s", resp.Status)
}
return nil
}