2018-09-22 19:22:42 +00:00
/ *
* Copyright ( c ) 2018 Jeffrey Walter < jeffreydwalter @ gmail . com >
*
* Permission is hereby granted , free of charge , to any person obtaining a copy of this software and associated
* documentation files ( the "Software" ) , to deal in the Software without restriction , including without limitation the
* rights to use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to the following conditions :
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR
* OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
* /
2017-12-10 19:17:09 +00:00
package request
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/cookiejar"
"net/url"
2018-09-22 19:22:42 +00:00
"sync"
2018-12-11 23:48:51 +00:00
"time"
2017-12-10 19:17:09 +00:00
"github.com/pkg/errors"
2018-09-18 18:02:21 +00:00
"golang.org/x/net/publicsuffix"
2017-12-10 19:17:09 +00:00
)
type Client struct {
2018-09-22 19:22:42 +00:00
BaseURL * url . URL
BaseHeaders * http . Header
HttpClient * http . Client
rwmutex sync . RWMutex
2017-12-10 19:17:09 +00:00
}
2018-09-22 19:22:42 +00:00
func NewClient ( baseURL string , baseHeaders http . Header ) ( * Client , error ) {
2017-12-10 19:17:09 +00:00
var err error
var jar * cookiejar . Jar
2018-09-18 18:02:21 +00:00
options := cookiejar . Options { PublicSuffixList : publicsuffix . List }
2017-12-10 19:17:09 +00:00
if jar , err = cookiejar . New ( & options ) ; err != nil {
return nil , errors . Wrap ( err , "failed to create client object" )
}
var u * url . URL
2018-09-22 19:22:42 +00:00
if u , err = url . Parse ( baseURL ) ; err != nil {
2017-12-10 19:17:09 +00:00
return nil , errors . Wrap ( err , "failed to create client object" )
}
header := make ( http . Header )
2018-09-19 19:12:06 +00:00
header . Set ( "User-Agent" , "Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NETGEAR/v1 (iOS Vuezone)" )
header . Set ( "Content-Type" , "application/json" )
header . Set ( "Accept" , "application/json" )
2017-12-10 19:17:09 +00:00
return & Client {
2018-09-22 19:22:42 +00:00
BaseURL : u ,
BaseHeaders : & header ,
2018-12-11 23:48:51 +00:00
HttpClient : & http . Client { Jar : jar , Timeout : 30 * time . Second } ,
2017-12-10 19:17:09 +00:00
} , nil
}
2018-09-22 19:22:42 +00:00
func ( c * Client ) AddHeader ( key , value string ) {
c . rwmutex . Lock ( )
c . BaseHeaders . Set ( key , value )
c . rwmutex . Unlock ( )
}
2017-12-10 19:17:09 +00:00
func ( c * Client ) Get ( uri string , header http . Header ) ( * Response , error ) {
req , err := c . newRequest ( "GET" , uri , nil , header )
if err != nil {
return nil , errors . WithMessage ( err , "get request " + uri + " failed" )
}
return c . do ( req )
}
func ( c * Client ) Post ( uri string , body interface { } , header http . Header ) ( * Response , error ) {
req , err := c . newRequest ( "POST" , uri , body , header )
if err != nil {
return nil , errors . WithMessage ( err , "post request " + uri + " failed" )
}
return c . do ( req )
}
func ( c * Client ) Put ( uri string , body interface { } , header http . Header ) ( * Response , error ) {
req , err := c . newRequest ( "PUT" , uri , body , header )
if err != nil {
return nil , errors . WithMessage ( err , "put request " + uri + " failed" )
}
return c . do ( req )
}
func ( c * Client ) newRequest ( method string , uri string , body interface { } , header http . Header ) ( * Request , error ) {
var buf io . ReadWriter
if body != nil {
buf = new ( bytes . Buffer )
err := json . NewEncoder ( buf ) . Encode ( body )
if err != nil {
return nil , errors . Wrap ( err , "failed to create request object" )
}
}
2018-12-11 23:48:51 +00:00
// log.Printf("\n\nBODY (%s): %s\n\n", uri, buf)
2018-09-18 18:02:21 +00:00
2017-12-10 19:17:09 +00:00
u := c . BaseURL . String ( ) + uri
req , err := http . NewRequest ( method , u , buf )
if err != nil {
return nil , errors . Wrap ( err , "failed to create request object" )
}
2018-09-22 19:22:42 +00:00
c . rwmutex . RLock ( )
baseHeaders := * c . BaseHeaders
c . rwmutex . RUnlock ( )
for k , v := range baseHeaders {
2017-12-10 19:17:09 +00:00
for _ , h := range v {
2018-09-19 19:12:06 +00:00
//log.Printf("Adding header (%s): (%s - %s)\n\n", u, k, h)
req . Header . Set ( k , h )
2017-12-10 19:17:09 +00:00
}
}
for k , v := range header {
for _ , h := range v {
2018-09-19 19:12:06 +00:00
//log.Printf("Adding header (%s): (%s - %s)\n\n", u, k, h)
req . Header . Set ( k , h )
2017-12-10 19:17:09 +00:00
}
}
return & Request {
Request : * req ,
} , nil
}
func ( c * Client ) newResponse ( resp * http . Response ) ( * Response , error ) {
return & Response {
Response : * resp ,
} , nil
}
func ( c * Client ) do ( req * Request ) ( * Response , error ) {
2018-12-11 23:48:51 +00:00
// log.Printf("\n\nCOOKIES (%s): %v\n\n", req.URL, c.HttpClient.Jar.Cookies(req.URL))
// log.Printf("\n\nHEADERS (%s): %v\n\n", req.URL, req.Header)
2017-12-10 19:17:09 +00:00
2018-09-17 04:44:41 +00:00
resp , err := c . HttpClient . Do ( & req . Request )
2017-12-10 19:17:09 +00:00
if err != nil {
return nil , errors . Wrap ( err , "failed to execute http request" )
}
2018-09-17 04:44:41 +00:00
if resp . StatusCode >= http . StatusBadRequest {
2017-12-10 19:17:09 +00:00
defer resp . Body . Close ( )
return nil , errors . New ( "http request failed with status: " + resp . Status )
}
return c . newResponse ( resp )
}