101 lines
2.1 KiB
Go
101 lines
2.1 KiB
Go
|
// 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)
|
||
|
}
|