mirror of
				https://github.com/AlexxIT/go2rtc.git
				synced 2025-10-31 03:36:30 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			247 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			247 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 | 
