https 、客户端与服务端连接优化

This commit is contained in:
刘河
2019-02-01 02:06:30 +08:00
parent 717028e5f1
commit eccc221e67
32 changed files with 1106 additions and 1140 deletions

View File

@@ -13,6 +13,7 @@ import (
"net/url"
"strconv"
"strings"
"sync"
"time"
)
@@ -52,12 +53,6 @@ func (s *CryptConn) Write(b []byte) (n int, err error) {
//解密读
func (s *CryptConn) Read(b []byte) (n int, err error) {
defer func() {
if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
err = io.EOF
n = 0
}
}()
var lens int
var buf []byte
var rb []byte
@@ -122,14 +117,8 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) {
//snappy压缩读 包含解密
func (s *SnappyConn) Read(b []byte) (n int, err error) {
buf := bufPool.Get().([]byte)
defer func() {
if err == nil && n == len(IO_EOF) && string(b[:n]) == IO_EOF {
err = io.EOF
n = 0
}
bufPool.Put(buf)
}()
buf := BufPool.Get().([]byte)
defer BufPool.Put(buf)
if n, err = s.r.Read(buf); err != nil {
return
}
@@ -152,6 +141,7 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
type Conn struct {
Conn net.Conn
sync.Mutex
}
//new conn
@@ -161,101 +151,7 @@ func NewConn(conn net.Conn) *Conn {
return c
}
//读取指定长度内容
func (s *Conn) ReadLen(cLen int) ([]byte, error) {
if cLen > poolSize {
return nil, errors.New("长度错误" + strconv.Itoa(cLen))
}
var buf []byte
if cLen <= poolSizeSmall {
buf = bufPoolSmall.Get().([]byte)[:cLen]
defer bufPoolSmall.Put(buf)
} else {
buf = bufPoolMax.Get().([]byte)[:cLen]
defer bufPoolMax.Put(buf)
}
if n, err := io.ReadFull(s, buf); err != nil || n != cLen {
return buf, errors.New("读取指定长度错误" + err.Error())
}
return buf, nil
}
//获取长度
func (s *Conn) GetLen() (int, error) {
val, err := s.ReadLen(4)
if err != nil {
return 0, err
}
return GetLenByBytes(val)
}
//写入长度+内容 粘包
func (s *Conn) WriteLen(buf []byte) (int, error) {
var b []byte
var err error
if b, err = GetLenBytes(buf); err != nil {
return 0, err
}
return s.Write(b)
}
//读取flag
func (s *Conn) ReadFlag() (string, error) {
val, err := s.ReadLen(4)
if err != nil {
return "", err
}
return string(val), err
}
//读取host 连接地址 压缩类型
func (s *Conn) GetHostFromConn() (typeStr string, host string, en, de int, crypt, mux bool, err error) {
retry:
lType, err := s.ReadLen(3)
if err != nil {
return
}
if typeStr = string(lType); typeStr == TEST_FLAG {
en, de, crypt, mux = s.GetConnInfoFromConn()
goto retry
} else if typeStr != CONN_TCP && typeStr != CONN_UDP {
err = errors.New("unknown conn type")
return
}
cLen, err := s.GetLen()
if err != nil || cLen > poolSize {
return
}
hostByte, err := s.ReadLen(cLen)
if err != nil {
return
}
host = string(hostByte)
return
}
//写连接类型 和 host地址
func (s *Conn) WriteHost(ltype string, host string) (int, error) {
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, []byte(ltype))
binary.Write(raw, binary.LittleEndian, int32(len([]byte(host))))
binary.Write(raw, binary.LittleEndian, []byte(host))
return s.Write(raw.Bytes())
}
//设置连接为长连接
func (s *Conn) SetAlive() {
conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
}
func (s *Conn) SetReadDeadline(t time.Duration) {
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
//从tcp报文中解析出host连接类型等 TODO 多种情况
//从tcp报文中解析出host连接类型等
func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
var b [32 * 1024]byte
var n int
@@ -285,6 +181,71 @@ func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.
return
}
//读取指定长度内容
func (s *Conn) ReadLen(cLen int) ([]byte, error) {
if cLen > poolSize {
return nil, errors.New("长度错误" + strconv.Itoa(cLen))
}
var buf []byte
if cLen <= poolSizeSmall {
buf = BufPoolSmall.Get().([]byte)[:cLen]
defer BufPoolSmall.Put(buf)
} else {
buf = BufPoolMax.Get().([]byte)[:cLen]
defer BufPoolMax.Put(buf)
}
if n, err := io.ReadFull(s, buf); err != nil || n != cLen {
return buf, errors.New("读取指定长度错误" + err.Error())
}
return buf, nil
}
//read length or id (content length=4)
func (s *Conn) GetLen() (int, error) {
val, err := s.ReadLen(4)
if err != nil {
return 0, err
}
return GetLenByBytes(val)
}
//read flag
func (s *Conn) ReadFlag() (string, error) {
val, err := s.ReadLen(4)
if err != nil {
return "", err
}
return string(val), err
}
//read connect status
func (s *Conn) GetConnStatus() (id int, status bool, err error) {
id, err = s.GetLen()
if err != nil {
return
}
var b []byte
if b, err = s.ReadLen(1); err != nil {
return
} else {
status = GetBoolByStr(string(b[0]))
}
return
}
//设置连接为长连接
func (s *Conn) SetAlive() {
conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
}
//set read dead time
func (s *Conn) SetReadDeadline(t time.Duration) {
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
//单独读(加密|压缩)
func (s *Conn) ReadFrom(b []byte, compress int, crypt bool, rate *Rate) (int, error) {
if COMPRESS_SNAPY_DECODE == compress {
@@ -301,24 +262,112 @@ func (s *Conn) WriteTo(b []byte, compress int, crypt bool, rate *Rate) (n int, e
return NewCryptConn(s.Conn, crypt, rate).Write(b)
}
//写压缩方式,加密
func (s *Conn) WriteConnInfo(en, de int, crypt, mux bool) {
s.Write([]byte(strconv.Itoa(en) + strconv.Itoa(de) + GetStrByBool(crypt) + GetStrByBool(mux)))
}
//获取压缩方式,是否加密
func (s *Conn) GetConnInfoFromConn() (en, de int, crypt, mux bool) {
buf, err := s.ReadLen(4)
if err != nil {
//send msg
func (s *Conn) SendMsg(content []byte, link *Link) (n int, err error) {
/*
The msg info is formed as follows:
+----+--------+
|id | content |
+----+--------+
| 4 | ... |
+----+--------+
*/
s.Lock()
defer s.Unlock()
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, int32(link.Id))
if n, err = s.Write(raw.Bytes()); err != nil {
return
}
en, _ = strconv.Atoi(string(buf[0]))
de, _ = strconv.Atoi(string(buf[1]))
crypt = GetBoolByStr(string(buf[2]))
mux = GetBoolByStr(string(buf[3]))
raw.Reset()
binary.Write(raw, binary.LittleEndian, content)
n, err = s.WriteTo(raw.Bytes(), link.En, link.Crypt, link.Rate)
return
}
//get msg content from conn
func (s *Conn) GetMsgContent(link *Link) (content []byte, err error) {
s.Lock()
defer s.Unlock()
buf := BufPoolCopy.Get().([]byte)
if n, err := s.ReadFrom(buf, link.De, link.Crypt, link.Rate); err == nil && n > 4 {
content = buf[:n]
}
return
}
//send info for link
func (s *Conn) SendLinkInfo(link *Link) (int, error) {
/*
The link info is formed as follows:
+----------+------+----------+------+----------+-----+
| id | len | type | hostlen | host | en | de |crypt |
+----------+------+----------+------+---------+------+
| 4 | 4 | 3 | 4 | host | 1 | 1 | 1 |
+----------+------+----------+------+----+----+------+
*/
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, []byte(NEW_CONN))
binary.Write(raw, binary.LittleEndian, int32(14+len(link.Host)))
binary.Write(raw, binary.LittleEndian, int32(link.Id))
binary.Write(raw, binary.LittleEndian, []byte(link.ConnType))
binary.Write(raw, binary.LittleEndian, int32(len(link.Host)))
binary.Write(raw, binary.LittleEndian, []byte(link.Host))
binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.En)))
binary.Write(raw, binary.LittleEndian, []byte(strconv.Itoa(link.De)))
binary.Write(raw, binary.LittleEndian, []byte(GetStrByBool(link.Crypt)))
s.Lock()
defer s.Unlock()
return s.Write(raw.Bytes())
}
func (s *Conn) GetLinkInfo() (link *Link, err error) {
s.Lock()
defer s.Unlock()
var hostLen, n int
var buf []byte
if n, err = s.GetLen(); err != nil {
return
}
link = new(Link)
if buf, err = s.ReadLen(n); err != nil {
return
}
if link.Id, err = GetLenByBytes(buf[:4]); err != nil {
return
}
link.ConnType = string(buf[4:7])
if hostLen, err = GetLenByBytes(buf[7:11]); err != nil {
return
} else {
link.Host = string(buf[11 : 11+hostLen])
link.En = GetIntNoErrByStr(string(buf[11+hostLen]))
link.De = GetIntNoErrByStr(string(buf[12+hostLen]))
link.Crypt = GetBoolByStr(string(buf[13+hostLen]))
}
return
}
//write connect success
func (s *Conn) WriteSuccess(id int) (int, error) {
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, int32(id))
binary.Write(raw, binary.LittleEndian, []byte("1"))
s.Lock()
defer s.Unlock()
return s.Write(raw.Bytes())
}
//write connect fail
func (s *Conn) WriteFail(id int) (int, error) {
raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, int32(id))
binary.Write(raw, binary.LittleEndian, []byte("0"))
s.Lock()
defer s.Unlock()
return s.Write(raw.Bytes())
}
//close
func (s *Conn) Close() error {
return s.Conn.Close()
@@ -351,29 +400,18 @@ func (s *Conn) WriteClose() (int, error) {
//write main
func (s *Conn) WriteMain() (int, error) {
s.Lock()
defer s.Unlock()
return s.Write([]byte(WORK_MAIN))
}
//write chan
func (s *Conn) WriteChan() (int, error) {
s.Lock()
defer s.Unlock()
return s.Write([]byte(WORK_CHAN))
}
//write test
func (s *Conn) WriteTest() (int, error) {
return s.Write([]byte(TEST_FLAG))
}
//write test
func (s *Conn) WriteSuccess() (int, error) {
return s.Write([]byte(CONN_SUCCESS))
}
//write test
func (s *Conn) WriteFail() (int, error) {
return s.Write([]byte(CONN_ERROR))
}
//获取长度+内容
func GetLenBytes(buf []byte) (b []byte, err error) {
raw := bytes.NewBuffer([]byte{})

View File

@@ -17,61 +17,8 @@ var (
once sync.Once
)
type Flow struct {
ExportFlow int64 //出口流量
InletFlow int64 //入口流量
FlowLimit int64 //流量限制,出口+入口 /M
}
type Client struct {
Cnf *Config
Id int //id
VerifyKey string //验证密钥
Addr string //客户端ip地址
Remark string //备注
Status bool //是否开启
IsConnect bool //是否连接
RateLimit int //速度限制 /kb
Flow *Flow //流量
Rate *Rate //速度控制
}
type Tunnel struct {
Id int //Id
TcpPort int //服务端与客户端通信端口
Mode string //启动方式
Target string //目标
Status bool //是否开启
Client *Client //所属客户端id
Flow *Flow
Config *Config
UseClientCnf bool //是否继承客户端配置
Remark string //备注
}
type Config struct {
U string //socks5验证用户名
P string //socks5验证密码
Compress string //压缩方式
Crypt bool //是否加密
Mux bool //是否加密
CompressEncode int //加密方式
CompressDecode int //解密方式
}
type Host struct {
Host string //启动方式
Target string //目标
HeaderChange string //host修改
HostChange string //host修改
Flow *Flow
Client *Client
Remark string //备注
}
func NewCsv() *Csv {
c := new(Csv)
return c
return new(Csv)
}
type Csv struct {
@@ -108,7 +55,6 @@ func (s *Csv) StoreTasksToCsv() {
task.Config.Compress,
utils.GetStrByBool(task.Status),
GetStrByBool(task.Config.Crypt),
GetStrByBool(task.Config.Mux),
strconv.Itoa(task.Config.CompressEncode),
strconv.Itoa(task.Config.CompressDecode),
strconv.Itoa(task.Id),
@@ -160,17 +106,16 @@ func (s *Csv) LoadTaskFromCsv() {
P: item[4],
Compress: item[5],
Crypt: GetBoolByStr(item[7]),
Mux: GetBoolByStr(item[8]),
CompressEncode: GetIntNoErrByStr(item[9]),
CompressDecode: GetIntNoErrByStr(item[10]),
CompressEncode: GetIntNoErrByStr(item[8]),
CompressDecode: GetIntNoErrByStr(item[9]),
},
Status: utils.GetBoolByStr(item[6]),
Id: GetIntNoErrByStr(item[11]),
UseClientCnf: GetBoolByStr(item[13]),
Remark: item[14],
Id: GetIntNoErrByStr(item[10]),
UseClientCnf: GetBoolByStr(item[12]),
Remark: item[13],
}
post.Flow = new(Flow)
if post.Client, err = s.GetClient(GetIntNoErrByStr(item[12])); err != nil {
if post.Client, err = s.GetClient(GetIntNoErrByStr(item[11])); err != nil {
continue
}
tasks = append(tasks, post)
@@ -284,13 +229,12 @@ func (s *Csv) LoadClientFromCsv() {
VerifyKey: item[1],
Remark: item[2],
Status: GetBoolByStr(item[3]),
RateLimit: GetIntNoErrByStr(item[9]),
RateLimit: GetIntNoErrByStr(item[8]),
Cnf: &Config{
U: item[4],
P: item[5],
Crypt: GetBoolByStr(item[6]),
Mux: GetBoolByStr(item[7]),
Compress: item[8],
Compress: item[7],
},
}
if post.Id > s.ClientIncreaseId {
@@ -301,7 +245,7 @@ func (s *Csv) LoadClientFromCsv() {
post.Rate.Start()
}
post.Flow = new(Flow)
post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[10]))
post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[9]))
clients = append(clients, post)
}
s.Clients = clients
@@ -407,10 +351,14 @@ func (s *Csv) GetClientId() int {
func (s *Csv) UpdateClient(t *Client) error {
s.Lock()
defer s.Unlock()
for k, v := range s.Clients {
for _, v := range s.Clients {
if v.Id == t.Id {
s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
s.Clients = append(s.Clients, t)
v.Cnf = t.Cnf
v.VerifyKey = t.VerifyKey
v.Remark = t.Remark
v.RateLimit = t.RateLimit
v.Flow = t.Flow
v.Rate = t.Rate
s.StoreClientsToCsv()
return nil
}
@@ -458,7 +406,6 @@ func (s *Csv) StoreClientsToCsv() {
client.Cnf.U,
client.Cnf.P,
utils.GetStrByBool(client.Cnf.Crypt),
utils.GetStrByBool(client.Cnf.Mux),
client.Cnf.Compress,
strconv.Itoa(client.RateLimit),
strconv.Itoa(int(client.Flow.FlowLimit)),
@@ -480,15 +427,3 @@ func GetCsvDb() *Csv {
return CsvDb
}
//深拷贝Tunnel
func DeepCopyConfig(c *Config) *Config {
return &Config{
U: c.U,
P: c.P,
Compress: c.Compress,
Crypt: c.Crypt,
Mux: c.Mux,
CompressEncode: c.CompressEncode,
CompressDecode: c.CompressDecode,
}
}

116
utils/link.go Normal file
View File

@@ -0,0 +1,116 @@
package utils
import (
"net"
"sync"
)
type Link struct {
Id int //id
ConnType string //连接类型
Host string //目标
En int //加密
De int //解密
Crypt bool //加密
Conn *Conn
Flow *Flow
UdpListener *net.UDPConn
Rate *Rate
UdpRemoteAddr *net.UDPAddr
}
func NewLink(id int, connType string, host string, en, de int, crypt bool, conn *Conn, flow *Flow, udpListener *net.UDPConn, rate *Rate, UdpRemoteAddr *net.UDPAddr) *Link {
return &Link{
Id: id,
ConnType: connType,
Host: host,
En: en,
De: de,
Crypt: crypt,
Conn: conn,
Flow: flow,
UdpListener: udpListener,
Rate: rate,
UdpRemoteAddr: UdpRemoteAddr,
}
}
type Flow struct {
ExportFlow int64 //出口流量
InletFlow int64 //入口流量
FlowLimit int64 //流量限制,出口+入口 /M
sync.RWMutex
}
func (s *Flow) Add(in, out int) {
s.Lock()
defer s.Unlock()
s.InletFlow += int64(in)
s.ExportFlow += int64(out)
}
type Client struct {
Cnf *Config
Id int //id
VerifyKey string //验证密钥
Addr string //客户端ip地址
Remark string //备注
Status bool //是否开启
IsConnect bool //是否连接
RateLimit int //速度限制 /kb
Flow *Flow //流量
Rate *Rate //速度控制
id int
sync.RWMutex
}
func (s *Client) GetId() int {
s.Lock()
defer s.Unlock()
s.id++
return s.id
}
type Tunnel struct {
Id int //Id
TcpPort int //服务端与客户端通信端口
Mode string //启动方式
Target string //目标
Status bool //是否开启
Client *Client //所属客户端id
Flow *Flow
Config *Config
UseClientCnf bool //是否继承客户端配置
Remark string //备注
}
type Config struct {
U string //socks5验证用户名
P string //socks5验证密码
Compress string //压缩方式
Crypt bool //是否加密
CompressEncode int //加密方式
CompressDecode int //解密方式
}
type Host struct {
Host string //启动方式
Target string //目标
HeaderChange string //host修改
HostChange string //host修改
Flow *Flow
Client *Client
Remark string //备注
}
//深拷贝Config
func DeepCopyConfig(c *Config) *Config {
return &Config{
U: c.U,
P: c.P,
Compress: c.Compress,
Crypt: c.Crypt,
CompressEncode: c.CompressEncode,
CompressDecode: c.CompressDecode,
}
}

View File

@@ -1,13 +1,15 @@
package utils
import "sync"
import (
"sync"
)
const poolSize = 64 * 1024
const poolSizeSmall = 100
const poolSizeUdp = 1472
const poolSizeCopy = 32 * 1024
var bufPool = sync.Pool{
var BufPool = sync.Pool{
New: func() interface{} {
return make([]byte, poolSize)
},
@@ -18,18 +20,24 @@ var BufPoolUdp = sync.Pool{
return make([]byte, poolSizeUdp)
},
}
var bufPoolMax = sync.Pool{
var BufPoolMax = sync.Pool{
New: func() interface{} {
return make([]byte, poolSize)
},
}
var bufPoolSmall = sync.Pool{
var BufPoolSmall = sync.Pool{
New: func() interface{} {
return make([]byte, poolSizeSmall)
},
}
var bufPoolCopy = sync.Pool{
var BufPoolCopy = sync.Pool{
New: func() interface{} {
return make([]byte, poolSizeCopy)
},
}
func PutBufPoolCopy(buf []byte) {
if cap(buf) == poolSizeCopy {
BufPoolCopy.Put(buf[:poolSizeCopy])
}
}

View File

@@ -2,7 +2,6 @@ package utils
import (
"encoding/base64"
"io"
"io/ioutil"
"log"
"net"
@@ -11,8 +10,6 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"time"
)
const (
@@ -26,9 +23,8 @@ const (
RES_SIGN = "sign"
RES_MSG = "msg0"
RES_CLOSE = "clse"
NEW_CONN = "conn" //新连接标志
CONN_SUCCESS = "sucs"
CONN_ERROR = "fail"
TEST_FLAG = "tst"
CONN_TCP = "tcp"
CONN_UDP = "udp"
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
@@ -42,32 +38,6 @@ WWW-Authenticate: Basic realm="easyProxy"
`
)
//copy
func Relay(in, out net.Conn, compressType int, crypt, mux bool, rate *Rate) (n int64, err error) {
switch compressType {
case COMPRESS_SNAPY_ENCODE:
n, err = copyBuffer(NewSnappyConn(in, crypt, rate), out)
out.Close()
NewSnappyConn(in, crypt, rate).Write([]byte(IO_EOF))
case COMPRESS_SNAPY_DECODE:
n, err = copyBuffer(in, NewSnappyConn(out, crypt, rate))
in.Close()
if !mux {
out.Close()
}
case COMPRESS_NONE_ENCODE:
n, err = copyBuffer(NewCryptConn(in, crypt, rate), out)
out.Close()
NewCryptConn(in, crypt, rate).Write([]byte(IO_EOF))
case COMPRESS_NONE_DECODE:
n, err = copyBuffer(in, NewCryptConn(out, crypt, rate))
in.Close()
if !mux {
out.Close()
}
}
return
}
//判断压缩方式
func GetCompressType(compress string) (int, int) {
@@ -152,71 +122,11 @@ func GetIntNoErrByStr(str string) int {
return i
}
// io.copy的优化版读取buffer长度原为32*1024与snappy不同导致读取出的内容存在差异不利于解密
//内存优化 用到pool快速回收
func copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
for {
//放在里面是为了加快回收和重利用
buf := bufPoolCopy.Get().([]byte)
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
bufPoolCopy.Put(buf)
if nw > 0 {
written += int64(nw)
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
} else {
bufPoolCopy.Put(buf)
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}
//连接重置 清空缓存区
func FlushConn(c net.Conn) {
c.SetReadDeadline(time.Now().Add(time.Second * 3))
buf := bufPool.Get().([]byte)
defer bufPool.Put(buf)
for {
if _, err := c.Read(buf); err != nil {
break
}
}
c.SetReadDeadline(time.Time{})
}
//简单的一个校验值
func Getverifyval(vkey string) string {
return Md5(vkey)
}
//wait replay group
//conn1 网桥 conn2
func ReplayWaitGroup(conn1 net.Conn, conn2 net.Conn, compressEncode, compressDecode int, crypt, mux bool, rate *Rate) (out int64, in int64) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
in, _ = Relay(conn1, conn2, compressEncode, crypt, mux, rate)
wg.Done()
}()
out, _ = Relay(conn2, conn1, compressDecode, crypt, mux, rate)
wg.Wait()
return
}
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) {
if host != "" {
@@ -236,8 +146,8 @@ func ChangeHostAndHeader(r *http.Request, host string, header string, addr strin
r.Header.Set("X-Real-IP", addr)
}
func ReadAllFromFile(filePth string) ([]byte, error) {
f, err := os.Open(filePth)
func ReadAllFromFile(filePath string) ([]byte, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}