| 
									
										
											  
											
												perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via 
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
  roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
  which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte 
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address 
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight 
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
   performs a single request and returns the result. Any deadline provided on the request is
   forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
   will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
	// Responses from the remote server.
	// Channel will be closed after an error or when the remote closes.
	// All responses *must* be read by the caller until either an error is returned or the channel is closed.
	// Canceling the context will cause the context cancellation error to be returned.
	Responses <-chan Response
	// Requests sent to the server.
	// If the handler is defined with 0 incoming capacity this will be nil.
	// Channel *must* be closed to signal the end of the stream.
	// If the request context is canceled, the stream will no longer process requests.
	Requests chan<- []byte
}
type Response struct {
	Msg []byte
	Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
											
										 
											2023-11-21 09:09:35 +08:00
										 |  |  | // Copyright (c) 2015-2023 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package grid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2023-11-26 17:32:59 +08:00
										 |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-01-31 04:49:02 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
											  
											
												perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via 
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
  roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
  which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte 
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address 
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight 
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
   performs a single request and returns the result. Any deadline provided on the request is
   forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
   will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
	// Responses from the remote server.
	// Channel will be closed after an error or when the remote closes.
	// All responses *must* be read by the caller until either an error is returned or the channel is closed.
	// Canceling the context will cause the context cancellation error to be returned.
	Responses <-chan Response
	// Requests sent to the server.
	// If the handler is defined with 0 incoming capacity this will be nil.
	// Channel *must* be closed to signal the end of the stream.
	// If the request context is canceled, the stream will no longer process requests.
	Requests chan<- []byte
}
type Response struct {
	Msg []byte
	Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
											
										 
											2023-11-21 09:09:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/tinylib/msgp/msgp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MSS is a map[string]string that can be serialized.
 | 
					
						
							|  |  |  | // It is not very efficient, but it is only used for easy parameter passing.
 | 
					
						
							|  |  |  | type MSS map[string]string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get returns the value for the given key.
 | 
					
						
							|  |  |  | func (m *MSS) Get(key string) string { | 
					
						
							|  |  |  | 	if m == nil { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return (*m)[key] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalMsg deserializes m from the provided byte slice and returns the
 | 
					
						
							|  |  |  | // remainder of bytes.
 | 
					
						
							|  |  |  | func (m *MSS) UnmarshalMsg(bts []byte) (o []byte, err error) { | 
					
						
							|  |  |  | 	if m == nil { | 
					
						
							|  |  |  | 		return bts, errors.New("MSS: UnmarshalMsg on nil pointer") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if msgp.IsNil(bts) { | 
					
						
							|  |  |  | 		bts = bts[1:] | 
					
						
							|  |  |  | 		*m = nil | 
					
						
							|  |  |  | 		return bts, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var zb0002 uint32 | 
					
						
							|  |  |  | 	zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err = msgp.WrapError(err, "Values") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dst := *m | 
					
						
							|  |  |  | 	if dst == nil { | 
					
						
							|  |  |  | 		dst = make(map[string]string, zb0002) | 
					
						
							|  |  |  | 	} else if len(dst) > 0 { | 
					
						
							|  |  |  | 		for key := range dst { | 
					
						
							|  |  |  | 			delete(dst, key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for zb0002 > 0 { | 
					
						
							|  |  |  | 		var za0001 string | 
					
						
							|  |  |  | 		var za0002 string | 
					
						
							|  |  |  | 		zb0002-- | 
					
						
							|  |  |  | 		za0001, bts, err = msgp.ReadStringBytes(bts) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = msgp.WrapError(err, "Values") | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		za0002, bts, err = msgp.ReadStringBytes(bts) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = msgp.WrapError(err, "Values", za0001) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		dst[za0001] = za0002 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*m = dst | 
					
						
							|  |  |  | 	return bts, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalMsg appends the bytes representation of b to the provided byte slice.
 | 
					
						
							|  |  |  | func (m *MSS) MarshalMsg(bytes []byte) (o []byte, err error) { | 
					
						
							|  |  |  | 	if m == nil || *m == nil { | 
					
						
							|  |  |  | 		return msgp.AppendNil(bytes), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	o = msgp.AppendMapHeader(bytes, uint32(len(*m))) | 
					
						
							|  |  |  | 	for za0001, za0002 := range *m { | 
					
						
							|  |  |  | 		o = msgp.AppendString(o, za0001) | 
					
						
							|  |  |  | 		o = msgp.AppendString(o, za0002) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return o, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message.
 | 
					
						
							|  |  |  | func (m *MSS) Msgsize() int { | 
					
						
							|  |  |  | 	if m == nil || *m == nil { | 
					
						
							|  |  |  | 		return msgp.NilSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s := msgp.MapHeaderSize | 
					
						
							|  |  |  | 	for za0001, za0002 := range *m { | 
					
						
							|  |  |  | 		s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewMSS returns a new MSS.
 | 
					
						
							|  |  |  | func NewMSS() *MSS { | 
					
						
							|  |  |  | 	m := MSS(make(map[string]string)) | 
					
						
							|  |  |  | 	return &m | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewMSSWith returns a new MSS with the given map.
 | 
					
						
							|  |  |  | func NewMSSWith(m map[string]string) *MSS { | 
					
						
							|  |  |  | 	m2 := MSS(m) | 
					
						
							|  |  |  | 	return &m2 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-26 17:32:59 +08:00
										 |  |  | // ToQuery constructs a URL query string from the MSS, including "?" if there are any keys.
 | 
					
						
							|  |  |  | func (m MSS) ToQuery() string { | 
					
						
							|  |  |  | 	if len(m) == 0 { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var buf strings.Builder | 
					
						
							|  |  |  | 	buf.WriteByte('?') | 
					
						
							|  |  |  | 	keys := make([]string, 0, len(m)) | 
					
						
							|  |  |  | 	for k := range m { | 
					
						
							|  |  |  | 		keys = append(keys, k) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(keys) | 
					
						
							|  |  |  | 	for _, k := range keys { | 
					
						
							|  |  |  | 		v := m[k] | 
					
						
							|  |  |  | 		keyEscaped := url.QueryEscape(k) | 
					
						
							|  |  |  | 		if buf.Len() > 1 { | 
					
						
							|  |  |  | 			buf.WriteByte('&') | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buf.WriteString(keyEscaped) | 
					
						
							|  |  |  | 		buf.WriteByte('=') | 
					
						
							|  |  |  | 		buf.WriteString(url.QueryEscape(v)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return buf.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via 
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
  roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
  which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte 
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address 
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight 
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
   performs a single request and returns the result. Any deadline provided on the request is
   forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
   will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
	// Responses from the remote server.
	// Channel will be closed after an error or when the remote closes.
	// All responses *must* be read by the caller until either an error is returned or the channel is closed.
	// Canceling the context will cause the context cancellation error to be returned.
	Responses <-chan Response
	// Requests sent to the server.
	// If the handler is defined with 0 incoming capacity this will be nil.
	// Channel *must* be closed to signal the end of the stream.
	// If the request context is canceled, the stream will no longer process requests.
	Requests chan<- []byte
}
type Response struct {
	Msg []byte
	Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
											
										 
											2023-11-21 09:09:35 +08:00
										 |  |  | // NewBytes returns a new Bytes.
 | 
					
						
							|  |  |  | func NewBytes() *Bytes { | 
					
						
							|  |  |  | 	b := Bytes(GetByteBuffer()[:0]) | 
					
						
							|  |  |  | 	return &b | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewBytesWith returns a new Bytes with the provided content.
 | 
					
						
							|  |  |  | func NewBytesWith(b []byte) *Bytes { | 
					
						
							|  |  |  | 	bb := Bytes(b) | 
					
						
							|  |  |  | 	return &bb | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Bytes provides a byte slice that can be serialized.
 | 
					
						
							|  |  |  | type Bytes []byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalMsg deserializes b from the provided byte slice and returns the
 | 
					
						
							|  |  |  | // remainder of bytes.
 | 
					
						
							|  |  |  | func (b *Bytes) UnmarshalMsg(bytes []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	if b == nil { | 
					
						
							|  |  |  | 		return bytes, errors.New("Bytes: UnmarshalMsg on nil pointer") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if bytes, err := msgp.ReadNilBytes(bytes); err == nil { | 
					
						
							|  |  |  | 		*b = nil | 
					
						
							|  |  |  | 		return bytes, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	val, bytes, err := msgp.ReadBytesZC(bytes) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return bytes, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if cap(*b) >= len(val) { | 
					
						
							|  |  |  | 		*b = (*b)[:len(val)] | 
					
						
							|  |  |  | 		copy(*b, val) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		*b = append(make([]byte, 0, len(val)), val...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return bytes, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalMsg appends the bytes representation of b to the provided byte slice.
 | 
					
						
							|  |  |  | func (b *Bytes) MarshalMsg(bytes []byte) ([]byte, error) { | 
					
						
							|  |  |  | 	if b == nil || *b == nil { | 
					
						
							|  |  |  | 		return msgp.AppendNil(bytes), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return msgp.AppendBytes(bytes, *b), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message.
 | 
					
						
							|  |  |  | func (b *Bytes) Msgsize() int { | 
					
						
							|  |  |  | 	if b == nil || *b == nil { | 
					
						
							|  |  |  | 		return msgp.NilSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return msgp.ArrayHeaderSize + len(*b) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-01-31 04:49:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Recycle puts the Bytes back into the pool.
 | 
					
						
							|  |  |  | func (b *Bytes) Recycle() { | 
					
						
							|  |  |  | 	if *b != nil { | 
					
						
							|  |  |  | 		PutByteBuffer(*b) | 
					
						
							|  |  |  | 		*b = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // URLValues can be used for url.Values.
 | 
					
						
							|  |  |  | type URLValues map[string][]string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var urlValuesPool = sync.Pool{ | 
					
						
							|  |  |  | 	New: func() interface{} { | 
					
						
							|  |  |  | 		return make(map[string][]string, 10) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewURLValues returns a new URLValues.
 | 
					
						
							|  |  |  | func NewURLValues() *URLValues { | 
					
						
							|  |  |  | 	u := URLValues(urlValuesPool.Get().(map[string][]string)) | 
					
						
							|  |  |  | 	return &u | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewURLValuesWith returns a new URLValues with the provided content.
 | 
					
						
							|  |  |  | func NewURLValuesWith(values map[string][]string) *URLValues { | 
					
						
							|  |  |  | 	u := URLValues(values) | 
					
						
							|  |  |  | 	return &u | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Values returns the url.Values.
 | 
					
						
							|  |  |  | // If u is nil, an empty url.Values is returned.
 | 
					
						
							|  |  |  | // The values are a shallow copy of the underlying map.
 | 
					
						
							|  |  |  | func (u *URLValues) Values() url.Values { | 
					
						
							|  |  |  | 	if u == nil { | 
					
						
							|  |  |  | 		return url.Values{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return url.Values(*u) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Recycle the underlying map.
 | 
					
						
							|  |  |  | func (u *URLValues) Recycle() { | 
					
						
							|  |  |  | 	if *u != nil { | 
					
						
							|  |  |  | 		for key := range *u { | 
					
						
							|  |  |  | 			delete(*u, key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		val := map[string][]string(*u) | 
					
						
							|  |  |  | 		urlValuesPool.Put(val) | 
					
						
							|  |  |  | 		*u = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalMsg implements msgp.Marshaler
 | 
					
						
							|  |  |  | func (u URLValues) MarshalMsg(b []byte) (o []byte, err error) { | 
					
						
							|  |  |  | 	o = msgp.Require(b, u.Msgsize()) | 
					
						
							|  |  |  | 	o = msgp.AppendMapHeader(o, uint32(len(u))) | 
					
						
							|  |  |  | 	for zb0006, zb0007 := range u { | 
					
						
							|  |  |  | 		o = msgp.AppendString(o, zb0006) | 
					
						
							|  |  |  | 		o = msgp.AppendArrayHeader(o, uint32(len(zb0007))) | 
					
						
							|  |  |  | 		for zb0008 := range zb0007 { | 
					
						
							|  |  |  | 			o = msgp.AppendString(o, zb0007[zb0008]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // UnmarshalMsg implements msgp.Unmarshaler
 | 
					
						
							|  |  |  | func (u *URLValues) UnmarshalMsg(bts []byte) (o []byte, err error) { | 
					
						
							|  |  |  | 	var zb0004 uint32 | 
					
						
							|  |  |  | 	zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		err = msgp.WrapError(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if *u == nil { | 
					
						
							|  |  |  | 		*u = urlValuesPool.Get().(map[string][]string) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(*u) > 0 { | 
					
						
							|  |  |  | 		for key := range *u { | 
					
						
							|  |  |  | 			delete(*u, key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for zb0004 > 0 { | 
					
						
							|  |  |  | 		var zb0001 string | 
					
						
							|  |  |  | 		var zb0002 []string | 
					
						
							|  |  |  | 		zb0004-- | 
					
						
							|  |  |  | 		zb0001, bts, err = msgp.ReadStringBytes(bts) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = msgp.WrapError(err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var zb0005 uint32 | 
					
						
							|  |  |  | 		zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			err = msgp.WrapError(err, zb0001) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if cap(zb0002) >= int(zb0005) { | 
					
						
							|  |  |  | 			zb0002 = zb0002[:zb0005] | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			zb0002 = make([]string, zb0005) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for zb0003 := range zb0002 { | 
					
						
							|  |  |  | 			zb0002[zb0003], bts, err = msgp.ReadStringBytes(bts) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				err = msgp.WrapError(err, zb0001, zb0003) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		(*u)[zb0001] = zb0002 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	o = bts | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
 | 
					
						
							|  |  |  | func (u URLValues) Msgsize() (s int) { | 
					
						
							|  |  |  | 	s = msgp.MapHeaderSize | 
					
						
							|  |  |  | 	if u != nil { | 
					
						
							|  |  |  | 		for zb0006, zb0007 := range u { | 
					
						
							|  |  |  | 			_ = zb0007 | 
					
						
							|  |  |  | 			s += msgp.StringPrefixSize + len(zb0006) + msgp.ArrayHeaderSize | 
					
						
							|  |  |  | 			for zb0008 := range zb0007 { | 
					
						
							|  |  |  | 				s += msgp.StringPrefixSize + len(zb0007[zb0008]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |