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 }