wip
This commit is contained in:
104
httpconn/client.go
Normal file
104
httpconn/client.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package httpconn
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUnknownScheme = errors.New("uknown scheme")
|
||||
)
|
||||
|
||||
type Dialer struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewDialer() *Dialer {
|
||||
return &Dialer{timeout: 10 * time.Second}
|
||||
}
|
||||
|
||||
func (d *Dialer) SetTimeout(timeout time.Duration) {
|
||||
d.timeout = timeout
|
||||
}
|
||||
|
||||
func (d *Dialer) Dial(rawURL string) (net.Conn, error) {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
return d.DialHTTPS(u.Host+":443", u.Path)
|
||||
case "http":
|
||||
return d.DialHTTP(u.Host, u.Path)
|
||||
default:
|
||||
return nil, ErrUnknownScheme
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dialer) DialHTTPS(host, path string) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), d.timeout)
|
||||
dd := tls.Dialer{}
|
||||
conn, err := dd.DialContext(ctx, "tcp", host)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.finishDialing(conn, host, path)
|
||||
|
||||
}
|
||||
|
||||
func (d *Dialer) DialHTTP(host, path string) (net.Conn, error) {
|
||||
conn, err := net.DialTimeout("tcp", host, d.timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.finishDialing(conn, host, path)
|
||||
}
|
||||
|
||||
func (d *Dialer) finishDialing(conn net.Conn, host, path string) (net.Conn, error) {
|
||||
conn.SetDeadline(time.Now().Add(d.timeout))
|
||||
|
||||
if _, err := io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.WriteString(conn, "Host: "+host+"\n\n"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Require successful HTTP response before using the conn.
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Status != "200 OK" {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.SetDeadline(time.Time{})
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func Dial(rawURL string) (net.Conn, error) {
|
||||
return NewDialer().Dial(rawURL)
|
||||
}
|
||||
|
||||
func DialHTTPS(host, path string) (net.Conn, error) {
|
||||
return NewDialer().DialHTTPS(host, path)
|
||||
}
|
||||
|
||||
func DialHTTP(host, path string) (net.Conn, error) {
|
||||
return NewDialer().DialHTTP(host, path)
|
||||
}
|
||||
Reference in New Issue
Block a user