mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-16 16:17:38 +03:00
Document Request, Response, RequestHeader, ResponseHeader, URI and Args
This commit is contained in:
@@ -5,6 +5,9 @@ import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Args represents query arguments.
|
||||
//
|
||||
// It is forbidden copying Args instances. Create new instances instead.
|
||||
type Args struct {
|
||||
args []argsKV
|
||||
bufKV argsKV
|
||||
@@ -16,19 +19,25 @@ type argsKV struct {
|
||||
value []byte
|
||||
}
|
||||
|
||||
// Clear clear query args.
|
||||
func (a *Args) Clear() {
|
||||
a.args = a.args[:0]
|
||||
}
|
||||
|
||||
// Len returns the number of query args.
|
||||
func (a *Args) Len() int {
|
||||
return len(a.args)
|
||||
}
|
||||
|
||||
// Parse parses the given string containing query args.
|
||||
func (a *Args) Parse(s string) {
|
||||
a.buf = AppendBytesStr(a.buf[:0], s)
|
||||
a.ParseBytes(a.buf)
|
||||
}
|
||||
|
||||
// ParseBytes parses the given b containing query args.
|
||||
//
|
||||
// It is safe modifying b buffer conntents after ParseBytes return.
|
||||
func (a *Args) ParseBytes(b []byte) {
|
||||
a.Clear()
|
||||
|
||||
@@ -56,11 +65,16 @@ func (a *Args) ParseBytes(b []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns string representation of query args.
|
||||
func (a *Args) String() string {
|
||||
a.buf = a.AppendBytes(a.buf[:0])
|
||||
return string(a.buf)
|
||||
}
|
||||
|
||||
// AppendBytes appends query string to dst and returns dst
|
||||
// (which may be newly allocated).
|
||||
//
|
||||
// It is safe modifying dst buffer after AppendBytes returns.
|
||||
func (a *Args) AppendBytes(dst []byte) []byte {
|
||||
for i, n := 0, len(a.args); i < n; i++ {
|
||||
kv := &a.args[i]
|
||||
@@ -76,6 +90,7 @@ func (a *Args) AppendBytes(dst []byte) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
// Del deletes argument with the given key from query args.
|
||||
func (a *Args) Del(key string) {
|
||||
for i, n := 0, len(a.args); i < n; i++ {
|
||||
kv := &a.args[i]
|
||||
@@ -89,11 +104,15 @@ func (a *Args) Del(key string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets 'key=value' argument.
|
||||
func (a *Args) Set(key, value string) {
|
||||
a.bufKV.value = AppendBytesStr(a.bufKV.value[:0], value)
|
||||
a.SetBytes(key, a.bufKV.value)
|
||||
}
|
||||
|
||||
// SetBytes sets 'key=value' argument.
|
||||
//
|
||||
// It is safe modifying valye buffer after SetBytes() return.
|
||||
func (a *Args) SetBytes(key string, value []byte) {
|
||||
a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key)
|
||||
a.args = setKV(a.args, a.bufKV.key, value)
|
||||
@@ -133,35 +152,42 @@ func peekKV(h []argsKV, k []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Args) GetBytes(dst []byte, key string) []byte {
|
||||
value := a.Peek(key)
|
||||
return append(dst[:0], value...)
|
||||
}
|
||||
|
||||
// Warning: each call allocates memory for returned string.
|
||||
// Get returns query arg value for the given key.
|
||||
//
|
||||
// Each Get call allocates memory for returned string,
|
||||
// so consider using Peek() instead.
|
||||
func (a *Args) Get(key string) string {
|
||||
return string(a.Peek(key))
|
||||
}
|
||||
|
||||
// Peek returns query arg value for the given key.
|
||||
//
|
||||
// Returned value is valid until the next Args call.
|
||||
func (a *Args) Peek(key string) []byte {
|
||||
a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key)
|
||||
return peekKV(a.args, a.bufKV.key)
|
||||
}
|
||||
|
||||
// Has returns true if the given key exists in Args.
|
||||
func (a *Args) Has(key string) bool {
|
||||
return a.Peek(key) != nil
|
||||
}
|
||||
|
||||
var errNoArgValue = errors.New("No value for the given key")
|
||||
// ErrNoArgValue is returned when value with the given key is missing.
|
||||
var ErrNoArgValue = errors.New("No value for the given key")
|
||||
|
||||
// GetUint returns uint value for the given key.
|
||||
func (a *Args) GetUint(key string) (int, error) {
|
||||
value := a.Peek(key)
|
||||
if len(value) == 0 {
|
||||
return -1, errNoArgValue
|
||||
return -1, ErrNoArgValue
|
||||
}
|
||||
return parseUint(value)
|
||||
}
|
||||
|
||||
// GetUintOrZero returns uint value for the given key.
|
||||
//
|
||||
// Zero (0) is returned on error.
|
||||
func (a *Args) GetUintOrZero(key string) int {
|
||||
n, err := a.GetUint(key)
|
||||
if err != nil {
|
||||
@@ -170,14 +196,18 @@ func (a *Args) GetUintOrZero(key string) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// GetUfloat returns ufloat value for the given key.
|
||||
func (a *Args) GetUfloat(key string) (float64, error) {
|
||||
value := a.Peek(key)
|
||||
if len(value) == 0 {
|
||||
return -1, errNoArgValue
|
||||
return -1, ErrNoArgValue
|
||||
}
|
||||
return parseUfloat(value)
|
||||
}
|
||||
|
||||
// GetUfloatOrZero returns ufloat value for the given key.
|
||||
//
|
||||
// Zero (0) is returned on error.
|
||||
func (a *Args) GetUfloatOrZero(key string) float64 {
|
||||
f, err := a.GetUfloat(key)
|
||||
if err != nil {
|
||||
@@ -186,6 +216,9 @@ func (a *Args) GetUfloatOrZero(key string) float64 {
|
||||
return f
|
||||
}
|
||||
|
||||
// EqualBytesStr returns true if string(b) == s.
|
||||
//
|
||||
// It doesn't allocate memory unlike string(b) do.
|
||||
func EqualBytesStr(b []byte, s string) bool {
|
||||
if len(s) != len(b) {
|
||||
return false
|
||||
@@ -198,6 +231,8 @@ func EqualBytesStr(b []byte, s string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AppendBytesStr appends src to dst and returns dst
|
||||
// (which may be newly allocated).
|
||||
func AppendBytesStr(dst []byte, src string) []byte {
|
||||
for i, n := 0, len(src); i < n; i++ {
|
||||
dst = append(dst, src[i])
|
||||
|
||||
+1
-2
@@ -188,7 +188,6 @@ func testArgsHasNot(t *testing.T, a *Args, s string, unexpectedKeys ...string) {
|
||||
}
|
||||
|
||||
func testArgsParse(t *testing.T, a *Args, s string, expectedLen int, expectedArgs ...string) {
|
||||
var buf []byte
|
||||
a.Parse(s)
|
||||
if a.Len() != expectedLen {
|
||||
t.Fatalf("Unexpected args len %d. Expected %d. s=%q", a.Len(), expectedLen, s)
|
||||
@@ -197,7 +196,7 @@ func testArgsParse(t *testing.T, a *Args, s string, expectedLen int, expectedArg
|
||||
tmp := strings.SplitN(xx, "=", 2)
|
||||
k := tmp[0]
|
||||
v := tmp[1]
|
||||
buf = a.GetBytes(buf, k)
|
||||
buf := a.Peek(k)
|
||||
if string(buf) != v {
|
||||
t.Fatalf("Unexpected value for key=%q: %q. Expected %q. s=%q", k, buf, v, s)
|
||||
}
|
||||
|
||||
@@ -45,9 +45,17 @@ var (
|
||||
strPostArgsContentType = []byte("application/x-www-form-urlencoded")
|
||||
)
|
||||
|
||||
// ResponseHeader represents HTTP response header.
|
||||
type ResponseHeader struct {
|
||||
StatusCode int
|
||||
ContentLength int
|
||||
// Response status code.
|
||||
StatusCode int
|
||||
|
||||
// Response content length read from Content-Length header.
|
||||
//
|
||||
// It may be negative on chunked response.
|
||||
ContentLength int
|
||||
|
||||
// Set to true if response contains 'Connection: close' header.
|
||||
ConnectionClose bool
|
||||
|
||||
contentType []byte
|
||||
@@ -57,9 +65,17 @@ type ResponseHeader struct {
|
||||
bufKV argsKV
|
||||
}
|
||||
|
||||
// RequestHeader represents HTTP request header.
|
||||
type RequestHeader struct {
|
||||
Method []byte
|
||||
RequestURI []byte
|
||||
// Request method (e.g. 'GET', 'POST', etc.).
|
||||
Method []byte
|
||||
|
||||
// Request URI read from the first request line.
|
||||
RequestURI []byte
|
||||
|
||||
// Request content length read from Content-Length header.
|
||||
//
|
||||
// It may be negative on chunked request.
|
||||
ContentLength int
|
||||
|
||||
host []byte
|
||||
@@ -69,18 +85,22 @@ type RequestHeader struct {
|
||||
bufKV argsKV
|
||||
}
|
||||
|
||||
// IsMethodGet returns true if request method is GET.
|
||||
func (h *RequestHeader) IsMethodGet() bool {
|
||||
return bytes.Equal(h.Method, strGet)
|
||||
}
|
||||
|
||||
// IsMethodPost returns true if request methos is POST.
|
||||
func (h *RequestHeader) IsMethodPost() bool {
|
||||
return bytes.Equal(h.Method, strPost)
|
||||
}
|
||||
|
||||
// IsMethodHead returns true if request method is HEAD.
|
||||
func (h *RequestHeader) IsMethodHead() bool {
|
||||
return bytes.Equal(h.Method, strHead)
|
||||
}
|
||||
|
||||
// Clear clears response header.
|
||||
func (h *ResponseHeader) Clear() {
|
||||
h.StatusCode = 0
|
||||
h.ContentLength = 0
|
||||
@@ -92,6 +112,7 @@ func (h *ResponseHeader) Clear() {
|
||||
h.h = h.h[:0]
|
||||
}
|
||||
|
||||
// Clear clears request header.
|
||||
func (h *RequestHeader) Clear() {
|
||||
h.Method = h.Method[:0]
|
||||
h.RequestURI = h.RequestURI[:0]
|
||||
@@ -103,6 +124,7 @@ func (h *RequestHeader) Clear() {
|
||||
h.h = h.h[:0]
|
||||
}
|
||||
|
||||
// Set sets the given 'key: value' header.
|
||||
func (h *ResponseHeader) Set(key, value string) {
|
||||
initHeaderKV(&h.bufKV, key, value)
|
||||
h.set(h.bufKV.key, h.bufKV.value)
|
||||
@@ -130,6 +152,9 @@ func (h *ResponseHeader) set(key, value []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetBytes sets the given 'key: value' header.
|
||||
//
|
||||
// It is safe modifying value buffer after SetBytes return.
|
||||
func (h *ResponseHeader) SetBytes(key string, value []byte) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.set(k, value)
|
||||
@@ -140,6 +165,7 @@ func (h *ResponseHeader) setStr(key []byte, value string) {
|
||||
h.set(key, h.bufKV.value)
|
||||
}
|
||||
|
||||
// Set sets the given 'key: value' header.
|
||||
func (h *RequestHeader) Set(key, value string) {
|
||||
initHeaderKV(&h.bufKV, key, value)
|
||||
h.set(h.bufKV.key, h.bufKV.value)
|
||||
@@ -162,16 +188,27 @@ func (h *RequestHeader) set(key, value []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// SetBytes sets the given 'key: value' header.
|
||||
//
|
||||
// It is safe modifying value buffer after SetBytes return.
|
||||
func (h *RequestHeader) SetBytes(key string, value []byte) {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
h.set(k, value)
|
||||
}
|
||||
|
||||
// Peek returns header value for the given key.
|
||||
//
|
||||
// Returned value may change on the next call to ResponseHeader.
|
||||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *ResponseHeader) Peek(key string) []byte {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
return h.peek(k)
|
||||
}
|
||||
|
||||
// Peek returns header value for the given key.
|
||||
//
|
||||
// Returned value may change on the next call to RequestHeader.
|
||||
// Do not store references to returned value. Make copies instead.
|
||||
func (h *RequestHeader) Peek(key string) []byte {
|
||||
k := getHeaderKeyBytes(&h.bufKV, key)
|
||||
return h.peek(k)
|
||||
@@ -204,14 +241,21 @@ func (h *RequestHeader) peek(key []byte) []byte {
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns header value for the given key.
|
||||
//
|
||||
// Get allocates memory on each call, so prefer using Peek instead.
|
||||
func (h *ResponseHeader) Get(key string) string {
|
||||
return string(h.Peek(key))
|
||||
}
|
||||
|
||||
// Get returns header value for the given key.
|
||||
//
|
||||
// Get allocates memory on each call, so prefer using Peek instead.
|
||||
func (h *RequestHeader) Get(key string) string {
|
||||
return string(h.Peek(key))
|
||||
}
|
||||
|
||||
// Read reads response header from r.
|
||||
func (h *ResponseHeader) Read(r *bufio.Reader) error {
|
||||
n := 1
|
||||
for {
|
||||
@@ -253,6 +297,7 @@ func (h *ResponseHeader) tryRead(r *bufio.Reader, n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads request header from r.
|
||||
func (h *RequestHeader) Read(r *bufio.Reader) error {
|
||||
n := 1
|
||||
for {
|
||||
@@ -320,6 +365,7 @@ func refreshServerDate() {
|
||||
serverDate.Store([]byte(s))
|
||||
}
|
||||
|
||||
// Write writes response header to w.
|
||||
func (h *ResponseHeader) Write(w *bufio.Writer) error {
|
||||
statusCode := h.StatusCode
|
||||
if statusCode < 0 {
|
||||
@@ -363,6 +409,7 @@ func (h *ResponseHeader) Write(w *bufio.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write writes request header to w.
|
||||
func (h *RequestHeader) Write(w *bufio.Writer) error {
|
||||
method := h.Method
|
||||
if len(method) == 0 {
|
||||
|
||||
@@ -9,14 +9,22 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Request represents HTTP request.
|
||||
//
|
||||
// It is forbidden copying Request instances. Create new instances instead.
|
||||
type Request struct {
|
||||
// Request header
|
||||
Header RequestHeader
|
||||
Body []byte
|
||||
|
||||
// Request body
|
||||
Body []byte
|
||||
|
||||
// Request URI.
|
||||
// URI becomes available only after Request.ParseURI() call.
|
||||
URI URI
|
||||
parsedURI bool
|
||||
|
||||
// Arguments sent in POST.
|
||||
// PostArgs becomes available only after Request.ParsePostArgs() call.
|
||||
PostArgs Args
|
||||
parsedPostArgs bool
|
||||
@@ -25,11 +33,17 @@ type Request struct {
|
||||
timeoutTimer *time.Timer
|
||||
}
|
||||
|
||||
// Response represents HTTP response.
|
||||
//
|
||||
// It is forbidden copying Response instances. Create new instances instead.
|
||||
type Response struct {
|
||||
// Response header
|
||||
Header ResponseHeader
|
||||
Body []byte
|
||||
|
||||
// if set to true, Response.Read() skips reading body.
|
||||
// Response body
|
||||
Body []byte
|
||||
|
||||
// If set to true, Response.Read() skips reading body.
|
||||
// Use it for HEAD requests.
|
||||
SkipBody bool
|
||||
|
||||
@@ -37,6 +51,7 @@ type Response struct {
|
||||
timeoutTimer *time.Timer
|
||||
}
|
||||
|
||||
// ParseURI parses request uri and fills Request.URI.
|
||||
func (req *Request) ParseURI() {
|
||||
if req.parsedURI {
|
||||
return
|
||||
@@ -45,6 +60,7 @@ func (req *Request) ParseURI() {
|
||||
req.parsedURI = true
|
||||
}
|
||||
|
||||
// ParsePostArgs parses args sent in POST body and fills Request.PostArgs.
|
||||
func (req *Request) ParsePostArgs() error {
|
||||
if req.parsedPostArgs {
|
||||
return nil
|
||||
@@ -62,6 +78,7 @@ func (req *Request) ParsePostArgs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear clears request contents.
|
||||
func (req *Request) Clear() {
|
||||
req.Header.Clear()
|
||||
req.Body = req.Body[:0]
|
||||
@@ -71,13 +88,21 @@ func (req *Request) Clear() {
|
||||
req.parsedPostArgs = false
|
||||
}
|
||||
|
||||
// Clear clears response contents.
|
||||
func (resp *Response) Clear() {
|
||||
resp.Header.Clear()
|
||||
resp.Body = resp.Body[:0]
|
||||
}
|
||||
|
||||
// ErrReadTimeout may be returned from Request.ReadTimeout
|
||||
// or Response.ReadTimeout on timeout.
|
||||
var ErrReadTimeout = errors.New("read timeout")
|
||||
|
||||
// ReadTimeout reads request (including body) from the given r during
|
||||
// the given timeout.
|
||||
//
|
||||
// If request couldn't be read during the given timeout,
|
||||
// it returns ErrReadTimeout.
|
||||
func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
|
||||
if timeout <= 0 {
|
||||
return req.Read(r)
|
||||
@@ -107,6 +132,11 @@ func (req *Request) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadTimeout reads response (including body) from the given r during
|
||||
// the given timeout.
|
||||
//
|
||||
// If response couldn't be read during the given timeout,
|
||||
// it returns ErrReadTimeout.
|
||||
func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error {
|
||||
if timeout <= 0 {
|
||||
return resp.Read(r)
|
||||
@@ -136,6 +166,7 @@ func (resp *Response) ReadTimeout(r *bufio.Reader, timeout time.Duration) error
|
||||
return err
|
||||
}
|
||||
|
||||
// Read reads request (including body) from the given r.
|
||||
func (req *Request) Read(r *bufio.Reader) error {
|
||||
req.Body = req.Body[:0]
|
||||
req.URI.Clear()
|
||||
@@ -159,6 +190,7 @@ func (req *Request) Read(r *bufio.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads response (including body) from the given r.
|
||||
func (resp *Response) Read(r *bufio.Reader) error {
|
||||
resp.Body = resp.Body[:0]
|
||||
|
||||
@@ -190,6 +222,9 @@ func isSkipResponseBody(statusCode int) bool {
|
||||
return statusCode == StatusNoContent || statusCode == StatusNotModified
|
||||
}
|
||||
|
||||
// Write write request to w.
|
||||
//
|
||||
// Write doesn't flush request to w for performance reasons.
|
||||
func (req *Request) Write(w *bufio.Writer) error {
|
||||
contentLengthOld := req.Header.ContentLength
|
||||
req.Header.ContentLength = len(req.Body)
|
||||
@@ -206,6 +241,9 @@ func (req *Request) Write(w *bufio.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write writes response to w.
|
||||
//
|
||||
// Write doesn't flush response to w for performance reasons.
|
||||
func (resp *Response) Write(w *bufio.Writer) error {
|
||||
contentLengthOld := resp.Header.ContentLength
|
||||
resp.Header.ContentLength = len(resp.Body)
|
||||
|
||||
@@ -4,24 +4,46 @@ import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// URI represents URI :) .
|
||||
//
|
||||
// It is forbidden copying URI instances. Create new instances instead.
|
||||
type URI struct {
|
||||
// Full uri like {Scheme}://{Host}{Path}?{QueryString}#{Hash}
|
||||
URI []byte
|
||||
|
||||
// Original Path passed to URI.Parse()
|
||||
// Original path passed to URI.Parse()
|
||||
PathOriginal []byte
|
||||
|
||||
Scheme []byte
|
||||
Host []byte
|
||||
Path []byte
|
||||
QueryString []byte
|
||||
Hash []byte
|
||||
// Scheme part, i.e. http of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
//
|
||||
// Scheme is always lowercased.
|
||||
Scheme []byte
|
||||
|
||||
// Becomes available after URI.ParseQueryArgs() call
|
||||
// Host part, i.e. aaa.com of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
//
|
||||
// Host is always lowercased.
|
||||
Host []byte
|
||||
|
||||
// Path part, i.e. /foo/bar of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
//
|
||||
// Path is always urldecoded and normalized,
|
||||
// i.e. '//f%20obar/baz/../zzz' becomes '/f obar/zzz'.
|
||||
Path []byte
|
||||
|
||||
// Query string part, i.e. baz=123 of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
QueryString []byte
|
||||
|
||||
// Hash part, i.e. qwe of http://aaa.com/foo/bar?baz=123#qwe .
|
||||
Hash []byte
|
||||
|
||||
// Parsed query string arguments.
|
||||
//
|
||||
// Becomes available after URI.ParseQueryArgs() call.
|
||||
QueryArgs Args
|
||||
parsedQueryArgs bool
|
||||
}
|
||||
|
||||
// Clear clears uri.
|
||||
func (x *URI) Clear() {
|
||||
x.URI = x.URI[:0]
|
||||
x.PathOriginal = x.PathOriginal[:0]
|
||||
@@ -34,6 +56,9 @@ func (x *URI) Clear() {
|
||||
x.parsedQueryArgs = false
|
||||
}
|
||||
|
||||
// Parse initializes URI from the given host and uri.
|
||||
//
|
||||
// It is safe modifying host and uri buffers after the Parse call.
|
||||
func (x *URI) Parse(host, uri []byte) {
|
||||
x.Clear()
|
||||
|
||||
@@ -122,7 +147,10 @@ func normalizePath(dst, src []byte) []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// Appends RequestURI to dst. RequestURI doesn't contain Scheme and Host.
|
||||
// AppendRequestURI appends RequestURI to dst and returns dst
|
||||
// (which may be newly allocated).
|
||||
//
|
||||
// Appended RequestURI doesn't contain Scheme and Host.
|
||||
func (x *URI) AppendRequestURI(dst []byte) []byte {
|
||||
path := x.Path
|
||||
if len(path) == 0 {
|
||||
@@ -140,7 +168,7 @@ func (x *URI) AppendRequestURI(dst []byte) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
// Appends URI to dst.
|
||||
// AppendBytes appends URI to dst and returns dst (with may be newly allocated).
|
||||
func (x *URI) AppendBytes(dst []byte) []byte {
|
||||
startPos := len(dst)
|
||||
scheme := x.Scheme
|
||||
@@ -172,6 +200,7 @@ func splitHostUri(host, uri []byte) ([]byte, []byte, []byte) {
|
||||
return scheme, uri[:n], uri[n:]
|
||||
}
|
||||
|
||||
// ParseQueryArgs initializes QueryArgs by parsing QueryString.
|
||||
func (x *URI) ParseQueryArgs() {
|
||||
if x.parsedQueryArgs {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user