Skip to content

Commit

Permalink
Document Request, Response, RequestHeader, ResponseHeader, URI and Args
Browse files Browse the repository at this point in the history
  • Loading branch information
valyala committed Oct 23, 2015
1 parent 40e775d commit e9d6d7f
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 26 deletions.
53 changes: 44 additions & 9 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()

Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand All @@ -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])
Expand Down
3 changes: 1 addition & 2 deletions args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down
55 changes: 51 additions & 4 deletions header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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]
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit e9d6d7f

Please sign in to comment.