2017-05-17 15:22:44 +02:00
package try
import (
2022-08-11 15:42:07 +02:00
"context"
2017-05-17 15:22:44 +02:00
"errors"
"fmt"
2021-03-04 20:08:03 +01:00
"io"
2017-05-17 15:22:44 +02:00
"net/http"
2019-04-02 03:40:04 -05:00
"reflect"
2017-05-17 15:22:44 +02:00
"strings"
2022-01-12 14:42:21 +01:00
"github.com/kvtools/valkeyrie/store"
2017-05-17 15:22:44 +02:00
)
// ResponseCondition is a retry condition function.
2020-05-11 12:06:07 +02:00
// It receives a response, and returns an error if the response failed the condition.
2017-05-17 15:22:44 +02:00
type ResponseCondition func ( * http . Response ) error
// BodyContains returns a retry condition function.
2020-05-11 12:06:07 +02:00
// The condition returns an error if the request body does not contain all the given strings.
2017-05-17 15:22:44 +02:00
func BodyContains ( values ... string ) ResponseCondition {
return func ( res * http . Response ) error {
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2017-05-17 15:22:44 +02:00
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "failed to read response body: %w" , err )
2017-05-17 15:22:44 +02:00
}
for _ , value := range values {
if ! strings . Contains ( string ( body ) , value ) {
return fmt . Errorf ( "could not find '%s' in body '%s'" , value , string ( body ) )
}
}
return nil
}
}
2017-12-22 01:02:04 +08:00
// BodyNotContains returns a retry condition function.
2020-05-11 12:06:07 +02:00
// The condition returns an error if the request body contain one of the given strings.
2017-12-22 01:02:04 +08:00
func BodyNotContains ( values ... string ) ResponseCondition {
return func ( res * http . Response ) error {
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2017-12-22 01:02:04 +08:00
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "failed to read response body: %w" , err )
2017-12-22 01:02:04 +08:00
}
for _ , value := range values {
if strings . Contains ( string ( body ) , value ) {
return fmt . Errorf ( "find '%s' in body '%s'" , value , string ( body ) )
}
}
return nil
}
}
2017-05-17 15:22:44 +02:00
// BodyContainsOr returns a retry condition function.
2020-05-11 12:06:07 +02:00
// The condition returns an error if the request body does not contain one of the given strings.
2017-05-17 15:22:44 +02:00
func BodyContainsOr ( values ... string ) ResponseCondition {
return func ( res * http . Response ) error {
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2017-05-17 15:22:44 +02:00
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "failed to read response body: %w" , err )
2017-05-17 15:22:44 +02:00
}
for _ , value := range values {
if strings . Contains ( string ( body ) , value ) {
return nil
}
}
return fmt . Errorf ( "could not find '%v' in body '%s'" , values , string ( body ) )
}
}
// HasBody returns a retry condition function.
// The condition returns an error if the request body does not have body content.
func HasBody ( ) ResponseCondition {
return func ( res * http . Response ) error {
2021-03-04 20:08:03 +01:00
body , err := io . ReadAll ( res . Body )
2017-05-17 15:22:44 +02:00
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "failed to read response body: %w" , err )
2017-05-17 15:22:44 +02:00
}
if len ( body ) == 0 {
2018-07-03 10:02:03 +02:00
return errors . New ( "response doesn't have body content" )
2017-05-17 15:22:44 +02:00
}
return nil
}
}
2018-07-26 12:42:03 +02:00
// HasCn returns a retry condition function.
// The condition returns an error if the cn is not correct.
func HasCn ( cn string ) ResponseCondition {
return func ( res * http . Response ) error {
if res . TLS == nil {
return errors . New ( "response doesn't have TLS" )
}
if len ( res . TLS . PeerCertificates ) == 0 {
return errors . New ( "response TLS doesn't have peer certificates" )
}
if res . TLS . PeerCertificates [ 0 ] == nil {
return errors . New ( "first peer certificate is nil" )
}
commonName := res . TLS . PeerCertificates [ 0 ] . Subject . CommonName
if cn != commonName {
return fmt . Errorf ( "common name don't match: %s != %s" , cn , commonName )
}
return nil
}
}
2017-05-17 15:22:44 +02:00
// StatusCodeIs returns a retry condition function.
// The condition returns an error if the given response's status code is not the
// given HTTP status code.
func StatusCodeIs ( status int ) ResponseCondition {
return func ( res * http . Response ) error {
if res . StatusCode != status {
return fmt . Errorf ( "got status code %d, wanted %d" , res . StatusCode , status )
}
return nil
}
}
2019-04-02 03:40:04 -05:00
// HasHeader returns a retry condition function.
// The condition returns an error if the response does not have a header set.
func HasHeader ( header string ) ResponseCondition {
return func ( res * http . Response ) error {
if _ , ok := res . Header [ header ] ; ! ok {
return errors . New ( "response doesn't contain header: " + header )
}
return nil
}
}
// HasHeaderValue returns a retry condition function.
// The condition returns an error if the response does not have a header set, and a value for that header.
// Has an option to test for an exact header match only, not just contains.
func HasHeaderValue ( header , value string , exactMatch bool ) ResponseCondition {
return func ( res * http . Response ) error {
if _ , ok := res . Header [ header ] ; ! ok {
return errors . New ( "response doesn't contain header: " + header )
}
matchFound := false
for _ , hdr := range res . Header [ header ] {
if value != hdr && exactMatch {
return fmt . Errorf ( "got header %s with value %s, wanted %s" , header , hdr , value )
}
if value == hdr {
matchFound = true
}
}
if ! matchFound {
return fmt . Errorf ( "response doesn't contain header %s with value %s" , header , value )
}
return nil
}
}
// HasHeaderStruct returns a retry condition function.
// The condition returns an error if the response does contain the headers set, and matching contents.
func HasHeaderStruct ( header http . Header ) ResponseCondition {
return func ( res * http . Response ) error {
for key := range header {
2019-07-12 03:46:04 -06:00
if _ , ok := res . Header [ key ] ; ! ok {
return fmt . Errorf ( "header %s not present in the response. Expected headers: %v Got response headers: %v" , key , header , res . Header )
}
2019-04-02 03:40:04 -05:00
2019-07-12 03:46:04 -06:00
// Header exists in the response, test it.
if ! reflect . DeepEqual ( header [ key ] , res . Header [ key ] ) {
return fmt . Errorf ( "for header %s got values %v, wanted %v" , key , res . Header [ key ] , header [ key ] )
2019-04-02 03:40:04 -05:00
}
}
return nil
}
}
2017-05-17 15:22:44 +02:00
// DoCondition is a retry condition function.
2020-05-11 12:06:07 +02:00
// It returns an error.
2017-05-17 15:22:44 +02:00
type DoCondition func ( ) error
// KVExists is a retry condition function.
2020-05-11 12:06:07 +02:00
// Verify if a Key exists in the store.
2017-05-17 15:22:44 +02:00
func KVExists ( kv store . Store , key string ) DoCondition {
return func ( ) error {
2022-08-11 15:42:07 +02:00
_ , err := kv . Exists ( context . Background ( ) , key , nil )
2017-05-17 15:22:44 +02:00
return err
}
}