boi
This commit is contained in:
commit
4a3d59dea9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.exe
|
100
client.go
Normal file
100
client.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
url2 "net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DoConn(c *bufio.ReadWriter, method string, url string, hdrs Headers, reqbody io.Reader) (res *Message, resbody io.Reader, err error) {
|
||||||
|
resbody = EmptyBody
|
||||||
|
if reqbody == nil {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
u, err := url2.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hdrs == nil {
|
||||||
|
hdrs = make(Headers)
|
||||||
|
}
|
||||||
|
req := &Message{
|
||||||
|
Method: method,
|
||||||
|
Path: u.RequestURI(),
|
||||||
|
Version: VersionHTTP1_1,
|
||||||
|
Headers: hdrs,
|
||||||
|
}
|
||||||
|
req.Set("Host", u.Host)
|
||||||
|
req.Set("User-Agent", "boihttp-go/1.0")
|
||||||
|
err = req.WriteRequest(c.Writer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.Writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.Writer.ReadFrom(reqbody)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = new(Message)
|
||||||
|
err = res.ReadResponse(c.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resbody = res.body(c.Reader)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Do(method string, url string, hdrs Headers, reqbody io.Reader, dial DialFunc, ctx context.Context) (res *Message, resbody io.Reader, err error) {
|
||||||
|
u, err := url2.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if u.Scheme != "http" {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
host, port := u.Hostname(), u.Port()
|
||||||
|
if port == "" {
|
||||||
|
port = "80"
|
||||||
|
}
|
||||||
|
if dial == nil {
|
||||||
|
var d net.Dialer
|
||||||
|
dial = d.DialContext
|
||||||
|
}
|
||||||
|
c, err := dial(ctx, "tcp", net.JoinHostPort(host, port))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
||||||
|
res, resbody, err = DoConn(rw, method, url, hdrs, reqbody)
|
||||||
|
if resbody == EmptyBody || resbody.(*io.LimitedReader).N == 0 {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
resbody = &closeOnEOF{r: reqbody, c: c}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type closeOnEOF struct {
|
||||||
|
r io.Reader
|
||||||
|
c net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *closeOnEOF) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = x.r.Read(p)
|
||||||
|
if err == io.EOF {
|
||||||
|
x.c.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
|
func Get(url string, hdrs Headers, ctx context.Context) (res *Message, resbody io.Reader, err error) {
|
||||||
|
return Do("GET", url, hdrs, EmptyBody, nil, ctx)
|
||||||
|
}
|
72
client_test.go
Normal file
72
client_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nadeen.lk/boihttp"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
const httpbin_addr = "httpbin.org:80"
|
||||||
|
const httpbin_url = "http://httpbin.org/anything"
|
||||||
|
|
||||||
|
func TestClientDoConn(t *testing.T) {
|
||||||
|
c, err := net.Dial("tcp", httpbin_addr)
|
||||||
|
throw(err)
|
||||||
|
defer c.Close()
|
||||||
|
r, w := bufio.NewReaderSize(c, 1024), bufio.NewWriterSize(c, 1024)
|
||||||
|
res, body, err := boihttp.DoConn(bufio.NewReadWriter(r, w), "GET", httpbin_url, nil, boihttp.EmptyBody)
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDoProxy(t *testing.T) {
|
||||||
|
d, err := proxy.SOCKS5("tcp", "127.0.0.1:1080", nil, proxy.Direct)
|
||||||
|
throw(err)
|
||||||
|
res, body, err := boihttp.Do("GET", httpbin_url, nil, boihttp.EmptyBody, d.(proxy.ContextDialer).DialContext, context.Background())
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDoGet(t *testing.T) {
|
||||||
|
res, body, err := boihttp.Do("GET", httpbin_url, nil, boihttp.EmptyBody, nil, context.Background())
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
func TestClientGet(t *testing.T) {
|
||||||
|
res, body, err := boihttp.Get(httpbin_url, nil, context.Background())
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDoPost(t *testing.T) {
|
||||||
|
reqbody := []byte("what's up doc?")
|
||||||
|
hdrs := make(boihttp.Headers)
|
||||||
|
hdrs.SetContentLength(len(reqbody))
|
||||||
|
res, body, err := boihttp.Do("POST", httpbin_url, hdrs, bytes.NewReader(reqbody), nil, context.Background())
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDoMultiple(t *testing.T) {
|
||||||
|
c, err := net.Dial("tcp", httpbin_addr)
|
||||||
|
throw(err)
|
||||||
|
defer c.Close()
|
||||||
|
r, w := bufio.NewReaderSize(c, 1024), bufio.NewWriterSize(c, 1024)
|
||||||
|
hdrs := make(boihttp.Headers)
|
||||||
|
hdrs.SetKeepAlive(true)
|
||||||
|
for z := 4; z >= 0; z-- {
|
||||||
|
if z == 0 {
|
||||||
|
hdrs.SetKeepAlive(false)
|
||||||
|
}
|
||||||
|
res, body, err := boihttp.DoConn(bufio.NewReadWriter(r, w), "GET", httpbin_url, hdrs.Clone(), boihttp.EmptyBody)
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
||||||
|
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module git.nadeen.lk/boihttp
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc=
|
||||||
|
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
65
headers.go
Normal file
65
headers.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Headers map[string][]string
|
||||||
|
|
||||||
|
func (h Headers) Add(key, value string) {
|
||||||
|
textproto.MIMEHeader(h).Add(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) Get(key string) string {
|
||||||
|
return textproto.MIMEHeader(h).Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) Has(key string) bool {
|
||||||
|
return len(textproto.MIMEHeader(h).Values(key)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) Set(key string, value string) {
|
||||||
|
textproto.MIMEHeader(h).Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) Del(key string) {
|
||||||
|
textproto.MIMEHeader(h).Del(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) Clone() Headers {
|
||||||
|
x := make(Headers)
|
||||||
|
for k, v := range h {
|
||||||
|
y := make([]string, len(v))
|
||||||
|
copy(y, v)
|
||||||
|
x[k] = y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) SetKeepAlive(keepalive bool) {
|
||||||
|
if keepalive {
|
||||||
|
h.Set("Connection", "keep-alive")
|
||||||
|
} else {
|
||||||
|
h.Set("Connection", "close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) KeepAlive() bool {
|
||||||
|
return strings.ToLower(h.Get("Connection")) == "keep-alive"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) ContentLength() int {
|
||||||
|
x, err := strconv.Atoi(h.Get("Content-Length"))
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Headers) SetContentLength(x int) {
|
||||||
|
h.Set("Content-Length", strconv.Itoa(x))
|
||||||
|
}
|
137
message.go
Normal file
137
message.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
VersionHTTP1_0 = "HTTP/1.0"
|
||||||
|
VersionHTTP1_1 = "HTTP/1.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc2616#section-4
|
||||||
|
type Message struct {
|
||||||
|
StatusCode int
|
||||||
|
Version, Method, Path, Status string
|
||||||
|
Headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) SetStatus(code int, status string) {
|
||||||
|
m.Status = status
|
||||||
|
m.StatusCode = code
|
||||||
|
}
|
||||||
|
|
||||||
|
var EmptyBody io.Reader = &emptyBody{}
|
||||||
|
|
||||||
|
type emptyBody struct{}
|
||||||
|
|
||||||
|
func (l *emptyBody) Read(p []byte) (n int, err error) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) body(r *bufio.Reader) io.Reader {
|
||||||
|
n := m.ContentLength()
|
||||||
|
if n == 0 {
|
||||||
|
return EmptyBody
|
||||||
|
}
|
||||||
|
return &io.LimitedReader{R: r, N: int64(n)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) ReadRequest(r *bufio.Reader) error {
|
||||||
|
return m.read(r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) ReadResponse(r *bufio.Reader) error {
|
||||||
|
return m.read(r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) read(r *bufio.Reader, isRequest bool) error {
|
||||||
|
line, err := readline(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parts := bytes.SplitN(line, []byte(" "), 3)
|
||||||
|
if len(parts) != 3 {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
if isRequest { // GET / HTTP/1.0
|
||||||
|
m.Method, m.Path, m.Version = string(parts[0]), string(parts[1]), string(parts[2])
|
||||||
|
} else { // HTTP/1.0 200 Noice
|
||||||
|
m.StatusCode, err = strconv.Atoi(string(parts[1]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.Version, m.Status = string(parts[0]), string(parts[2])
|
||||||
|
}
|
||||||
|
m.Headers = make(Headers)
|
||||||
|
n := 0
|
||||||
|
for {
|
||||||
|
line, err := readline(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(line) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parts = bytes.SplitN(line, []byte(": "), 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
panic("wtf")
|
||||||
|
}
|
||||||
|
m.Add(string(parts[0]), string(parts[1]))
|
||||||
|
n++
|
||||||
|
if n >= 64 {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readline(r *bufio.Reader) ([]byte, error) {
|
||||||
|
line, p, err := r.ReadLine()
|
||||||
|
if p || len(line) > 8192 {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const crlf = "\r\n"
|
||||||
|
|
||||||
|
func (m *Message) WriteRequest(r *bufio.Writer) error {
|
||||||
|
return m.write(r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) WriteResponse(r *bufio.Writer) error {
|
||||||
|
return m.write(r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) write(w *bufio.Writer, isRequest bool) error {
|
||||||
|
if isRequest { // GET / HTTP/1.0
|
||||||
|
w.WriteString(m.Method)
|
||||||
|
w.WriteByte(' ')
|
||||||
|
w.WriteString(m.Path)
|
||||||
|
w.WriteByte(' ')
|
||||||
|
w.WriteString(m.Version)
|
||||||
|
} else { // HTTP/1.0 200 Noice
|
||||||
|
w.WriteString(m.Version)
|
||||||
|
w.WriteByte(' ')
|
||||||
|
w.WriteString(strconv.Itoa(m.StatusCode))
|
||||||
|
w.WriteByte(' ')
|
||||||
|
w.WriteString(m.Status)
|
||||||
|
}
|
||||||
|
w.WriteString(crlf)
|
||||||
|
for k, vs := range m.Headers {
|
||||||
|
for _, v := range vs {
|
||||||
|
w.WriteString(k)
|
||||||
|
w.WriteString(": ")
|
||||||
|
w.WriteString(v)
|
||||||
|
w.WriteString(crlf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := w.WriteString(crlf)
|
||||||
|
return err
|
||||||
|
}
|
57
message_test.go
Normal file
57
message_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nadeen.lk/boihttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMessage(t *testing.T) {
|
||||||
|
c, err := net.Dial("tcp", httpbin_addr)
|
||||||
|
throw(err)
|
||||||
|
defer c.Close()
|
||||||
|
r, w := bufio.NewReaderSize(c, 1024), bufio.NewWriterSize(c, 1024)
|
||||||
|
err = (&boihttp.Message{
|
||||||
|
Method: "GET",
|
||||||
|
Path: "/anything",
|
||||||
|
Version: boihttp.VersionHTTP1_1,
|
||||||
|
Headers: boihttp.Headers{
|
||||||
|
"Host": []string{"httpbin.org"},
|
||||||
|
"Connection": []string{"close"},
|
||||||
|
"Boi": []string{"whatsupdoc?"},
|
||||||
|
},
|
||||||
|
}).WriteRequest(w)
|
||||||
|
throw(err)
|
||||||
|
throw(w.Flush())
|
||||||
|
var res boihttp.Message
|
||||||
|
throw(res.ReadResponse(r))
|
||||||
|
print(&res, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func print(res *boihttp.Message, r io.Reader) {
|
||||||
|
fmt.Printf("status: %d %s\n", res.StatusCode, res.Status)
|
||||||
|
fmt.Println("headers:")
|
||||||
|
for k, vs := range res.Headers {
|
||||||
|
for _, v := range vs {
|
||||||
|
fmt.Printf("\t\"%s\": \"%s\"\n", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d, err := ioutil.ReadAll(r)
|
||||||
|
throw(err)
|
||||||
|
fmt.Printf("body: \"%s\"\n", string(d))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func throw(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
97
server.go
Normal file
97
server.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler func(req *Message, reqbody io.Reader, res *Message) (resbody io.Reader)
|
||||||
|
|
||||||
|
func Listen(addr string, ctx context.Context, handler Handler) (err error) {
|
||||||
|
var lc net.ListenConfig
|
||||||
|
l, err := lc.Listen(ctx, "tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return Serve(l, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serve(l net.Listener, handler Handler) (err error) {
|
||||||
|
for {
|
||||||
|
c, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go handler2(c, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler2(c net.Conn, handler Handler) {
|
||||||
|
defer c.Close()
|
||||||
|
err := Handle(bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c)), handler)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, z := err.(net.Error); z {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "boihttp error: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Handle(c *bufio.ReadWriter, handler Handler) (err error) {
|
||||||
|
for {
|
||||||
|
more, err := handle(c, handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(c *bufio.ReadWriter, handler Handler) (more bool, err error) {
|
||||||
|
req := new(Message)
|
||||||
|
err = req.ReadRequest(c.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reqbody := req.body(c.Reader)
|
||||||
|
res := new(Message)
|
||||||
|
res.Headers = make(Headers)
|
||||||
|
res.SetKeepAlive(req.KeepAlive() || (req.Version == VersionHTTP1_1 && !req.Has("Connection")))
|
||||||
|
res.Version = req.Version
|
||||||
|
resbody := handler(req, reqbody, res)
|
||||||
|
_, err = io.Copy(io.Discard, reqbody)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
more = res.KeepAlive()
|
||||||
|
err = res.WriteResponse(c.Writer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.Writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.Writer.ReadFrom(resbody)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = c.Writer.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
87
server_test.go
Normal file
87
server_test.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Copyright (c) 2022 Nadeen Udantha <me@nadeen.lk>. All rights reserved.
|
||||||
|
|
||||||
|
package boihttp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nadeen.lk/boihttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer(t *testing.T) {
|
||||||
|
l, err := net.Listen("tcp", "localhost:0")
|
||||||
|
throw(err)
|
||||||
|
go func() {
|
||||||
|
err := boihttp.Serve(l, handler)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
url := fmt.Sprintf("http://localhost:%d/", l.Addr().(*net.TCPAddr).Port)
|
||||||
|
c, err := net.Dial("tcp", l.Addr().String())
|
||||||
|
throw(err)
|
||||||
|
rw := bufio.NewReadWriter(bufio.NewReader(c), bufio.NewWriter(c))
|
||||||
|
hdrs := make(boihttp.Headers)
|
||||||
|
hdrs.Set("Connection", "keep-alive")
|
||||||
|
for z := 4; z >= 0; z-- {
|
||||||
|
if z == 0 {
|
||||||
|
hdrs.Set("Connection", "close")
|
||||||
|
}
|
||||||
|
hdrs2 := hdrs.Clone()
|
||||||
|
x := rand.Int() & 0xffff
|
||||||
|
xx := strconv.Itoa(x)
|
||||||
|
hdrs2.Set("x", xx)
|
||||||
|
hdrs2.SetContentLength(len(xx))
|
||||||
|
res, resbody, err := boihttp.DoConn(rw, "POST", url, hdrs2, bytes.NewBufferString(xx))
|
||||||
|
throw(err)
|
||||||
|
y, err := strconv.Atoi(res.Get("y"))
|
||||||
|
throw(err)
|
||||||
|
if x*x != y {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
yy, err := ioutil.ReadAll(resbody)
|
||||||
|
throw(err)
|
||||||
|
y, err = strconv.Atoi(string(yy))
|
||||||
|
throw(err)
|
||||||
|
if x*x != y {
|
||||||
|
panic("wtf?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(req *boihttp.Message, reqbody io.Reader, res *boihttp.Message) (resbody io.Reader) {
|
||||||
|
x, err := strconv.Atoi(req.Get("x"))
|
||||||
|
throw(err)
|
||||||
|
xx, err := ioutil.ReadAll(reqbody)
|
||||||
|
throw(err)
|
||||||
|
x2, err := strconv.Atoi(string(xx))
|
||||||
|
throw(err)
|
||||||
|
if x != x2 {
|
||||||
|
panic("wtf")
|
||||||
|
}
|
||||||
|
y := strconv.Itoa(x * x)
|
||||||
|
res.Set("y", y)
|
||||||
|
res.SetContentLength(len(y))
|
||||||
|
return bytes.NewBufferString(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer2(t *testing.T) {
|
||||||
|
go boihttp.Listen("localhost:8080", context.Background(), func(req *boihttp.Message, reqbody io.Reader, res *boihttp.Message) (resbody io.Reader) {
|
||||||
|
res.SetStatus(200, "Noice")
|
||||||
|
res.Set("working", "true")
|
||||||
|
res.SetContentLength(3)
|
||||||
|
return bytes.NewBufferString("lol")
|
||||||
|
})
|
||||||
|
res, body, err := boihttp.Get("http://localhost:8080/", nil, context.Background())
|
||||||
|
throw(err)
|
||||||
|
print(res, body)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user