github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/http/header.go (about)

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