github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/http/header.go (about)

     1  /* For license and copyright information please see the LEGAL file in the code repository */
     2  
     3  package http
     4  
     5  import (
     6  	"strings"
     7  
     8  	"golang.org/x/exp/maps"
     9  
    10  	"github.com/GeniusesGroup/libgo/convert"
    11  	"github.com/GeniusesGroup/libgo/protocol"
    12  )
    13  
    14  // header is represent HTTP header structure!
    15  type header struct {
    16  	headers    map[string][]string
    17  	valuesPool []string // shared backing array for headers' values
    18  }
    19  
    20  func (h *header) Init() {
    21  	h.headers = make(map[string][]string, headerInitMapLen)
    22  	h.valuesPool = make([]string, headerValuesPoolLen)
    23  }
    24  func (h *header) Reinit() {
    25  	maps.Clear(h.headers)
    26  	h.valuesPool = h.valuesPool[:0]
    27  }
    28  func (h *header) Deinit() {}
    29  
    30  // Get returns the first value associated with the given key.
    31  // Key must already be in CanonicalHeaderKey form.
    32  func (h *header) Get(key string) string {
    33  	var values = h.headers[key]
    34  	if len(values) > 0 {
    35  		return values[0]
    36  	}
    37  	return ""
    38  }
    39  
    40  // Gets returns all values associated with the given key.
    41  // Key must already be in CanonicalHeaderKey form.
    42  func (h *header) Gets(key string) []string {
    43  	var values = h.headers[key]
    44  	if len(values) > 0 {
    45  		return values
    46  	}
    47  	return nil
    48  }
    49  
    50  // Add adds the key, value pair to the header.
    51  // It appends to any existing values associated with key.
    52  // Key must already be in CanonicalHeaderKey form.
    53  func (h *header) Add(key, value string) {
    54  	var values []string = h.headers[key]
    55  	if values == nil {
    56  		h.Set(key, value)
    57  	} else {
    58  		h.headers[key] = append(values, value)
    59  	}
    60  }
    61  
    62  // Adds append given values to end of given key exiting values.
    63  // Key must already be in CanonicalHeaderKey form.
    64  func (h *header) Adds(key string, values []string) {
    65  	h.headers[key] = append(h.headers[key], values...)
    66  }
    67  
    68  // Set replace given value in given key.
    69  // Key must already be in CanonicalHeaderKey form.
    70  func (h *header) Set(key string, value string) {
    71  	if len(h.valuesPool) == 0 {
    72  		h.valuesPool = make([]string, headerValuesPoolLen)
    73  	}
    74  	// More than likely this will be a single-element key. Most headers aren't multi-valued.
    75  	// Set the capacity on valuesPool[0] to 1, so any future append won't extend the slice into the other strings.
    76  	var values []string = h.valuesPool[:1:1]
    77  	h.valuesPool = h.valuesPool[1:]
    78  	values[0] = value
    79  	h.headers[key] = values
    80  }
    81  
    82  // Sets sets the header entries associated with key to
    83  // the single element value. It replaces any existing values associated with key.
    84  // Key must already be in CanonicalHeaderKey form.
    85  func (h *header) Sets(key string, values []string) {
    86  	h.headers[key] = values
    87  }
    88  
    89  // Replace change the exiting header entry associated with key to the given single element value.
    90  // It use for some logic like TransferEncoding(), ...
    91  func (h *header) Replace(key string, value string) {
    92  	h.headers[key][0] = value
    93  }
    94  
    95  // Del deletes the values associated with key.
    96  // Key must already be in CanonicalHeaderKey form.
    97  func (h *header) Del(key string) {
    98  	delete(h.headers, key)
    99  }
   100  
   101  // Exclude eliminate headers by given keys!
   102  func (h *header) Exclude(exclude map[string]bool) {
   103  	for key := range exclude {
   104  		delete(h.headers, key)
   105  	}
   106  }
   107  
   108  //libgo:impl protocol.Codec
   109  func (h *header) Decode(source protocol.Codec) (n int, err protocol.Error) {
   110  	// TODO:::
   111  	return
   112  }
   113  func (h *header) Encode(destination protocol.Codec) (n int, err protocol.Error) {
   114  	var encodedHeader = h.Marshal()
   115  	n, err = destination.Unmarshal(encodedHeader)
   116  	return
   117  }
   118  func (h *header) Marshal() (httpHeader []byte) {
   119  	httpHeader = make([]byte, 0, h.Len())
   120  	httpHeader = h.MarshalTo(httpHeader)
   121  	return
   122  }
   123  func (h *header) MarshalTo(httpPacket []byte) []byte {
   124  	for key, values := range h.headers {
   125  		// TODO::: some header key must not inline by coma like set-cookie. check if other need this exception.
   126  		switch key {
   127  		case HeaderKeySetCookie:
   128  			for _, value := range values {
   129  				httpPacket = append(httpPacket, key...)
   130  				httpPacket = append(httpPacket, ColonSpace...)
   131  				httpPacket = append(httpPacket, value...)
   132  				httpPacket = append(httpPacket, CRLF...)
   133  			}
   134  		default:
   135  			httpPacket = append(httpPacket, key...)
   136  			httpPacket = append(httpPacket, ColonSpace...)
   137  			for _, value := range values {
   138  				httpPacket = append(httpPacket, value...)
   139  				httpPacket = append(httpPacket, Comma)
   140  			}
   141  			httpPacket = httpPacket[:len(httpPacket)-1] // Remove trailing comma
   142  			httpPacket = append(httpPacket, CRLF...)
   143  		}
   144  	}
   145  	return httpPacket
   146  }
   147  func (h *header) Unmarshal(data []byte) (n int, err protocol.Error) {
   148  	var s = convert.UnsafeByteSliceToString(data)
   149  	n, err = h.unmarshal(s)
   150  	return
   151  }
   152  func (h *header) Len() (ln int) {
   153  	for key, values := range h.headers {
   154  		ln += len(key)
   155  		ln += 4 // 4=len(ColonSpace)+len(CRLF)
   156  		for _, value := range values {
   157  			ln += len(value)
   158  			ln++ // 1=len(Coma)
   159  		}
   160  	}
   161  	return
   162  }
   163  
   164  /*
   165  ********** Other methods **********
   166   */
   167  
   168  // unmarshal parses and decodes data of given httpPacket(without first line) to (h *header).
   169  // This method not respect to some RFCs like field-name in RFC7230, ... due to be more liberal in what it accept!
   170  // In some bad packet may occur panic, handle panic by recover otherwise app will crash and exit!
   171  func (h *header) unmarshal(s string) (headerEnd int, err protocol.Error) {
   172  	var colonIndex, newLineIndex int
   173  	var key, value string
   174  	for {
   175  		newLineIndex = strings.IndexByte(s, '\r')
   176  		if newLineIndex < 3 {
   177  			// newLineIndex == -1 >> broken or malformed packet, panic may occur!
   178  			// newLineIndex == 0 >> End of headers part of packet, no panic
   179  			// 1 < newLineIndex > 3 >> bad header || broken || malformed packet, panic may occur!
   180  			return
   181  		}
   182  
   183  		colonIndex = strings.IndexByte(s[:newLineIndex], ':')
   184  		switch colonIndex {
   185  		case -1:
   186  			// TODO::: Header key without value!?? Bad http packet!??
   187  		default:
   188  			key = s[:colonIndex]
   189  			value = s[colonIndex+2 : newLineIndex] // +2 due to have a space after colon force by RFC &&
   190  			h.Add(key, value)                      // TODO::: is legal to have multiple key in request header or use h.Set()??
   191  		}
   192  		newLineIndex += 2 // +2 due to have "\r\n" at end of each header line
   193  		s = s[newLineIndex:]
   194  		headerEnd += newLineIndex
   195  	}
   196  }