From 1ae8fab210a949caa3076f5b6ee218781ff0beaa Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 31 May 2025 14:08:32 -0300 Subject: [PATCH] receive HTTP request success; still working on response --- examples/stackbasic/main.go | 43 +++++++++++++++++----------- examples/tcpclient/main.go | 42 ++++++++++++++++++++++------ http/httpraw/header.go | 32 +++++++++++++++------ internal/ltesto/httptap.go | 56 ++++++++++++++++++++++++------------- 4 files changed, 119 insertions(+), 54 deletions(-) diff --git a/examples/stackbasic/main.go b/examples/stackbasic/main.go index 4eede40..ff0a8b5 100644 --- a/examples/stackbasic/main.go +++ b/examples/stackbasic/main.go @@ -61,6 +61,7 @@ func main() { fmt.Println("hosting server at ", addrPort.String(), "over tap interface of mtu:", mtu, "prefix:", ippfx, "gateway:", net.HardwareAddr(gatewayMAC[:]).String()) buf := make([]byte, mtu) var hdr httpraw.Header + hdr.Reset(make([]byte, 0, 1024)) for { nread, err := tap.Read(buf[:]) if err != nil { @@ -74,6 +75,7 @@ func main() { slogger.error("recv", slog.String("err", err.Error()), slog.Int("plen", nread)) } } + doHTTP(handler, &hdr) nw, err := lStack.HandleEth(buf[:]) debugEthPacket(nil, "OUT", buf[:nw]) if err != nil { @@ -87,29 +89,36 @@ func main() { slogger.info("write", slog.Int("plen", nw)) } } - if handler.State() == tcp.StateEstablished { - data := handler.BufferedInput() - if data > 0 { - n, err := handler.Read(buf[:]) - if err != nil { - slogger.error("tcp-read", slog.String("err", err.Error())) - } else { - hdr.Reset(buf[:n]) - err = hdr.Parse(false) - if err != nil { - slogger.error("http-parse", slog.String("err", err.Error())) - } else { - fmt.Println(hdr.String()) - } - } - } - } + if nread == 0 && nw == 0 { time.Sleep(5 * time.Millisecond) } } } +func doHTTP(conn *internet.TCPConn, hdr *httpraw.Header) error { + const asRequest = false + if conn.State() != tcp.StateEstablished || conn.BufferedInput() == 0 { + return nil // No data yet. + } + fmt.Println("state is established; check request and send response") + _, err := hdr.ReadFromLimited(conn, hdr.Free()) + if err != nil { + return err + } + needMore, err := hdr.TryParse(asRequest) + if err != nil { + if !needMore { + fmt.Println("IT's SO GOVER") + conn.Close() + } + return err + } + // HTTP parsed succesfully! + fmt.Println("GOT HTTP:\n", hdr.String()) + return nil +} + func NewEthernetTCPStack(ourMAC, gwMAC [6]byte, ip netip.AddrPort, mtu uint16, slogger logger) (*LinkStack, *internet.TCPConn, error) { var err error lStack := LinkStack{ diff --git a/examples/tcpclient/main.go b/examples/tcpclient/main.go index fcd0d4b..cb0ea57 100644 --- a/examples/tcpclient/main.go +++ b/examples/tcpclient/main.go @@ -4,6 +4,8 @@ import ( "fmt" "net" "os" + "os/signal" + "syscall" "github.com/soypat/lneto/http/httpraw" ) @@ -17,27 +19,51 @@ func main() { } func run() error { + // Prepare GET request. + var hdr httpraw.Header + hdr.SetMethod("GET") + hdr.SetRequestURI("/") + hdr.SetProtocol("HTTP/1.1") + req, err := hdr.AppendRequest(nil) + if err != nil { + return err + } + fmt.Println("dialing...") conn, err := net.DialTCP("tcp4", &net.TCPAddr{IP: []byte{192, 168, 10, 1}, Port: 1337}, &net.TCPAddr{IP: []byte{192, 168, 10, 2}, Port: 80}) if err != nil { return err } + defer conn.Close() + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + s := <-c + fmt.Println("terminating connection on signal", s.String()) + conn.Close() + os.Exit(0) + }() fmt.Println("reading...") - var hdr httpraw.Header - for { + conn.Write(req) + + hdr.Reset(nil) + var needMore bool = true + for needMore { _, err = hdr.ReadFromLimited(conn, 1024) if err != nil { - return err + break } - const asRequest = false - var ok bool - ok, err = hdr.TryParse(asRequest) - if ok { + const asResponse = true + needMore, err = hdr.TryParse(asResponse) + if needMore { break } else if err != nil { - return err + break } } + if err != nil { + return err + } fmt.Println("got HTTP:\n", hdr.String()) return nil } diff --git a/http/httpraw/header.go b/http/httpraw/header.go index cda21d6..2760165 100644 --- a/http/httpraw/header.go +++ b/http/httpraw/header.go @@ -24,6 +24,7 @@ const ( flagConnClose flagNoHTTP11 flagMangledBuffer // set when header fields appended to buffer via Add,Set calls + flagReaderEOF ) func (f flags) hasAny(checkThese flags) bool { @@ -77,20 +78,21 @@ func (h *Header) Parse(asResponse bool) error { } // TryParse begins parsing or resumes parsing from a failed previous attempt from any of the Parse* methods. -// It fails if HTTP data is incomplete. It panics if called after header parsing completed succesfully. -// As long as ok returns true future calls to TryParse may succeed. +// As long as needMoreData returns true future calls to TryParse may succeed and the header is not done parsing. // -// ok, err := h.TryParse() -// for ; ok; ok, err = h.TryParse() { -// _, err = h.ReadFrom(r, 256) -// if err != nil && err != io.EOF { -// return err +// needMoreData := true +// var err error +// for needMoreData { +// _, err = h.ReadFrom(r, 1024) +// if err != nil { +// break // } +// needMoreData, err = h.TryParse() // } // if err != nil { // return err // } -func (h *Header) TryParse(asResponse bool) (ok bool, err error) { +func (h *Header) TryParse(asResponse bool) (needMoreData bool, err error) { if h.flags.hasAny(flagDoneParsingHeader) { return false, errors.New("TryParse called after header parsed") } else if h.flags.hasAny(flagMangledBuffer) { @@ -103,11 +105,12 @@ func (h *Header) TryParse(asResponse bool) (ok bool, err error) { } } err = h.parseNextHeaders() - return err == nil || err == errNeedMore, err + return err == errNeedMore, err } // ReadFromLimited reads at most maxBytesToRead from reader and appends them to underlying buffer. // Used to accumulate HTTP header for later parsing with [Header.TryParse]. +// If read is successful (read length>0) and reader returns [io.EOF] then ReadFromLimited will return a nil error. func (h *Header) ReadFromLimited(r io.Reader, maxBytesToRead int) (int, error) { if maxBytesToRead <= 0 { return 0, errSmallBuffer @@ -124,6 +127,12 @@ func (h *Header) ReadFromLimited(r io.Reader, maxBytesToRead int) (int, error) { blen := len(h.hbuf.buf) b := h.hbuf.buf[blen:min(blen+maxBytesToRead, cap(h.hbuf.buf))] n, err := r.Read(b) + if err != nil && err == io.EOF { + h.flags |= flagReaderEOF + if n > 0 { + err = nil // Nil-out error if read was succesful so as to not spook readers. + } + } h.hbuf.buf = h.hbuf.buf[:blen+n] return n, err } @@ -150,6 +159,11 @@ func (h *Header) Free() int { return h.hbuf.free() } +// Capacity returns the total capacity of the underlying buffer. +func (h *Header) Capacity() int { + return cap(h.hbuf.buf) +} + // ForEach iterates over header key-value field tuples. func (h *Header) ForEach(cb func(key, value []byte) error) error { return h.hbuf.forEach(cb) diff --git a/internal/ltesto/httptap.go b/internal/ltesto/httptap.go index 71e0207..4787c68 100644 --- a/internal/ltesto/httptap.go +++ b/internal/ltesto/httptap.go @@ -56,7 +56,7 @@ func (h *HTTPTapClient) ensureMTU() (err error) { if err != nil { return err } - var info TapInfo + var info tapInfo err = json.NewDecoder(resp.Body).Decode(&info) if err != nil { return err @@ -85,40 +85,56 @@ type HTTPTapClient struct { buf []byte } -func (h *HTTPTapClient) ReadDiscard() { - var data [2048]byte - var n int = -1 - for n != 0 { - n, _ = h.Read(data[:]) // Empty remote data. +func (h *HTTPTapClient) ReadDiscard() error { + for { + d, _ := h.ReadBytes() // Empty remote data. + if len(d) == 0 { + break + } } + return nil +} + +func (h *HTTPTapClient) ReadBytes() (data []byte, err error) { + err = h.ensureMTU() + if err != nil { + return nil, err + } + resp, err := h.c.Get(h.recvurl) + if err != nil { + return nil, err + } else if resp.StatusCode != 200 { + return nil, errors.New(resp.Status + " for " + h.recvurl) + } + buf := h.buf + err = json.NewDecoder(resp.Body).Decode(&buf) + if err != nil { + return nil, err + } + return buf, nil } func (h *HTTPTapClient) Read(b []byte) (int, error) { err := h.ensureMTU() if err != nil { return 0, err + } else if len(b) < h.MTU() { + return 0, errors.New("buffer must have at least MTU size") } - resp, err := h.c.Get(h.recvurl) + data, err := h.ReadBytes() if err != nil { return 0, err - } else if resp.StatusCode != 200 { - return 0, errors.New(resp.Status + " for " + h.recvurl) } - var data []byte - err = json.NewDecoder(resp.Body).Decode(&data) - if err != nil { - return 0, err - } else if len(b) < len(data) { - return 0, fmt.Errorf("got too large packet %d for buffer %d", len(data), len(b)) - } - copy(b, data) - return len(data), nil + n := copy(b, data) + return n, nil } func (h *HTTPTapClient) Write(b []byte) (int, error) { err := h.ensureMTU() if err != nil { return 0, err + } else if len(b) > h.MTU() { + return 0, errors.New("buffer larger than MTU") } data, _ := json.Marshal(b) resp, err := h.c.Post(h.sendurl, "application/json", bytes.NewReader(data)) @@ -140,7 +156,7 @@ type HTTPTapServer struct { tapfailed bool } -type TapInfo struct { +type tapInfo struct { MTU int IPPrefix string HardwareAddr string @@ -185,7 +201,7 @@ func NewHTTPTapServer(iface string, ip netip.Prefix, mtu, queueOut, queueIn int) }) ipstr := ip.String() sv.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) { - info := TapInfo{ + info := tapInfo{ MTU: mtu, IPPrefix: ipstr, }