github.com/Ingenico-ePayments/connect-sdk-go@v0.0.0-20240318153750-1f8cd329b9c9/communicator/communication/Header.go (about)

     1  package communication
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  )
     8  
     9  var (
    10  	// ErrNoName occurs when the given name is empty
    11  	ErrNoName = errors.New("name is required")
    12  )
    13  
    14  // Header represents a single response header. Immutable.
    15  type Header struct {
    16  	name, value string
    17  }
    18  
    19  // NewHeader returns a Header with the given name and value
    20  func NewHeader(name, value string) (*Header, error) {
    21  	if len(name) == 0 {
    22  		return nil, ErrNoName
    23  	}
    24  	return &Header{name, value}, nil
    25  }
    26  
    27  // String is the implementation of the Stringer interface
    28  // Format: 'name:value'
    29  func (h Header) String() string {
    30  	return h.name + ":" + h.value
    31  }
    32  
    33  // Name returns the name of the header
    34  func (h Header) Name() string {
    35  	return h.name
    36  }
    37  
    38  // Value returns the value of the header
    39  func (h Header) Value() string {
    40  	return h.value
    41  }
    42  
    43  // Headers represents a slice of Header
    44  type Headers []Header
    45  
    46  // GetHeader searches for the headerName in the headers
    47  func (h Headers) GetHeader(headerName string) *Header {
    48  	for _, header := range h {
    49  		if strings.EqualFold(header.Name(), headerName) {
    50  			return &header
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  // GetHeaderValue searches for the header name and returns the value as string,
    57  // or the empty string if it doesn't exist. This doesn't break
    58  // HTTP support since headers can't have empty values
    59  func (h Headers) GetHeaderValue(headerName string) string {
    60  	for _, header := range h {
    61  		if strings.EqualFold(header.Name(), headerName) {
    62  			return header.Value()
    63  		}
    64  	}
    65  	return ""
    66  }
    67  
    68  var contentDispositionRegex = regexp.MustCompile("(?i)(?:^|;)\\s*filename\\s*=\\s*(.*?)\\s*(?:;|$)")
    69  
    70  // GetDispositionFilename returns the content of the filename found in the Content-Disposition header,
    71  // or the empty string if it couldn't have been found.
    72  func (h Headers) GetDispositionFilename() string {
    73  	headerValue := h.GetHeaderValue("Content-Disposition")
    74  
    75  	if headerValue == "" {
    76  		return ""
    77  	}
    78  
    79  	filenameResult := contentDispositionRegex.FindAllStringSubmatch(headerValue, -1)
    80  	if filenameResult != nil && filenameResult[0] != nil {
    81  		return trimQuotes(filenameResult[0][1])
    82  	}
    83  
    84  	return ""
    85  }
    86  
    87  // Removes the quotes from the file name
    88  func trimQuotes(filename string) string {
    89  	if len(filename) < 2 {
    90  		return filename
    91  	}
    92  
    93  	if (strings.HasPrefix(filename, "\"") && strings.HasSuffix(filename, "\"")) ||
    94  		(strings.HasPrefix(filename, "'") && strings.HasSuffix(filename, "'")) {
    95  		return filename[1 : len(filename)-1]
    96  	}
    97  
    98  	return filename
    99  }
   100  
   101  // Len represents the length of the slice
   102  func (h Headers) Len() int {
   103  	return len(h)
   104  }
   105  
   106  // Swap swaps two elements
   107  func (h Headers) Swap(i, j int) {
   108  	h[i], h[j] = h[j], h[i]
   109  }
   110  
   111  // Less checks if two positions are in lexicographic order
   112  func (h Headers) Less(i, j int) bool {
   113  	return h[i].Name() < h[j].Name()
   114  }