-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added semaphore http client (#3)
- Loading branch information
1 parent
8b00782
commit 426a192
Showing
4 changed files
with
181 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,10 @@ import ( | |
|
||
const invalidURL string = "postgres://user:abc{[email protected]:5432/db?sslmode=require" | ||
|
||
func newOSRMClient() OSRMClient { | ||
return OSRMClient{client: NewHTTPClient(HTTPClientConfig{})} | ||
} | ||
|
||
func TestNew(t *testing.T) { | ||
testCases := []struct { | ||
name, baseURL string | ||
|
@@ -38,8 +42,22 @@ func TestNew(t *testing.T) { | |
} | ||
} | ||
|
||
func TestOSRMClient_SetHTTPClient(t *testing.T) { | ||
osrm := newOSRMClient() | ||
|
||
assert.PanicsWithValue(t, "http client can't be nil", func() { | ||
osrm.SetHTTPClient(nil) | ||
}) | ||
|
||
client := NewHTTPClient(HTTPClientConfig{MaxConcurrency: 10}) | ||
|
||
osrm.SetHTTPClient(client) | ||
|
||
assert.Equal(t, client, osrm.client) | ||
} | ||
|
||
func TestOSRMClient_get(t *testing.T) { | ||
osrm := OSRMClient{} | ||
osrm := newOSRMClient() | ||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.Write([]byte("{\"message\": \"Ok\"}")) | ||
})) | ||
|
@@ -59,7 +77,7 @@ func TestOSRMClient_get(t *testing.T) { | |
} | ||
|
||
func TestOSRMClient_applyOpts(t *testing.T) { | ||
osrm := OSRMClient{} | ||
osrm := newOSRMClient() | ||
u := url.URL{} | ||
|
||
osrm.applyOpts(&u, []Option{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package gosrm | ||
|
||
import "net/http" | ||
|
||
type ( | ||
// httpClient is the default implementation of HTTPClient interface. | ||
httpClient struct { | ||
client *http.Client | ||
pool chan struct{} | ||
} | ||
|
||
// HTTPClientConfig is the config used to customize http client. | ||
HTTPClientConfig struct { | ||
// MaxConcurrency is the max number of concurrent requests. | ||
// If it's 0 then there is no limit. | ||
// | ||
// Defaults to 0. | ||
MaxConcurrency uint | ||
|
||
// HTTPClient is the client which will be used to do HTTP calls. | ||
// | ||
// Defaults to http.DefaultClient | ||
HTTPClient *http.Client | ||
} | ||
) | ||
|
||
// acquire acquires a spot in the pool. | ||
func (c httpClient) acquire() { | ||
if cap(c.pool) == 0 { | ||
return | ||
} | ||
c.pool <- struct{}{} | ||
} | ||
|
||
// release releases a spot from the pool. | ||
func (c httpClient) release() { | ||
if cap(c.pool) == 0 { | ||
return | ||
} | ||
<-c.pool | ||
} | ||
|
||
// Do does the HTTP call. | ||
func (c httpClient) Do(req *http.Request) (*http.Response, error) { | ||
c.acquire() | ||
defer c.release() | ||
|
||
return c.client.Do(req) | ||
} | ||
|
||
// NewHTTPClient returns a new HTTP client. | ||
func NewHTTPClient(cfg HTTPClientConfig) HTTPClient { | ||
var c httpClient | ||
|
||
if cfg.HTTPClient != nil { | ||
c.client = cfg.HTTPClient | ||
} else { | ||
c.client = http.DefaultClient | ||
} | ||
|
||
c.pool = make(chan struct{}, cfg.MaxConcurrency) | ||
|
||
return c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package gosrm | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewHTTPClient(t *testing.T) { | ||
cfg := HTTPClientConfig{} | ||
|
||
c := NewHTTPClient(cfg).(httpClient) | ||
assert.Equal(t, http.DefaultClient, c.client) | ||
assert.Equal(t, 0, cap(c.pool)) | ||
|
||
cfg.MaxConcurrency = 100 | ||
|
||
c = NewHTTPClient(cfg).(httpClient) | ||
assert.Equal(t, 100, cap(c.pool)) | ||
|
||
cfg.HTTPClient = &http.Client{} | ||
c = NewHTTPClient(cfg).(httpClient) | ||
assert.Equal(t, cfg.HTTPClient, c.client) | ||
} | ||
|
||
func TestHTTPClient_acquire_and_release(t *testing.T) { | ||
cfg := HTTPClientConfig{} | ||
c := NewHTTPClient(cfg).(httpClient) | ||
|
||
for i := 0; i < 5; i++ { | ||
c.acquire() | ||
} | ||
|
||
// chan is not used since it's disabled. | ||
assert.Len(t, c.pool, 0) | ||
|
||
c.release() | ||
assert.Len(t, c.pool, 0) | ||
|
||
cfg.MaxConcurrency = 2 | ||
c = NewHTTPClient(cfg).(httpClient) | ||
|
||
for i := 0; i < int(cfg.MaxConcurrency); i++ { | ||
c.acquire() | ||
} | ||
|
||
// chan is full. | ||
assert.Len(t, c.pool, 2) | ||
|
||
for i := cfg.MaxConcurrency; i > 0; i-- { | ||
c.release() | ||
assert.Len(t, c.pool, int(i-1)) | ||
} | ||
|
||
// chan is empty. | ||
assert.Len(t, c.pool, 0) | ||
} | ||
|
||
func TestHTTPClient_Do(t *testing.T) { | ||
client := NewHTTPClient(HTTPClientConfig{MaxConcurrency: 1}) | ||
|
||
testsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.WriteHeader(201) | ||
})) | ||
|
||
req, err := http.NewRequest("GET", testsrv.URL, nil) | ||
assert.NoError(t, err) | ||
|
||
res, err := client.Do(req) | ||
assert.NoError(t, err) | ||
assert.Equal(t, 201, res.StatusCode) | ||
} |