From 9884015d6acee46fd049e4e6e6f7ec016da28914 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Tue, 18 Jul 2017 18:19:36 +0800 Subject: [PATCH] add tcp node client server --- chain.go | 32 +++++++++ options.go | 19 ++--- server.go | 3 + socks/selector.go | 167 +++++++++++++++++++++++++++++++++++++++++++ ss.go | 7 +- ssocks/ssocks.go | 165 ++++++++++++++++++++++++++++++++++++++++++ tcp/client.go | 177 +++++++++++++++++++++++++++++++++++++++++++++- tcp/node.go | 13 ++-- tcp/options.go | 54 +++++++++++--- tcp/server.go | 131 +++++++++++++++++++++++++++++++++- 10 files changed, 732 insertions(+), 36 deletions(-) create mode 100644 socks/selector.go create mode 100644 ssocks/ssocks.go diff --git a/chain.go b/chain.go index f2a5eb0..f096866 100644 --- a/chain.go +++ b/chain.go @@ -560,3 +560,35 @@ func (c *ProxyChain) getQuicConn(header http.Header) (net.Conn, error) { type Chain struct { nodes []Node } + +func (c *Chain) Dial(addr string) (net.Conn, error) { + if len(c.nodes) == 0 { + return net.Dial("tcp", addr) + } + + nodes := c.nodes + conn, err := nodes[0].Client().Connect() + if err != nil { + return nil, err + } + + for i, node := range nodes { + if i == len(nodes)-1 { + break + } + + cn, err := node.Client().Dial(conn, nodes[i+1].Options().BaseOptions().Addr) + if err != nil { + conn.Close() + return nil, err + } + conn = cn + } + + cn, err := nodes[len(nodes)-1].Client().Dial(conn, addr) + if err != nil { + conn.Close() + return nil, err + } + return cn, nil +} diff --git a/options.go b/options.go index 55bbe16..e7fa3a7 100644 --- a/options.go +++ b/options.go @@ -7,41 +7,32 @@ import ( // Options holds options of node type Options interface { - Get(opt string) (v interface{}) - Set(opt string, v interface{}) + BaseOptions() *BaseOptions } type Option func(Options) -type DefaultOptions struct { +type BaseOptions struct { Addr string `opt:"addr"` // [host]:port Protocol string `opt:"protocol"` // protocol: http/socks5/ss Transport string `opt:"transport"` // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp } -func (o *DefaultOptions) Get(opt string) interface{} { - return GetOption(o, opt) -} - -func (o *DefaultOptions) Set(opt string, v interface{}) { - SetOption(o, opt, v) -} - func AddrOption(a string) Option { return func(opts Options) { - SetOption(opts, "addr", a) + opts.BaseOptions().Addr = a } } func ProtocolOption(p string) Option { return func(opts Options) { - SetOption(opts, "protocol", p) + opts.BaseOptions().Protocol = p } } func TransportOption(t string) Option { return func(opts Options) { - SetOption(opts, "transport", t) + opts.BaseOptions().Transport = t } } diff --git a/server.go b/server.go index dfd1aac..853ac34 100644 --- a/server.go +++ b/server.go @@ -298,5 +298,8 @@ func (_ *ProxyServer) transport(conn1, conn2 net.Conn) (err error) { // Server represents a node server type Server interface { + Chain() *Chain + SetChain(chain *Chain) + Options() Options Run() error } diff --git a/socks/selector.go b/socks/selector.go new file mode 100644 index 0000000..fd530e3 --- /dev/null +++ b/socks/selector.go @@ -0,0 +1,167 @@ +package socks + +import ( + "crypto/tls" + "net" + "net/url" + + "github.com/ginuerzh/gosocks5" + "github.com/golang/glog" +) + +const ( + MethodTLS uint8 = 0x80 // extended method for tls + MethodTLSAuth uint8 = 0x82 // extended method for tls+auth +) + +const ( + CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp +) + +type ClientSelector struct { + methods []uint8 + User *url.Userinfo + TLSConfig *tls.Config +} + +func (selector *ClientSelector) Methods() []uint8 { + return selector.methods +} + +func (selector *ClientSelector) AddMethod(methods ...uint8) { + selector.methods = append(selector.methods, methods...) +} + +func (selector *ClientSelector) Select(methods ...uint8) (method uint8) { + return +} + +func (selector *ClientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { + switch method { + case MethodTLS: + conn = tls.Client(conn, selector.TLSConfig) + + case gosocks5.MethodUserPass, MethodTLSAuth: + if method == MethodTLSAuth { + conn = tls.Client(conn, selector.TLSConfig) + } + + var username, password string + if selector.User != nil { + username = selector.User.Username() + password, _ = selector.User.Password() + } + + req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password) + if err := req.Write(conn); err != nil { + glog.Infoln("socks5 auth:", err) + return nil, err + } + glog.Infoln(req) + + resp, err := gosocks5.ReadUserPassResponse(conn) + if err != nil { + glog.Infoln("socks5 auth:", err) + return nil, err + } + glog.Infoln(resp) + + if resp.Status != gosocks5.Succeeded { + return nil, gosocks5.ErrAuthFailure + } + case gosocks5.MethodNoAcceptable: + return nil, gosocks5.ErrBadMethod + } + + return conn, nil +} + +type ServerSelector struct { + methods []uint8 + users []*url.Userinfo + tlsConfig *tls.Config +} + +func (selector *ServerSelector) Methods() []uint8 { + return selector.methods +} + +func (selector *ServerSelector) Select(methods ...uint8) (method uint8) { + glog.Infof("%d %d %v", gosocks5.Ver5, len(methods), methods) + + method = gosocks5.MethodNoAuth + for _, m := range methods { + if m == MethodTLS { + method = m + break + } + } + + // when user/pass is set, auth is mandatory + if selector.users != nil { + if method == gosocks5.MethodNoAuth { + method = gosocks5.MethodUserPass + } + if method == MethodTLS { + method = MethodTLSAuth + } + } + + return +} + +func (selector *ServerSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) { + glog.Infof("%d %d", gosocks5.Ver5, method) + + switch method { + case MethodTLS: + conn = tls.Server(conn, selector.tlsConfig) + + case gosocks5.MethodUserPass, MethodTLSAuth: + if method == MethodTLSAuth { + conn = tls.Server(conn, selector.tlsConfig) + } + + req, err := gosocks5.ReadUserPassRequest(conn) + if err != nil { + glog.Infoln("[socks5-auth]", err) + return nil, err + } + glog.Infoln("[socks5]", req.String()) + + valid := false + for _, user := range selector.users { + username := user.Username() + password, _ := user.Password() + if (req.Username == username && req.Password == password) || + (req.Username == username && password == "") || + (username == "" && req.Password == password) { + valid = true + break + } + } + if len(selector.users) > 0 && !valid { + resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) + if err := resp.Write(conn); err != nil { + glog.Infoln("[socks5-auth]", err) + return nil, err + } + glog.Infoln("[socks5]", resp) + glog.Infoln("[socks5-auth] proxy authentication required") + + return nil, gosocks5.ErrAuthFailure + } + + resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) + if err := resp.Write(conn); err != nil { + glog.Infoln("[socks5-auth]", err) + return nil, err + } + glog.Infoln(resp) + + case gosocks5.MethodNoAcceptable: + return nil, gosocks5.ErrBadMethod + } + + return conn, nil +} diff --git a/ss.go b/ss.go index cf5eb47..58841d7 100644 --- a/ss.go +++ b/ss.go @@ -5,13 +5,14 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/ginuerzh/gosocks5" - "github.com/golang/glog" - ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" "io" "net" "strconv" "time" + + "github.com/ginuerzh/gosocks5" + "github.com/golang/glog" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ) const ( diff --git a/ssocks/ssocks.go b/ssocks/ssocks.go new file mode 100644 index 0000000..afde874 --- /dev/null +++ b/ssocks/ssocks.go @@ -0,0 +1,165 @@ +package ssocks + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "net/url" + "strconv" + "time" + + "github.com/go-log/log" + + "github.com/ginuerzh/gost" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" +) + +// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write, +// we wrap around it to make io.Copy happy +type shadowConn struct { + conn net.Conn +} + +func NewConn(conn net.Conn) net.Conn { + return &shadowConn{conn: conn} +} + +func (c *shadowConn) Read(b []byte) (n int, err error) { + return c.conn.Read(b) +} + +func (c *shadowConn) Write(b []byte) (n int, err error) { + n = len(b) // force byte length consistent + _, err = c.conn.Write(b) + return +} + +func (c *shadowConn) Close() error { + return c.conn.Close() +} + +func (c *shadowConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *shadowConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *shadowConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *shadowConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *shadowConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +const ( + idType = 0 // address type index + idIP0 = 1 // ip addres start index + idDmLen = 1 // domain address length index + idDm0 = 2 // domain address start index + + typeIPv4 = 1 // type is ipv4 address + typeDm = 3 // type is domain address + typeIPv6 = 4 // type is ipv6 address + + lenIPv4 = net.IPv4len + 2 // ipv4 + 2port + lenIPv6 = net.IPv6len + 2 // ipv6 + 2port + lenDmBase = 2 // 1addrLen + 2port, plus addrLen + lenHmacSha1 = 10 +) + +type ShadowServer struct { + conn net.Conn + base gost.Server +} + +func NewServer(conn net.Conn, cipher *url.Userinfo, base gost.Server) (*ShadowServer, error) { + method := cipher.Username() + password, _ := cipher.Password() + cp, err := ss.NewCipher(method, password) + if err != nil { + return nil, err + } + return &ShadowServer{conn: ss.NewConn(conn, cp), base: base}, nil +} + +func (s *ShadowServer) Serve() error { + log.Logf("[ss] %s - %s", s.conn.RemoteAddr(), s.conn.LocalAddr()) + + addr, err := s.getRequest() + if err != nil { + log.Logf("[ss] %s - %s : %s", s.conn.RemoteAddr(), s.conn.LocalAddr(), err) + return err + } + log.Logf("[ss] %s -> %s", s.conn.RemoteAddr(), addr) + + cc, err := s.base.Chain().Dial(addr) + if err != nil { + log.Logf("[ss] %s -> %s : %s", s.conn.RemoteAddr(), addr, err) + return err + } + defer cc.Close() + + log.Logf("[ss] %s <-> %s", s.conn.RemoteAddr(), addr) + defer log.Logf("[ss] %s >-< %s", s.conn.RemoteAddr(), addr) + + return gost.Transport(&shadowConn{conn: s.conn}, cc) +} + +// This function is copied from shadowsocks library with some modification. +func (s *ShadowServer) getRequest() (host string, err error) { + // buf size should at least have the same size with the largest possible + // request size (when addrType is 3, domain name has at most 256 bytes) + // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) + buf := make([]byte, gost.SmallBufferSize) + + // read till we get possible domain length field + s.conn.SetReadDeadline(time.Now().Add(30 * time.Second)) + if _, err = io.ReadFull(s.conn, buf[:idType+1]); err != nil { + return + } + + var reqStart, reqEnd int + addrType := buf[idType] + switch addrType & ss.AddrMask { + case typeIPv4: + reqStart, reqEnd = idIP0, idIP0+lenIPv4 + case typeIPv6: + reqStart, reqEnd = idIP0, idIP0+lenIPv6 + case typeDm: + if _, err = io.ReadFull(s.conn, buf[idType+1:idDmLen+1]); err != nil { + return + } + reqStart, reqEnd = idDm0, int(idDm0+buf[idDmLen]+lenDmBase) + default: + err = fmt.Errorf("addr type %d not supported", addrType&ss.AddrMask) + return + } + + if _, err = io.ReadFull(s.conn, buf[reqStart:reqEnd]); err != nil { + return + } + + // Return string for typeIP is not most efficient, but browsers (Chrome, + // Safari, Firefox) all seems using typeDm exclusively. So this is not a + // big problem. + switch addrType & ss.AddrMask { + case typeIPv4: + host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() + case typeIPv6: + host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() + case typeDm: + host = string(buf[idDm0 : idDm0+buf[idDmLen]]) + } + // parse port + port := binary.BigEndian.Uint16(buf[reqEnd-2 : reqEnd]) + host = net.JoinHostPort(host, strconv.Itoa(int(port))) + return +} diff --git a/tcp/client.go b/tcp/client.go index 5ed219f..33e9c98 100644 --- a/tcp/client.go +++ b/tcp/client.go @@ -1,12 +1,31 @@ package tcp -import "net" +import ( + "bufio" + "crypto/tls" + "encoding/base64" + "errors" + "fmt" + "net" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + + "github.com/ginuerzh/gosocks4" + "github.com/ginuerzh/gosocks5" + "github.com/ginuerzh/gost/socks" + "github.com/ginuerzh/gost/ssocks" + "github.com/go-log/log" + ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" +) type nodeClient struct { + options *nodeOptions } func (c *nodeClient) Connect() (net.Conn, error) { - return nil, nil + return net.Dial("tcp", c.options.BaseOptions().Addr) } func (c *nodeClient) Handshake(conn net.Conn) (net.Conn, error) { @@ -14,5 +33,157 @@ func (c *nodeClient) Handshake(conn net.Conn) (net.Conn, error) { } func (c *nodeClient) Dial(conn net.Conn, addr string) (net.Conn, error) { - return nil, nil + if c.options.BaseOptions().Protocol == "socks5" { + selector := &socks.ClientSelector{ + TLSConfig: &tls.Config{ + InsecureSkipVerify: !c.options.secureVerify, + ServerName: c.options.serverName, + }, + } + selector.AddMethod( + gosocks5.MethodNoAuth, + gosocks5.MethodUserPass, + socks.MethodTLS, + ) + if len(c.options.users) > 0 { + selector.User = &c.options.users[0] + } + + cc := gosocks5.ClientConn(conn, selector) + if err := cc.Handleshake(); err != nil { + return nil, err + } + conn = cc + } + + return c.dial(conn, addr) +} + +func (c *nodeClient) dial(conn net.Conn, addr string) (net.Conn, error) { + protocol := c.options.BaseOptions().Protocol + switch protocol { + case "ss": // shadowsocks + rawaddr, err := ss.RawAddr(addr) + if err != nil { + return nil, err + } + + var method, password string + if len(c.options.users) > 0 { + method = c.options.users[0].Username() + password, _ = c.options.users[0].Password() + } + + cipher, err := ss.NewCipher(method, password) + if err != nil { + return nil, err + } + + sc, err := ss.DialWithRawAddrConn(rawaddr, conn, cipher) + if err != nil { + return nil, err + } + conn = ssocks.NewConn(sc) + + case "socks5": + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + p, _ := strconv.Atoi(port) + req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{ + Type: gosocks5.AddrDomain, + Host: host, + Port: uint16(p), + }) + if err := req.Write(conn); err != nil { + return nil, err + } + log.Log("[socks5]", req) + + reply, err := gosocks5.ReadReply(conn) + if err != nil { + return nil, err + } + log.Log("[socks5]", reply) + if reply.Rep != gosocks5.Succeeded { + return nil, errors.New("Service unavailable") + } + + case "socks4", "socks4a": + atype := gosocks4.AddrDomain + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + p, _ := strconv.Atoi(port) + + if protocol == "socks4" { + taddr, err := net.ResolveTCPAddr("tcp4", addr) + if err != nil { + return nil, err + } + host = taddr.IP.String() + p = taddr.Port + atype = gosocks4.AddrIPv4 + } + req := gosocks4.NewRequest(gosocks4.CmdConnect, + &gosocks4.Addr{Type: atype, Host: host, Port: uint16(p)}, nil) + if err := req.Write(conn); err != nil { + return nil, err + } + log.Logf("[%s] %s", protocol, req) + + reply, err := gosocks4.ReadReply(conn) + if err != nil { + return nil, err + } + log.Logf("[%s] %s", protocol, reply) + + if reply.Code != gosocks4.Granted { + return nil, fmt.Errorf("%s: code=%d", protocol, reply.Code) + } + case "http": + fallthrough + default: + req := &http.Request{ + Method: http.MethodConnect, + URL: &url.URL{Host: addr}, + Host: addr, + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + req.Header.Set("Proxy-Connection", "keep-alive") + if len(c.options.users) > 0 { + user := c.options.users[0] + s := user.String() + if _, set := user.Password(); !set { + s += ":" + } + req.Header.Set("Proxy-Authorization", + "Basic "+base64.StdEncoding.EncodeToString([]byte(s))) + } + if err := req.Write(conn); err != nil { + return nil, err + } + //if glog.V(LDEBUG) { + dump, _ := httputil.DumpRequest(req, false) + log.Log(string(dump)) + //} + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return nil, err + } + //if glog.V(LDEBUG) { + dump, _ = httputil.DumpResponse(resp, false) + log.Log(string(dump)) + //} + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%d %s", resp.StatusCode, resp.Status) + } + } + + return conn, nil } diff --git a/tcp/node.go b/tcp/node.go index 3bfab96..18cdf82 100644 --- a/tcp/node.go +++ b/tcp/node.go @@ -5,18 +5,21 @@ import ( ) type tcpNode struct { - options *tcpNodeOptions + options *nodeOptions client *nodeClient server *nodeServer } // NewNode creates a tcpNode with options func NewNode(opts ...gost.Option) gost.Node { - node := &tcpNode{ - options: new(tcpNodeOptions), - } + options := new(nodeOptions) for _, opt := range opts { - opt(node.options) + opt(options) + } + node := &tcpNode{ + options: options, + client: &nodeClient{options: options}, + server: &nodeServer{options: options}, } return node diff --git a/tcp/options.go b/tcp/options.go index 87918a5..063b3c9 100644 --- a/tcp/options.go +++ b/tcp/options.go @@ -6,21 +6,55 @@ import ( "github.com/ginuerzh/gost" ) -type tcpNodeOptions struct { - *gost.DefaultOptions - Users []url.Userinfo `opt:"users"` // authentication for proxy +type nodeOptions struct { + base *gost.BaseOptions + users []url.Userinfo `opt:"users"` // authentication for proxy + certFile string `opt:"cert"` + keyFile string `opt:"key"` + serverName string `opt:"server_name"` + secureVerify bool `opt:"secure"` } -func (o *tcpNodeOptions) Get(opt string) interface{} { - return gost.GetOption(o, opt) -} - -func (o *tcpNodeOptions) Set(opt string, v interface{}) { - gost.SetOption(o, opt, v) +func (o *nodeOptions) BaseOptions() *gost.BaseOptions { + return o.base } func UsersOption(users ...url.Userinfo) gost.Option { return func(opts gost.Options) { - gost.SetOption(opts, "users", users) + if o, ok := opts.(*nodeOptions); ok { + o.users = users + } + } +} + +func (o *nodeOptions) ServerNameOption(n string) gost.Option { + return func(opts gost.Options) { + if o, ok := opts.(*nodeOptions); ok { + o.serverName = n + } + } +} + +func (o *nodeOptions) SecureVerifyOption(b bool) gost.Option { + return func(opts gost.Options) { + if o, ok := opts.(*nodeOptions); ok { + o.secureVerify = b + } + } +} + +func (o *nodeOptions) CertFileOption(f string) gost.Option { + return func(opts gost.Options) { + if o, ok := opts.(*nodeOptions); ok { + o.certFile = f + } + } +} + +func (o *nodeOptions) KeyFileOption(f string) gost.Option { + return func(opts gost.Options) { + if o, ok := opts.(*nodeOptions); ok { + o.keyFile = f + } } } diff --git a/tcp/server.go b/tcp/server.go index 809ac7f..d33447a 100644 --- a/tcp/server.go +++ b/tcp/server.go @@ -1,8 +1,137 @@ package tcp +import ( + "bufio" + "net" + "net/http" + "net/url" + "weed-fs/go/glog" + + "github.com/ginuerzh/gosocks4" + "github.com/ginuerzh/gosocks5" + "github.com/ginuerzh/gost" + "github.com/ginuerzh/gost/ssocks" +) + type nodeServer struct { + options *nodeOptions + chain *gost.Chain +} + +func (s *nodeServer) Chain() *gost.Chain { + return s.chain +} + +func (s *nodeServer) SetChain(chain *gost.Chain) { + s.chain = chain +} + +func (s *nodeServer) Options() gost.Options { + return s.options } func (s *nodeServer) Run() error { - return nil + ln, err := net.Listen("tcp", s.options.BaseOptions().Addr) + if err != nil { + return err + } + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + return err + } + go s.handleConn(conn) + } +} + +func (s *nodeServer) handleConn(conn net.Conn) { + defer conn.Close() + + switch s.options.BaseOptions().Protocol { + case "ss": // shadowsocks + var cipher url.Userinfo + if len(s.options.users) > 0 { + cipher = s.options.users[0] + } + server := ssocks.NewServer(conn, &cipher, s) + server.Serve() + return + case "http": + req, err := http.ReadRequest(bufio.NewReader(conn)) + if err != nil { + glog.V(LWARNING).Infoln("[http]", err) + return + } + NewHttpServer(conn, s).HandleRequest(req) + return + case "socks", "socks5": + conn = gosocks5.ServerConn(conn, s.selector) + req, err := gosocks5.ReadRequest(conn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5]", err) + return + } + NewSocks5Server(conn, s).HandleRequest(req) + return + case "socks4", "socks4a": + req, err := gosocks4.ReadRequest(conn) + if err != nil { + glog.V(LWARNING).Infoln("[socks4]", err) + return + } + NewSocks4Server(conn, s).HandleRequest(req) + return + } + + br := bufio.NewReader(conn) + b, err := br.Peek(1) + if err != nil { + glog.V(LWARNING).Infoln(err) + return + } + + switch b[0] { + case gosocks4.Ver4: + req, err := gosocks4.ReadRequest(br) + if err != nil { + glog.V(LWARNING).Infoln("[socks4]", err) + return + } + NewSocks4Server(conn, s).HandleRequest(req) + + case gosocks5.Ver5: + methods, err := gosocks5.ReadMethods(br) + if err != nil { + glog.V(LWARNING).Infoln("[socks5]", err) + return + } + method := s.selector.Select(methods...) + if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { + glog.V(LWARNING).Infoln("[socks5] select:", err) + return + } + c, err := s.selector.OnSelected(method, conn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5] onselected:", err) + return + } + conn = c + + req, err := gosocks5.ReadRequest(conn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5] request:", err) + return + } + NewSocks5Server(conn, s).HandleRequest(req) + + default: // http + req, err := http.ReadRequest(br) + if err != nil { + glog.V(LWARNING).Infoln("[http]", err) + return + } + NewHttpServer(conn, s).HandleRequest(req) + } }