chore(restructure): reorganize project layout
- Reorganize internal packages and overall project structure - Update imports and wiring to match the new layout - Separate HTTP parsing and streaming from the server package - Separate middleware from the server package - Separate session registry from the session package - Move HTTP, HTTPS, and TCP servers to the transport package - Session package no longer starts the TCP server directly - Server package no longer starts HTTP/HTTPS servers on initialization - Forwarder no longer handles accepting TCP requests - Move session details to the types package - HTTP/HTTPS initialization is now the responsibility of main
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package header
|
||||
|
||||
type ResponseHeader interface {
|
||||
Value(key string) string
|
||||
Set(key string, value string)
|
||||
Remove(key string)
|
||||
Finalize() []byte
|
||||
}
|
||||
|
||||
type responseHeader struct {
|
||||
startLine []byte
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
type RequestHeader interface {
|
||||
Value(key string) string
|
||||
Set(key string, value string)
|
||||
Remove(key string)
|
||||
Finalize() []byte
|
||||
GetMethod() string
|
||||
GetPath() string
|
||||
GetVersion() string
|
||||
}
|
||||
type requestHeader struct {
|
||||
method string
|
||||
path string
|
||||
version string
|
||||
startLine []byte
|
||||
headers map[string]string
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package header
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func setRemainingHeaders(remaining []byte, header interface {
|
||||
Set(key string, value string)
|
||||
}) {
|
||||
for len(remaining) > 0 {
|
||||
lineEnd := bytes.Index(remaining, []byte("\r\n"))
|
||||
if lineEnd == -1 {
|
||||
lineEnd = len(remaining)
|
||||
}
|
||||
|
||||
line := remaining[:lineEnd]
|
||||
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
colonIdx := bytes.IndexByte(line, ':')
|
||||
if colonIdx != -1 {
|
||||
key := bytes.TrimSpace(line[:colonIdx])
|
||||
value := bytes.TrimSpace(line[colonIdx+1:])
|
||||
header.Set(string(key), string(value))
|
||||
}
|
||||
|
||||
if lineEnd == len(remaining) {
|
||||
break
|
||||
}
|
||||
|
||||
remaining = remaining[lineEnd+2:]
|
||||
}
|
||||
}
|
||||
|
||||
func parseHeadersFromBytes(headerData []byte) (RequestHeader, error) {
|
||||
header := &requestHeader{
|
||||
headers: make(map[string]string, 16),
|
||||
}
|
||||
|
||||
lineEnd := bytes.Index(headerData, []byte("\r\n"))
|
||||
if lineEnd == -1 {
|
||||
return nil, fmt.Errorf("invalid request: no CRLF found in start line")
|
||||
}
|
||||
|
||||
startLine := headerData[:lineEnd]
|
||||
header.startLine = startLine
|
||||
var err error
|
||||
header.method, header.path, header.version, err = parseStartLine(startLine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
remaining := headerData[lineEnd+2:]
|
||||
|
||||
setRemainingHeaders(remaining, header)
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func parseStartLine(startLine []byte) (method, path, version string, err error) {
|
||||
firstSpace := bytes.IndexByte(startLine, ' ')
|
||||
if firstSpace == -1 {
|
||||
return "", "", "", fmt.Errorf("invalid start line: missing method")
|
||||
}
|
||||
|
||||
secondSpace := bytes.IndexByte(startLine[firstSpace+1:], ' ')
|
||||
if secondSpace == -1 {
|
||||
return "", "", "", fmt.Errorf("invalid start line: missing version")
|
||||
}
|
||||
secondSpace += firstSpace + 1
|
||||
|
||||
method = string(startLine[:firstSpace])
|
||||
path = string(startLine[firstSpace+1 : secondSpace])
|
||||
version = string(startLine[secondSpace+1:])
|
||||
|
||||
return method, path, version, nil
|
||||
}
|
||||
|
||||
func parseHeadersFromReader(br *bufio.Reader) (RequestHeader, error) {
|
||||
header := &requestHeader{
|
||||
headers: make(map[string]string, 16),
|
||||
}
|
||||
|
||||
startLineBytes, err := br.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startLineBytes = bytes.TrimRight(startLineBytes, "\r\n")
|
||||
header.startLine = make([]byte, len(startLineBytes))
|
||||
copy(header.startLine, startLineBytes)
|
||||
|
||||
header.method, header.path, header.version, err = parseStartLine(header.startLine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
lineBytes, err := br.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lineBytes = bytes.TrimRight(lineBytes, "\r\n")
|
||||
|
||||
if len(lineBytes) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
colonIdx := bytes.IndexByte(lineBytes, ':')
|
||||
if colonIdx == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := bytes.TrimSpace(lineBytes[:colonIdx])
|
||||
value := bytes.TrimSpace(lineBytes[colonIdx+1:])
|
||||
|
||||
header.headers[string(key)] = string(value)
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func finalize(startLine []byte, headers map[string]string) []byte {
|
||||
size := len(startLine) + 2
|
||||
for key, val := range headers {
|
||||
size += len(key) + 2 + len(val) + 2
|
||||
}
|
||||
size += 2
|
||||
|
||||
buf := make([]byte, 0, size)
|
||||
buf = append(buf, startLine...)
|
||||
buf = append(buf, '\r', '\n')
|
||||
|
||||
for key, val := range headers {
|
||||
buf = append(buf, key...)
|
||||
buf = append(buf, ':', ' ')
|
||||
buf = append(buf, val...)
|
||||
buf = append(buf, '\r', '\n')
|
||||
}
|
||||
|
||||
buf = append(buf, '\r', '\n')
|
||||
return buf
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package header
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func NewRequest(r interface{}) (RequestHeader, error) {
|
||||
switch v := r.(type) {
|
||||
case []byte:
|
||||
return parseHeadersFromBytes(v)
|
||||
case *bufio.Reader:
|
||||
return parseHeadersFromReader(v)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type: %T", r)
|
||||
}
|
||||
}
|
||||
|
||||
func (req *requestHeader) Value(key string) string {
|
||||
val, ok := req.headers[key]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (req *requestHeader) Set(key string, value string) {
|
||||
req.headers[key] = value
|
||||
}
|
||||
|
||||
func (req *requestHeader) Remove(key string) {
|
||||
delete(req.headers, key)
|
||||
}
|
||||
|
||||
func (req *requestHeader) GetMethod() string {
|
||||
return req.method
|
||||
}
|
||||
|
||||
func (req *requestHeader) GetPath() string {
|
||||
return req.path
|
||||
}
|
||||
|
||||
func (req *requestHeader) GetVersion() string {
|
||||
return req.version
|
||||
}
|
||||
|
||||
func (req *requestHeader) Finalize() []byte {
|
||||
return finalize(req.startLine, req.headers)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package header
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func NewResponse(headerData []byte) (ResponseHeader, error) {
|
||||
header := &responseHeader{
|
||||
startLine: nil,
|
||||
headers: make(map[string]string, 16),
|
||||
}
|
||||
|
||||
lineEnd := bytes.Index(headerData, []byte("\r\n"))
|
||||
if lineEnd == -1 {
|
||||
return nil, fmt.Errorf("invalid response: no CRLF found in start line")
|
||||
}
|
||||
|
||||
header.startLine = headerData[:lineEnd]
|
||||
remaining := headerData[lineEnd+2:]
|
||||
setRemainingHeaders(remaining, header)
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (resp *responseHeader) Value(key string) string {
|
||||
return resp.headers[key]
|
||||
}
|
||||
|
||||
func (resp *responseHeader) Set(key string, value string) {
|
||||
resp.headers[key] = value
|
||||
}
|
||||
|
||||
func (resp *responseHeader) Remove(key string) {
|
||||
delete(resp.headers, key)
|
||||
}
|
||||
|
||||
func (resp *responseHeader) Finalize() []byte {
|
||||
return finalize(resp.startLine, resp.headers)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package stream
|
||||
|
||||
import "bytes"
|
||||
|
||||
func splitHeaderAndBody(data []byte, delimiterIdx int) ([]byte, []byte) {
|
||||
headerByte := data[:delimiterIdx+len(DELIMITER)]
|
||||
body := data[delimiterIdx+len(DELIMITER):]
|
||||
return headerByte, body
|
||||
}
|
||||
|
||||
func isHTTPHeader(buf []byte) bool {
|
||||
lines := bytes.Split(buf, []byte("\r\n"))
|
||||
|
||||
startLine := string(lines[0])
|
||||
if !requestLine.MatchString(startLine) && !responseLine.MatchString(startLine) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, line := range lines[1:] {
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
colonIdx := bytes.IndexByte(line, ':')
|
||||
if colonIdx <= 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"tunnel_pls/internal/http/header"
|
||||
)
|
||||
|
||||
func (hs *http) Read(p []byte) (int, error) {
|
||||
tmp := make([]byte, len(p))
|
||||
read, err := hs.reader.Read(tmp)
|
||||
if read == 0 && err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
tmp = tmp[:read]
|
||||
|
||||
headerEndIdx := bytes.Index(tmp, DELIMITER)
|
||||
if headerEndIdx == -1 {
|
||||
return handleNoDelimiter(p, tmp, err)
|
||||
}
|
||||
|
||||
headerByte, bodyByte := splitHeaderAndBody(tmp, headerEndIdx)
|
||||
|
||||
if !isHTTPHeader(headerByte) {
|
||||
copy(p, tmp)
|
||||
return read, nil
|
||||
}
|
||||
|
||||
return hs.processHTTPRequest(p, headerByte, bodyByte)
|
||||
}
|
||||
|
||||
func (hs *http) processHTTPRequest(p, headerByte, bodyByte []byte) (int, error) {
|
||||
reqhf, err := header.NewRequest(headerByte)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err = hs.ApplyRequestMiddlewares(reqhf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hs.reqHeader = reqhf
|
||||
combined := append(reqhf.Finalize(), bodyByte...)
|
||||
return copy(p, combined), nil
|
||||
}
|
||||
|
||||
func handleNoDelimiter(p, tmp []byte, err error) (int, error) {
|
||||
copy(p, tmp)
|
||||
return len(tmp), err
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"regexp"
|
||||
"tunnel_pls/internal/http/header"
|
||||
"tunnel_pls/internal/middleware"
|
||||
)
|
||||
|
||||
var DELIMITER = []byte{0x0D, 0x0A, 0x0D, 0x0A}
|
||||
var requestLine = regexp.MustCompile(`^(GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH|TRACE|CONNECT) \S+ HTTP/\d\.\d$`)
|
||||
var responseLine = regexp.MustCompile(`^HTTP/\d\.\d \d{3} .+`)
|
||||
|
||||
type HTTP interface {
|
||||
io.ReadWriteCloser
|
||||
CloseWrite() error
|
||||
RemoteAddr() net.Addr
|
||||
UseResponseMiddleware(mw middleware.ResponseMiddleware)
|
||||
UseRequestMiddleware(mw middleware.RequestMiddleware)
|
||||
SetRequestHeader(header header.RequestHeader)
|
||||
RequestMiddlewares() []middleware.RequestMiddleware
|
||||
ResponseMiddlewares() []middleware.ResponseMiddleware
|
||||
ApplyResponseMiddlewares(resphf header.ResponseHeader, body []byte) error
|
||||
ApplyRequestMiddlewares(reqhf header.RequestHeader) error
|
||||
}
|
||||
|
||||
type http struct {
|
||||
remoteAddr net.Addr
|
||||
writer io.Writer
|
||||
reader io.Reader
|
||||
headerBuf []byte
|
||||
buf []byte
|
||||
respHeader header.ResponseHeader
|
||||
reqHeader header.RequestHeader
|
||||
respMW []middleware.ResponseMiddleware
|
||||
reqMW []middleware.RequestMiddleware
|
||||
}
|
||||
|
||||
func New(writer io.Writer, reader io.Reader, remoteAddr net.Addr) HTTP {
|
||||
return &http{
|
||||
remoteAddr: remoteAddr,
|
||||
writer: writer,
|
||||
reader: reader,
|
||||
buf: make([]byte, 0, 4096),
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *http) RemoteAddr() net.Addr {
|
||||
return hs.remoteAddr
|
||||
}
|
||||
|
||||
func (hs *http) UseResponseMiddleware(mw middleware.ResponseMiddleware) {
|
||||
hs.respMW = append(hs.respMW, mw)
|
||||
}
|
||||
|
||||
func (hs *http) UseRequestMiddleware(mw middleware.RequestMiddleware) {
|
||||
hs.reqMW = append(hs.reqMW, mw)
|
||||
}
|
||||
|
||||
func (hs *http) SetRequestHeader(header header.RequestHeader) {
|
||||
hs.reqHeader = header
|
||||
}
|
||||
|
||||
func (hs *http) RequestMiddlewares() []middleware.RequestMiddleware {
|
||||
return hs.reqMW
|
||||
}
|
||||
|
||||
func (hs *http) ResponseMiddlewares() []middleware.ResponseMiddleware {
|
||||
return hs.respMW
|
||||
}
|
||||
|
||||
func (hs *http) Close() error {
|
||||
return hs.writer.(io.Closer).Close()
|
||||
}
|
||||
|
||||
func (hs *http) CloseWrite() error {
|
||||
if closer, ok := hs.writer.(interface{ CloseWrite() error }); ok {
|
||||
return closer.CloseWrite()
|
||||
}
|
||||
return hs.Close()
|
||||
}
|
||||
|
||||
func (hs *http) ApplyRequestMiddlewares(reqhf header.RequestHeader) error {
|
||||
for _, m := range hs.RequestMiddlewares() {
|
||||
if err := m.HandleRequest(reqhf); err != nil {
|
||||
log.Printf("Error when applying request middleware: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *http) ApplyResponseMiddlewares(resphf header.ResponseHeader, bodyByte []byte) error {
|
||||
for _, m := range hs.ResponseMiddlewares() {
|
||||
if err := m.HandleResponse(resphf, bodyByte); err != nil {
|
||||
log.Printf("Cannot apply middleware: %s\n", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package stream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"tunnel_pls/internal/http/header"
|
||||
)
|
||||
|
||||
func (hs *http) Write(p []byte) (int, error) {
|
||||
if hs.shouldBypassBuffering(p) {
|
||||
hs.respHeader = nil
|
||||
}
|
||||
|
||||
if hs.respHeader != nil {
|
||||
return hs.writer.Write(p)
|
||||
}
|
||||
|
||||
hs.buf = append(hs.buf, p...)
|
||||
|
||||
headerEndIdx := bytes.Index(hs.buf, DELIMITER)
|
||||
if headerEndIdx == -1 {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
return hs.processBufferedResponse(p, headerEndIdx)
|
||||
}
|
||||
|
||||
func (hs *http) shouldBypassBuffering(p []byte) bool {
|
||||
return hs.respHeader != nil && len(hs.buf) == 0 && len(p) >= 5 && string(p[0:5]) == "HTTP/"
|
||||
}
|
||||
|
||||
func (hs *http) processBufferedResponse(p []byte, delimiterIdx int) (int, error) {
|
||||
headerByte, bodyByte := splitHeaderAndBody(hs.buf, delimiterIdx)
|
||||
|
||||
if !isHTTPHeader(headerByte) {
|
||||
return hs.writeRawBuffer()
|
||||
}
|
||||
|
||||
if err := hs.processHTTPResponse(headerByte, bodyByte); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
hs.buf = nil
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (hs *http) writeRawBuffer() (int, error) {
|
||||
_, err := hs.writer.Write(hs.buf)
|
||||
length := len(hs.buf)
|
||||
hs.buf = nil
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
func (hs *http) processHTTPResponse(headerByte, bodyByte []byte) error {
|
||||
resphf, err := header.NewResponse(headerByte)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = hs.ApplyResponseMiddlewares(resphf, bodyByte); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.respHeader = resphf
|
||||
finalHeader := resphf.Finalize()
|
||||
|
||||
if err = hs.writeHeaderAndBody(finalHeader, bodyByte); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *http) writeHeaderAndBody(header, bodyByte []byte) error {
|
||||
if _, err := hs.writer.Write(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bodyByte) > 0 {
|
||||
if _, err := hs.writer.Write(bodyByte); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user