💾 Archived View for source.community › ckaznocha › gemini › raw › main › request.go captured on 2023-07-10 at 13:38:05.

View Raw

More Information

⬅️ Previous capture (2021-12-17)

🚧 View Differences

-=-=-=-=-=-=-

package gemini

import (
	"bufio"
	"crypto/x509/pkix"
	"fmt"
	"net/textproto"
	"reflect"
	"sync"
	"unsafe"
)

const maxRequestLength = 1024

// Request is a Gemini request.
type Request struct {
	Subject    *pkix.Name
	URI        *URI
	RemoteAddr string
}

var requestPool = sync.Pool{
	New: func() interface{} {
		b := make([]byte, 0, maxRequestLength)

		return &b
	},
}

// ReadRequest reads and parses a Gemini request from buffer. It returns an
// error if the request could not be read or was  malformed.
func ReadRequest(b *bufio.Reader) (*Request, error) {
	u, ok := requestPool.Get().(*[]byte)
	if !ok {
		return nil, fmt.Errorf("%w: %s", ErrRequestRead, "request pool returned non-byte slice")
	}

	defer requestPool.Put(u)

	sh := (*reflect.SliceHeader)(unsafe.Pointer(u)) //nolint:gosec // This is safe.
	sh.Len = 0

	cr := false

	for {
		b, err := b.ReadByte()
		if err != nil {
			return nil, fmt.Errorf("%w: %s", ErrRequestRead, err)
		}

		if b == '\r' {
			cr = true

			continue
		}

		if cr && b == '\n' {
			break
		}

		cr = false

		*u = append(*u, b)
		if len(*u) > maxRequestLength {
			return nil, ErrMaxRequestLengthExceeded
		}
	}

	*u = textproto.TrimBytes(*u)

	uri, err := ParseRequestURI(*(*string)(unsafe.Pointer(u))) //nolint:gosec // This is safe.
	if err != nil {
		return nil, err
	}

	return &Request{URI: uri}, nil
}