Refactoring for HomeKit client
This commit is contained in:
+246
@@ -0,0 +1,246 @@
|
||||
package hap
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
MimeTLV8 = "application/pairing+tlv8"
|
||||
MimeJSON = "application/hap+json"
|
||||
|
||||
UriPairSetup = "/pair-setup"
|
||||
UriPairVerify = "/pair-verify"
|
||||
UriPairings = "/pairings"
|
||||
UriAccessories = "/accessories"
|
||||
UriCharacteristics = "/characteristics"
|
||||
UriResource = "/resource"
|
||||
)
|
||||
|
||||
func (c *Conn) Write(p []byte) (r io.Reader, err error) {
|
||||
if c.secure == nil {
|
||||
if _, err = c.conn.Write(p); err == nil {
|
||||
r = bufio.NewReader(c.conn)
|
||||
}
|
||||
} else {
|
||||
if _, err = c.secure.Write(p); err == nil {
|
||||
r = <-c.httpResponse
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) Do(req *http.Request) (*http.Response, error) {
|
||||
if c.secure == nil {
|
||||
// insecure requests
|
||||
if err := req.Write(c.conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return http.ReadResponse(bufio.NewReader(c.conn), req)
|
||||
}
|
||||
|
||||
// secure support write interface to connection
|
||||
if err := req.Write(c.secure); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get decrypted buffer from connection
|
||||
buf := <-c.httpResponse
|
||||
|
||||
return http.ReadResponse(buf, req)
|
||||
}
|
||||
|
||||
func (c *Conn) Get(uri string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(
|
||||
"GET", "http://"+c.DeviceAddress+uri, nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
func (c *Conn) Post(uri string, data []byte) (*http.Response, error) {
|
||||
req, err := http.NewRequest(
|
||||
"POST", "http://"+c.DeviceAddress+uri,
|
||||
bytes.NewReader(data),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch uri {
|
||||
case "/pair-verify", "/pairings":
|
||||
req.Header.Set("Content-Type", MimeTLV8)
|
||||
case UriResource:
|
||||
req.Header.Set("Content-Type", MimeJSON)
|
||||
}
|
||||
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
func (c *Conn) Put(uri string, data []byte) (*http.Response, error) {
|
||||
req, err := http.NewRequest(
|
||||
"PUT", "http://"+c.DeviceAddress+uri,
|
||||
bytes.NewReader(data),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch uri {
|
||||
case UriCharacteristics:
|
||||
req.Header.Set("Content-Type", MimeJSON)
|
||||
}
|
||||
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
func (c *Conn) Handle() (err error) {
|
||||
defer func() {
|
||||
if c.conn == nil {
|
||||
err = nil
|
||||
}
|
||||
}()
|
||||
|
||||
b := make([]byte, 512000)
|
||||
for {
|
||||
var total, content int
|
||||
header := -1
|
||||
|
||||
for {
|
||||
var n1 int
|
||||
n1, err = c.secure.Read(b[total:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n1 == 0 {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
total += n1
|
||||
|
||||
// TODO: rewrite
|
||||
if header == -1 {
|
||||
// step 1. wait whole header
|
||||
header = bytes.Index(b[:total], []byte("\r\n\r\n"))
|
||||
if header < 0 {
|
||||
continue
|
||||
}
|
||||
header += 4
|
||||
|
||||
// step 2. check content-length
|
||||
i1 := bytes.Index(b[:total], []byte("Content-Length: "))
|
||||
if i1 < 0 {
|
||||
break
|
||||
}
|
||||
i1 += 16
|
||||
i2 := bytes.IndexByte(b[i1:total], '\r')
|
||||
content, err = strconv.Atoi(string(b[i1 : i1+i2]))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if total >= header+content {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// copy slice to buffer
|
||||
buf := bytes.NewBuffer(make([]byte, 0, total))
|
||||
buf.Write(b[:total])
|
||||
r := bufio.NewReader(buf)
|
||||
|
||||
// EVENT/1.0 200 OK
|
||||
if b[0] == 'E' {
|
||||
if c.OnEvent == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
tp := textproto.NewReader(r)
|
||||
|
||||
var s string
|
||||
if s, err = tp.ReadLine(); err != nil {
|
||||
return err
|
||||
}
|
||||
if s != "EVENT/1.0 200 OK" {
|
||||
return errors.New("wrong response")
|
||||
}
|
||||
|
||||
var mimeHeader textproto.MIMEHeader
|
||||
if mimeHeader, err = tp.ReadMIMEHeader(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cl int
|
||||
if cl, err = strconv.Atoi(
|
||||
mimeHeader.Get("Content-Length"),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res := http.Response{
|
||||
StatusCode: 200,
|
||||
Proto: "EVENT/1.0",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 0,
|
||||
Header: http.Header(mimeHeader),
|
||||
ContentLength: int64(cl),
|
||||
Body: io.NopCloser(r),
|
||||
}
|
||||
c.OnEvent(&res)
|
||||
continue
|
||||
}
|
||||
|
||||
//if bytes.Index(b, []byte("image/jpeg")) > 0 {
|
||||
// if total, err = c.secure.Read(b); err != nil {
|
||||
// return
|
||||
// }
|
||||
// buf.Write(b[:total])
|
||||
//}
|
||||
|
||||
c.httpResponse <- r
|
||||
}
|
||||
}
|
||||
|
||||
func WriteStatusCode(w io.Writer, statusCode int) (err error) {
|
||||
body := []byte(fmt.Sprintf(
|
||||
"HTTP/1.1 %d %s\n\n", statusCode, http.StatusText(statusCode),
|
||||
))
|
||||
//print("<<<", string(body), "<<<\n")
|
||||
_, err = w.Write(body)
|
||||
return
|
||||
}
|
||||
|
||||
func WriteResponse(
|
||||
w io.Writer, statusCode int, contentType string, body []byte,
|
||||
) (err error) {
|
||||
header := fmt.Sprintf(
|
||||
"HTTP/1.1 %d %s\nContent-Type: %s\nContent-Length: %d\n\n",
|
||||
statusCode, http.StatusText(statusCode), contentType, len(body),
|
||||
)
|
||||
body = append([]byte(header), body...)
|
||||
//print("<<<", string(body), "<<<\n")
|
||||
_, err = w.Write(body)
|
||||
return
|
||||
}
|
||||
|
||||
func WriteChunked(w io.Writer, contentType string, body []byte) (err error) {
|
||||
header := fmt.Sprintf(
|
||||
"HTTP/1.1 200 OK\nContent-Type: %s\nTransfer-Encoding: chunked\n\n%x\n",
|
||||
contentType, len(body),
|
||||
)
|
||||
body = append([]byte(header), body...)
|
||||
body = append(body, "\n0\n\n"...)
|
||||
//print("<<<", string(body), "<<<\n")
|
||||
_, err = w.Write(body)
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user