Compare commits

...

7 Commits

Author SHA1 Message Date
sago35 8fc9dd0670 net/http: add length check 2022-12-25 21:54:24 +09:00
sago35 479beeeece WIP : chunked 2022-12-25 19:07:33 +09:00
sago35 2526660a96 WIP: no data 2022-12-25 19:07:17 +09:00
sago35 4213d6f582 net/http: refactor net/http/tinygo.go 2022-12-25 11:48:04 +09:00
sago35 81cf507e4e net/http: add support for RoundTripper 2022-12-25 11:41:36 +09:00
sago35 86cc0e31f1 rtl8720dn: add handling when connect fails 2022-12-25 11:30:49 +09:00
sago35 e3e95cacfe net/http: add PostForm() 2022-12-25 11:12:41 +09:00
3 changed files with 189 additions and 126 deletions
+40
View File
@@ -2,6 +2,8 @@ package http
import (
"io"
"net/url"
"strings"
"time"
)
@@ -211,3 +213,41 @@ func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response,
req.Header.Set("Content-Type", contentType)
return c.Do(req)
}
// PostForm issues a POST to the specified URL, with data's keys and
// values URL-encoded as the request body.
//
// The Content-Type header is set to application/x-www-form-urlencoded.
// To set other headers, use NewRequest and DefaultClient.Do.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
//
// PostForm is a wrapper around DefaultClient.PostForm.
//
// See the Client.Do method documentation for details on how redirects
// are handled.
//
// To make a request with a specified context.Context, use NewRequestWithContext
// and DefaultClient.Do.
func PostForm(url string, data url.Values) (resp *Response, err error) {
return DefaultClient.PostForm(url, data)
}
// PostForm issues a POST to the specified URL,
// with data's keys and values URL-encoded as the request body.
//
// The Content-Type header is set to application/x-www-form-urlencoded.
// To set other headers, use NewRequest and Client.Do.
//
// When err is nil, resp always contains a non-nil resp.Body.
// Caller should close resp.Body when done reading from it.
//
// See the Client.Do method documentation for details on how redirects
// are handled.
//
// To make a request with a specified context.Context, use NewRequestWithContext
// and Client.Do.
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
+140 -123
View File
@@ -5,6 +5,7 @@ import (
"bytes"
"fmt"
"io"
"net/textproto"
"strconv"
"strings"
"time"
@@ -25,17 +26,43 @@ func (c *Client) Do(req *Request) (*Response, error) {
req.AddCookie(cookie)
}
}
transport := c.Transport
if transport == nil {
transport = DefaultTransport
}
res, err := transport.RoundTrip(req)
if c.Jar != nil {
if rc := res.Cookies(); len(rc) > 0 {
c.Jar.SetCookies(req.URL, rc)
}
}
return res, err
}
type Transport struct {
}
var DefaultTransport RoundTripper
func init() {
DefaultTransport = &Transport{}
}
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
switch req.URL.Scheme {
case "http":
return c.doHTTP(req)
return t.doHTTP(req)
case "https":
return c.doHTTPS(req)
return t.doHTTPS(req)
default:
return nil, fmt.Errorf("invalid schemer : %s", req.URL.Scheme)
}
}
func (c *Client) doHTTP(req *Request) (*Response, error) {
func (t *Transport) doHTTP(req *Request) (*Response, error) {
// make TCP connection
ip := net.ParseIP(req.URL.Hostname())
port := 80
@@ -106,10 +133,10 @@ func (c *Client) doHTTP(req *Request) (*Response, error) {
}
return c.doResp(conn, req)
return t.doResp(conn, req)
}
func (c *Client) doHTTPS(req *Request) (*Response, error) {
func (t *Transport) doHTTPS(req *Request) (*Response, error) {
conn, err := tls.Dial("tcp", req.URL.Host, nil)
retry := 0
for ; err != nil; conn, err = tls.Dial("tcp", req.URL.Host, nil) {
@@ -167,141 +194,131 @@ func (c *Client) doHTTPS(req *Request) (*Response, error) {
}
return c.doResp(conn, req)
return t.doResp(conn, req)
}
func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
func (t *Transport) doResp(conn net.Conn, req *Request) (*Response, error) {
resp := &Response{
Header: map[string][]string{},
}
// Header
var scanner *bufio.Scanner
cont := true
ofs := 0
remain := int64(0)
for cont {
for n, err := conn.Read(buf[ofs:]); n > 0; n, err = conn.Read(buf[ofs:]) {
if err != nil {
println("Read error: " + err.Error())
} else {
// Take care of the case where "\r\n\r\n" is on the boundary of a buffer
start := ofs
if start > 3 {
start -= 3
}
idx := bytes.Index(buf[start:ofs+n], []byte("\r\n\r\n"))
if idx == -1 {
ofs += n
continue
}
idx += start + 4
br := bufio.NewReader(conn)
tp := textproto.NewReader(br)
scanner = bufio.NewScanner(bytes.NewReader(buf[0 : ofs+n]))
if resp.Status == "" && scanner.Scan() {
status := strings.SplitN(scanner.Text(), " ", 2)
if len(status) != 2 {
conn.Close()
return nil, fmt.Errorf("invalid status : %q", scanner.Text())
}
resp.Proto = status[0]
fmt.Sscanf(status[0], "HTTP/%d.%d", &resp.ProtoMajor, &resp.ProtoMinor)
resp.Status = status[1]
fmt.Sscanf(status[1], "%d", &resp.StatusCode)
}
for scanner.Scan() {
text := scanner.Text()
if text == "" {
// end of header
if idx < n+ofs {
ofs = ofs + n - idx
for i := 0; i < ofs; i++ {
buf[i] = buf[i+idx]
}
} else {
ofs = 0
}
break
} else {
header := strings.SplitN(text, ": ", 2)
if len(header) != 2 {
conn.Close()
return nil, fmt.Errorf("invalid header : %q", text)
}
if resp.Header.Get(header[0]) == "" {
resp.Header.Set(header[0], header[1])
} else {
resp.Header.Add(header[0], header[1])
}
if strings.ToLower(header[0]) == "content-length" {
resp.ContentLength, err = strconv.ParseInt(header[1], 10, 64)
if err != nil {
conn.Close()
return nil, err
}
remain = resp.ContentLength
}
}
}
cont = false
break
}
}
}
// Body
remain -= int64(ofs)
if remain <= 0 {
resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs]))
if c.Jar != nil {
if rc := resp.Cookies(); len(rc) > 0 {
c.Jar.SetCookies(req.URL, rc)
}
}
return resp, conn.Close()
}
cont = true
lastRequestTime := time.Now()
for cont {
for {
end := ofs + 0x400
if len(buf) < end {
return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end)
}
n, err := conn.Read(buf[ofs : ofs+0x400])
if err != nil {
return nil, err
}
if n == 0 {
for {
line, err := tp.ReadLine()
if err != nil {
if err == io.ErrNoProgress {
// default: no timeout
continue
}
conn.Close()
return nil, err
}
status := strings.SplitN(line, " ", 2)
if len(status) != 2 {
conn.Close()
return nil, fmt.Errorf("invalid status : %q", line)
}
resp.Proto = status[0]
fmt.Sscanf(status[0], "HTTP/%d.%d", &resp.ProtoMajor, &resp.ProtoMinor)
resp.Status = status[1]
fmt.Sscanf(status[1], "%d", &resp.StatusCode)
break
}
m, err := tp.ReadMIMEHeader()
if err != nil {
conn.Close()
return nil, err
}
for k, v := range m {
//fmt.Printf("%s: %s\n", k, v)
if strings.ToLower(k) == "content-length" {
resp.ContentLength, err = strconv.ParseInt(v[0], 10, 64)
if err != nil {
conn.Close()
return nil, err
} else {
ofs += n
remain -= int64(n)
if remain <= 0 {
resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs]))
cont = false
break
}
if time.Now().Sub(lastRequestTime).Milliseconds() >= 1000 {
conn.Close()
return nil, fmt.Errorf("time out")
}
}
}
if resp.Header.Get(k) == "" {
resp.Header.Set(k, v[0])
v = v[1:]
}
for _, vv := range v {
resp.Header.Add(k, vv)
}
}
if c.Jar != nil {
if rc := resp.Cookies(); len(rc) > 0 {
c.Jar.SetCookies(req.URL, rc)
if resp.Header.Get("Transfer-Encoding") == "chunked" {
// chunked
cur := 0
end := 0
for {
length := 0
if len(buf) < cur+6 {
// This is not a very accurate check, but in many cases it should be fine.
return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", cur+6)
}
for i := 0; ; i++ {
buf[cur+i], err = br.ReadByte()
if err != nil {
conn.Close()
return nil, err
}
length = i + 1
if i > 1 && buf[cur+i-1] == '\r' && buf[cur+i] == '\n' {
break
}
}
//fmt.Printf("cur:%d length:%d\n", cur, length)
size, err := strconv.ParseInt(string(buf[cur:cur+length-2]), 16, 64)
if err != nil {
conn.Close()
return nil, err
}
//cur += length
//fmt.Printf("cur:%d length:%d size:%d\n", cur, length, size)
end = cur + int(size) + 2 // size + 2 (\r\n)
if len(buf) < end {
return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end)
}
for i := 0; i < int(size)+2; i++ {
buf[cur+i], err = br.ReadByte()
if err != nil {
conn.Close()
return nil, err
}
}
cur += int(size)
if size == 0 {
end = end - 2
break
}
}
//fmt.Printf("%q\n", buf[:end])
resp.Body = io.NopCloser(bytes.NewReader(buf[:end]))
} else {
end := int(resp.ContentLength)
if len(buf) < end {
return nil, fmt.Errorf("slice out of range : use http.SetBuf() to change the allocation to %d bytes or more", end)
}
for i := 0; i < end; i++ {
buf[i], err = br.ReadByte()
if err != nil {
conn.Close()
return nil, err
}
}
resp.Body = io.NopCloser(bytes.NewReader(buf[:end]))
}
return resp, conn.Close()
+9 -3
View File
@@ -71,10 +71,13 @@ func (d *Driver) ConnectTCPSocket(addr, port string) error {
name[6] = byte(ipaddr[2])
name[7] = byte(ipaddr[3])
_, err = d.Rpc_lwip_connect(socket, name, uint32(len(name)))
result, err := d.Rpc_lwip_connect(socket, name, uint32(len(name)))
if err != nil {
return err
}
if result == -1 {
return fmt.Errorf("failed to connect to %d.%d.%d.%d port %s", addr[0], addr[1], addr[2], addr[3], port)
}
readset := []byte{}
writeset := []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
@@ -341,8 +344,11 @@ func (d *Driver) ReadSocket(b []byte) (n int, err error) {
if err != nil {
return 0, err
}
if nn < 0 {
return 0, fmt.Errorf("error %d", n)
if nn == -76 {
// no data
return 0, nil
} else if nn < 0 {
return 0, fmt.Errorf("error %d", nn)
} else if nn == 0 || nn == -30848 {
return 0, d.DisconnectSocket()
}