예제 #1
0
파일: web.go 프로젝트: codeskyblue/gosuv
func (s *Supervisor) wsLog(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	log.Println(name)
	proc, ok := s.procMap[name]
	if !ok {
		log.Println("No such process")
		// TODO: raise error here?
		return
	}

	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	for data := range proc.Output.NewChanString(r.RemoteAddr) {
		err := c.WriteMessage(1, []byte(data))
		if err != nil {
			proc.Output.CloseWriter(r.RemoteAddr)
			break
		}
	}
}
예제 #2
0
파일: web.go 프로젝트: codeskyblue/gosuv
func (s *Supervisor) wsEvents(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	ch := make(chan string, 0)
	s.addStatusChangeListener(ch)
	go func() {
		_, _ = <-ch // ignore the history messages
		for message := range ch {
			// Question: type 1 ?
			c.WriteMessage(1, []byte(message))
		}
		// s.eventB.RemoveListener(ch)
	}()
	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Println("read:", mt, err)
			break
		}
		log.Printf("recv: %v %s", mt, message)
		err = c.WriteMessage(mt, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}
예제 #3
0
파일: config.go 프로젝트: millken/ydaemon
func startWorker() {
	log.Println("start Worker")
	Cronjob.Clear()
	Cronjob.AddFunc(Config.Flow.Cron,  func() { log.Println(Config.Flow.Cron + ": start DumpFlow()"); go DumpFlow(); })
	//Cronjob.AddFunc("@every 1s", func() { fmt.Println("Every 1") })
	Cronjob.Start()
	//defer Cronjob.Stop()
	//Cronjob.Entries()
}
예제 #4
0
파일: web.go 프로젝트: codeskyblue/gosuv
func (s *Supervisor) addOrUpdateProgram(pg Program) error {
	// defer s.broadcastEvent(pg.Name + " add or update")
	if err := pg.Check(); err != nil {
		return err
	}
	origPg, ok := s.pgMap[pg.Name]
	if ok {
		if reflect.DeepEqual(origPg, pg) {
			return nil
		}
		s.broadcastEvent(pg.Name + " update")
		log.Println("Update:", pg.Name)
		origProc := s.procMap[pg.Name]
		isRunning := origProc.IsRunning()
		go func() {
			s.stopAndWait(origProc.Name)

			newProc := s.newProcess(pg)
			s.procMap[pg.Name] = newProc
			s.pgMap[pg.Name] = pg // update origin
			if isRunning {
				newProc.Operate(StartEvent)
			}
		}()
	} else {
		s.names = append(s.names, pg.Name)
		s.pgMap[pg.Name] = pg
		s.procMap[pg.Name] = s.newProcess(pg)
		s.broadcastEvent(pg.Name + " added")
	}
	return nil
}
예제 #5
0
파일: gosuv.go 프로젝트: codeskyblue/gosuv
func actionConfigTest(c *cli.Context) error {
	if _, _, err := newSupervisorHandler(); err != nil {
		log.Fatal(err)
	}
	log.Println("test is successful")
	return nil
}
예제 #6
0
파일: fsm.go 프로젝트: codeskyblue/gosuv
func (p *Process) startCommand() {
	// p.Stdout.Reset()
	// p.Stderr.Reset()
	// p.Output.Reset() // Donot reset because log is still needed.
	log.Printf("start cmd(%s): %s", p.Name, p.Command)
	p.cmd = p.buildCommand()

	p.SetState(Running)
	if err := p.cmd.Start(); err != nil {
		log.Warnf("program %s start failed: %v", p.Name, err)
		p.SetState(Fatal)
		return
	}
	go func() {
		errC := GoFunc(p.cmd.Wait)
		startTime := time.Now()
		select {
		case <-errC:
			// if p.cmd.Wait() returns, it means program and its sub process all quited. no need to kill again
			// func Wait() will only return when program session finishs. (Only Tested on mac)
			log.Printf("program(%s) finished, time used %v", p.Name, time.Since(startTime))
			if time.Since(startTime) < time.Duration(p.StartSeconds)*time.Second {
				if p.retryLeft == p.StartRetries { // If first time quit so fast, just set to fatal
					p.SetState(Fatal)
					log.Printf("program(%s) exit too quick, status -> fatal", p.Name)
					return
				}
			}
			p.waitNextRetry()
		case <-p.stopC:
			log.Println("recv stop command")
			p.stopCommand() // clean up all process
		}
	}()
}
예제 #7
0
func DownloadImg(url_ string) {
	resp, err := http.Get(url_)
	if goutils.CheckErr(err) {
		return
	}
	log.Println(resp.Header.Get("Content-Type"))
	log.Println(resp.ContentLength)
	_ = resp
	b, err := ioutil.ReadAll(resp.Body)
	if goutils.CheckErr(err) {
		return
	}
	// log.Println(b)
	file, _ := os.OpenFile("down.png", os.O_CREATE|os.O_WRONLY, 0644)
	file.Write(b)
}
예제 #8
0
func proxyHandler(w http.ResponseWriter, r *http.Request) {
	hj, ok := w.(http.Hijacker)
	if !ok {
		http.Error(w, "webserver don't support hijacking", http.StatusInternalServerError)
		return
	}

	var proxyFor = r.Header.Get("X-Proxy-For")
	log.Println("X-Proxy-For client name:", r.RemoteAddr, proxyFor)

	connCh, ok := namedConnection[proxyFor]
	if !ok {
		http.Error(w, "inside error: proxy not ready to receive conn", http.StatusInternalServerError)
		return
	}
	hjconn, bufrw, err := hj.Hijack()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		connCh <- nil
		return
	}
	//if _, ok := hjconn.(*net.TCPConn); ok {
	//log.Println("Hijack is tcp conn")
	//}

	conn := NewHijackReadWriteCloser(hjconn.(*net.TCPConn), bufrw)
	connCh <- conn
}
예제 #9
0
파일: keeper.go 프로젝트: anley/webcron
func (k *Keeper) NewRecord(name string) (key string, rec *Record, err error) {
	k.tkmu.Lock()
	defer k.tkmu.Unlock()

	task, ok := k.tasks[name]
	if !ok {
		return "", nil, fmt.Errorf("No such task has name: %s", name)
	}

	xe.ShowSQL = true
	total, err := xe.Where("name = ?", name).Count(&Record{})
	log.Println("CC:", total, err)

	// order update
	k.removeFromOrder(name)
	k.taskOrder = append([]string{name}, k.taskOrder...)

	idx := int(total)
	rec = &Record{
		Name:   name,
		Index:  idx,
		Buffer: bytes.NewBuffer(nil),
		T:      task,
	}
	key = rec.Key()
	k.crmu.Lock()
	k.runRecs[key] = rec
	k.crmu.Unlock()
	return key, rec, nil
}
예제 #10
0
func (p *ProxyConn) start() {
	defer p.lconn.Close()
	defer p.rconn.Close()
	//connect to remote
	// rconn, err := net.DialTCP("tcp", nil, p.raddr)
	// if err != nil {
	// 	log.Printf("Remote connection failed: %s", err)
	// 	return
	// }
	// p.rconn = rconn
	// defer p.rconn.Close()

	// FIXME: may need to set a flag
	if tcpconn, ok := p.lconn.(*net.TCPConn); ok {
		tcpconn.SetNoDelay(true)
	}
	if tcpconn, ok := p.rconn.(*net.TCPConn); ok {
		tcpconn.SetNoDelay(true)
	}
	// p.lconn.SetNoDelay(true)
	// p.rconn.SetNoDelay(true)

	//display both ends
	// log.Printf("Opened %s >>> %s", p.lconn.RemoteAddr().String(), p.rconn.RemoteAddr().String())
	//bidirectional copy
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		ch1 := p.pipe(p.lconn, p.rconn)
		<-ch1
		closeRead(p.lconn)
		closeWrite(p.rconn)
		log.Println("close local -> remote")
		wg.Done()
	}()
	go func() {
		ch2 := p.pipe(p.rconn, p.lconn)
		<-ch2
		closeRead(p.rconn)
		closeWrite(p.lconn)
		log.Println("close remote -> local")
		wg.Done()
	}()
	wg.Wait()
	//wait for close...
	// log.Printf("Closed (%d bytes sent, %d bytes recieved)", p.sentBytes, p.receivedBytes)
}
예제 #11
0
파일: main.go 프로젝트: millken/ydaemon
func main() {
	flag.Parse()

	log.SetOutputLevel(*flagdebug)

	configFileName := filepath.Clean(*flagconfig + "/main.conf")
	log.Debugf("config file '%s'\n", configFileName)

	log.Printf("Starting %s\n", VERSION)

	if *cpuprofile != "" {
		prof, err := os.Create(*cpuprofile)
		if err != nil {
			panic(err.Error())
		}

		pprof.StartCPUProfile(prof)
		defer func() {
			log.Println("closing file")
			prof.Close()
		}()
		defer func() {
			log.Println("stopping profile")
			pprof.StopCPUProfile()
		}()
	}

	go configWatcher(configFileName)

	terminate := make(chan os.Signal)
	signal.Notify(terminate, os.Interrupt)

	<-terminate
	log.Printf("signal received, stopping")

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}

	//os.Exit(0)

}
예제 #12
0
func update_popup() (ReadEntity, error) {
	fs, fes, err := feedentries_updated()
	if err == nil {
		return ReadEntity{ReadSource: fs, Entries: fes}, nil
	}
	log.Println("update-popup", err)
	return ReadEntity{}, err
}
예제 #13
0
func load_gfwrules() {
	fp := filepath.Join(backend_config().DataFolder, "gfwlist.txt")
	reader, err := os.Open(fp)
	if err == nil {
		defer reader.Close()
		backend_context.ruler, err = gfwlist.NewGfwRuler(reader)
	}
	log.Println("gfwlist-load", err, fp)
}
예제 #14
0
파일: gosuv.go 프로젝트: codeskyblue/gosuv
func actionStatus(c *cli.Context) error {
	err := checkServerStatus()
	if err != nil {
		log.Fatal(err)
	} else {
		log.Println("Server is running, OK.")
	}
	return nil
}
예제 #15
0
파일: parse.go 프로젝트: codeskyblue/shweb
func addRoute(m *macaron.Macaron, method, patten, script, contentType string) {
	log.Println("Add Route:", method, patten, script)
	method = strings.ToUpper(method)
	switch method {
	case "GET":
		m.Get(patten, NewScriptHandler(script, contentType))
	case "POST":
		m.Post(patten, NewScriptHandler(script, contentType))
	}
}
예제 #16
0
파일: web.go 프로젝트: codeskyblue/gosuv
// Performance
func (s *Supervisor) wsPerf(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	name := mux.Vars(r)["name"]
	proc, ok := s.procMap[name]
	if !ok {
		log.Println("No such process")
		// TODO: raise error here?
		return
	}
	for {
		// c.SetWriteDeadline(time.Now().Add(3 * time.Second))
		if proc.cmd == nil || proc.cmd.Process == nil {
			log.Println("process not running")
			return
		}
		pid := proc.cmd.Process.Pid
		ps, err := gops.NewProcess(pid)
		if err != nil {
			break
		}
		mainPinfo, err := ps.ProcInfo()
		if err != nil {
			break
		}
		pi := ps.ChildrenProcInfo(true)
		pi.Add(mainPinfo)

		err = c.WriteJSON(pi)
		if err != nil {
			break
		}
		time.Sleep(700 * time.Millisecond)
	}
}
예제 #17
0
func handleRevConn(pURL *url.URL, lis net.Listener) {
	switch pURL.Scheme {
	case "tcp":
		for {
			rconn, err := lis.Accept()
			if err != nil {
				log.Errorf("accept error: %v", err)
				return
			}
			log.Info("dial local:", pURL)
			lconn, err := net.Dial("tcp", pURL.Host)
			if err != nil {
				// wsclient
				log.Println(err)
				rconn.Close()
				break
			}
			// start forward local proxy
			pc := &ProxyConn{
				lconn: lconn,
				rconn: rconn,
				stats: proxyStats,
			}
			go pc.start()
		}
	case "http", "https":
		remote := pURL
		rp := &httputil.ReverseProxy{
			Director: func(req *http.Request) {
				req.Host = remote.Host
				req.URL.Scheme = remote.Scheme
				req.URL.Host = remote.Host
			},
		}
		http.Serve(lis, rp)
	default:
		log.Println("Unknown protocol:", pURL.Scheme)
	}
}
예제 #18
0
func feedentries_updated() (ReadSource, []ReadEntry, error) {
	bcms := baidu.NewBcmsProxy(baiduq)
	var v pubsub.PubsubMessage
	err := bcms.FetchOneAsJson(&v)
	if err != nil {
		return ReadSource{}, nil, err
	}
	if (v.Status.StatusCode != 200 && v.Status.StatusCode != 0) || v.Status.Feed == "" {
		return ReadSource{}, nil, new_backenderror(v.Status.StatusCode, v.Status.StatusReason)
	}
	fs := ReadSource{
		FeedSource: feed.FeedSource{
			Name:        v.Title,
			Uri:         v.Status.Feed,
			Description: v.Subtitle,
			Period:      v.Status.Period / 60,
			Update:      int64(v.Updated),
		},
		LastTouch:  int64(unixtime.TimeNow()),
		NextTouch:  int64(unixtime.Time(v.Status.Period) + unixtime.TimeNow()),
		LastUpdate: int64(unixtime.TimeNow()),
	}
	if fs.Period == 0 {
		fs.Period = 120 // minutes
	}
	fes := make([]ReadEntry, len(v.Items))
	for idx, i := range v.Items {
		fes[idx] = ReadEntry{
			FeedEntry: feed.FeedEntry{
				Uri:     i.Uri,
				Title:   i.Title,
				PubDate: int64(i.Published),
				Summary: i.Summary,
				Content: i.Content,
				Tags:    i.Categories,
			},
		}
		feedentry_init_from_standardlinks(i.StandardLinks, fes[idx])
		if fes[idx].Uri == "" {
			feedentry_init_from_links(i.Links, fes[idx])
		}
	}
	if err == nil {
		fes = readentry_filter(fes)
		fo := new_feedsource_operator()
		// ignore touch error, because, source may not be subscribed
		fo.touch(fs.Uri, int64(fs.LastTouch), int64(fs.NextTouch), fs.Period)
	}
	log.Println("updated", fs.Name, fs.Update)
	return fs, fes, err
}
예제 #19
0
// source : feed(atom/rss) url
// count: must large than 0
// page: 0 based page index
// if page is 0, entries may be fetched online
func feedentry_unread(source string, count int, page int) ([]ReadEntry, error, int) {
	if count <= 0 {
		panic("invalid arg count")
	}
	var sc int
	if page == 0 {
		log.Println("curl-get...")
		c := curl.NewCurlerDetail(backend_config().FeedEntryFolder, 0, 0, nil, backend_context.ruler)
		cache, err := c.GetUtf8(source)
		log.Println("curl-get", cache.LocalUtf8)
		if err != nil || cache.LocalUtf8 == "" {
			return nil, err, cache.StatusCode
		}

		ext := curl.MimeToExt(cache.Mime)
		if ext != "xml" && ext != "atom+xml" && ext != "rss+xml" {
			return nil, new_backenderror(cache.StatusCode, "unsupported mime: "+cache.Mime), 0
		}
		f, err := os.Open(cache.LocalUtf8)
		if err != nil {
			return nil, err, cache.StatusCode
		}

		fs, v, err := feed.NewFeedMaker(f, source).MakeFeed()
		f.Close()
		rs := new_readsource(fs)
		if err == nil {
			new_feedsource_operator().update(rs)
			log.Println("feed-update", fs.Name)
		}
		rv := readentry_filter(new_readentries(v))
		log.Println("feedentries-filter", len(rv))
		sc = cache.StatusCode
	}
	rv, err := new_feedentry_operator().topn_by_feedsource(count*page, count, source)
	log.Println("unread-return(uri, page, count)", source, page, count, len(rv), err)
	return rv, err, sc
}
예제 #20
0
파일: apis.go 프로젝트: heartszhang/famous
func update_work() {
	backend_context.Lock()
	defer backend_context.Unlock()
	w := atomic.AddInt64(&backend_context.working, 1)
	defer atomic.AddInt64(&backend_context.working, -1)
	if w != 1 {
		return
	}
	fss, err := feedsource_expired(time.Now().Unix())
	if err != nil || len(fss) == 0 {
		return
	}
	idx := rand.Intn(len(fss))
	fs := fss[idx]
	newfs, fes, err := feed_fetch(fs.Uri)
	newfs.Type = fs.Type
	newfs.EnableProxy = fs.EnableProxy
	newfs.Categories = append(newfs.Categories, fs.Categories...)
	if newfs.Logo == "" {
		newfs.Logo = fs.Logo
	}

	newfs.SubscribeState = fs.SubscribeState
	newfs.LastTouch = int64(unixtime.TimeNow())
	newfs.LastUpdate = newfs.LastTouch
	newfs.NextTouch = newfs.Period + newfs.LastTouch
	err = feedsource_save(newfs)
	fes = readentry_filter(fes)
	backend_push_update(newfs, fes, err)
	ps := pubsub.NewSuperFeedrPubSubscriber("async", "Hearts", "Refresh")
	sc, err := ps.Subscribe(fs.Uri)
	if err != nil {
		log.Println("pubsub-google", sc, err)
	}
	ps = pubsub.NewGooglePubSubscriber()
	sc, err = ps.Subscribe(fs.Uri)
	log.Println("update-tick", fs.Name, sc, err)
}
예제 #21
0
func handleWsMsg(msg Msg, sURL *url.URL, rnl *RevNetListener) {
	u := sURL
	switch msg.Type {
	case TYPE_NEWCONN:
		log.Debug("dial remote:", u.Host)
		sconn, err := net.Dial("tcp", u.Host)
		if err != nil {
			log.Println(err)
			break
		}
		_, err = sconn.Write([]byte(fmt.Sprintf(
			"GET /proxyhijack HTTP/1.1\r\nX-Proxy-For: %s \r\n\r\n", msg.Name)))
		if err != nil {
			log.Println(err)
			break
		}
		rnl.connCh <- sconn
	case TYPE_MESSAGE:
		fmt.Printf("Recv Message: %v\n", msg.Body)
	default:
		log.Warnf("Type: %v not support", msg.Type)
	}
}
예제 #22
0
파일: web.go 프로젝트: codeskyblue/gosuv
func (s *Supervisor) hReload(w http.ResponseWriter, r *http.Request) {
	err := s.loadDB()
	log.Println("reload config file")
	if err == nil {
		s.renderJSON(w, JSONResponse{
			Status: 0,
			Value:  "load config success",
		})
	} else {
		s.renderJSON(w, JSONResponse{
			Status: 1,
			Value:  err.Error(),
		})
	}
}
예제 #23
0
파일: web.go 프로젝트: codeskyblue/gosuv
func (s *Supervisor) catchExitSignal() {
	sigC := make(chan os.Signal, 1)
	signal.Notify(sigC, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
	go func() {
		for sig := range sigC {
			if sig == syscall.SIGHUP {
				log.Println("Receive SIGHUP, just ignore")
				continue
			}
			log.Printf("Got signal: %v, stopping all running process\n", sig)
			s.Close()
			break
		}
		os.Exit(0)
	}()
}
예제 #24
0
func closeRead(c net.Conn) error {
	if x, ok := c.(interface {
		CloseRead() error
	}); ok {
		return x.CloseRead()
	} else {
		log.Println("force close", c)
		return c.Close()
	}
	// if tcpconn, ok := c.(*net.TCPConn); ok {
	// 	return tcpconn.CloseRead()
	// } else {
	// 	// return c.Close()
	// }
	// return nil
}
예제 #25
0
파일: web.go 프로젝트: insionng/gosuv
func handleSignal(lis net.Listener) {
	sigc := make(chan os.Signal, 2)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGHUP)
	go func() {
		for sig := range sigc {
			log.Println("Receive signal:", sig)
			if sig == syscall.SIGHUP {
				return // ignore
			}
			lis.Close()
			programTable.StopAll()
			os.Exit(0)
			return
		}
	}()
}
예제 #26
0
func (this *UfopServer) Listen() {
	//define handler
	http.HandleFunc("/uop", this.serveUfop)

	//bind and listen
	endPoint := fmt.Sprintf("%s:%d", this.cfg.ListenHost, this.cfg.ListenPort)
	ufopServer := &http.Server{
		Addr:           endPoint,
		ReadTimeout:    time.Duration(this.cfg.ReadTimeout) * time.Second,
		WriteTimeout:   time.Duration(this.cfg.WriteTimeout) * time.Second,
		MaxHeaderBytes: this.cfg.MaxHeaderBytes,
	}

	listenErr := ufopServer.ListenAndServe()
	if listenErr != nil {
		log.Println(listenErr)
	}
}
예제 #27
0
파일: keeper.go 프로젝트: anley/webcron
func (k *Keeper) reloadCron() {
	if k.cr != nil {
		k.cr.Stop()
	}
	k.cr = cron.New()
	for _, task := range k.orderedTasks() {
		ta := task
		if !ta.Enabled {
			continue
		}
		taskFunc := func() {
			if err := ta.Run(TRIGGER_SCHEDULE); err != nil {
				log.Println(ta.Name, err)
			}
		}
		k.cr.AddFunc(task.Schedule, taskFunc)
	}
	k.cr.Start()
}
예제 #28
0
파일: main.go 프로젝트: everfore/gotest
func root(rw http.ResponseWriter, req *http.Request) {
	notify := rw.(http.CloseNotifier).CloseNotify()
	go func() {
		select {
		case <-notify:
			log.Println("closed>", req.RemoteAddr)
		}
	}()
	log.Info(req.RemoteAddr)
	// ticker := time.NewTicker(2e9)
	// for {
	// 	select {
	// 	case <-ticker.C:
	// go hello(rw)
	rw.Write(ToByte("hello."))
	// 		log.Info("hello")
	// 	default:
	// 	}
	// }
}
예제 #29
0
파일: web.go 프로젝트: anley/webcron
func main() {
	flag.StringVar(&gcfg.SchedFile, "sched", "sched.json", "file which store schedule setting")
	flag.IntVar(&gcfg.ServerPort, "port", 4000, "port to listen")
	flag.StringVar(&gcfg.LogDir, "logdir", "logs", "log directory")
	flag.Parse()

	var err error
	xe, err = xorm.NewEngine("sqlite3", "./test.db")
	//xe, err = xorm.NewEngine("mysql", "cron:cron@tcp(10.246.13.180:3306)/cron?charset=utf8")
	// xe, err = xorm.NewEngine("mysql", "root:@/cron?charset=utf8")
	if err != nil {
		log.Fatal(err)
	}
	if err := xe.Sync(Record{}); err != nil {
		log.Fatal(err)
	}

	if _, err = os.Stat(gcfg.LogDir); err != nil {
		os.Mkdir(gcfg.LogDir, 0755)
	}
	if _, err = os.Stat(gcfg.SchedFile); err != nil {
		ioutil.WriteFile(gcfg.SchedFile, []byte("[]"), 0644)
	}
	tasks, err := loadTasks(gcfg.SchedFile)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(tasks)

	keeper = NewKeeper(tasks)

	initRoutes()
	log.Printf("Listening on *:%d", gcfg.ServerPort)
	http.Handle("/", m)
	//http.Handle("/-/", http.StripPrefix("/-/", http.FileServer(http.Dir("public"))))
	if err := http.ListenAndServe(":"+strconv.Itoa(gcfg.ServerPort), nil); err != nil {
		log.Fatal(err)
	}
}
예제 #30
0
func serveRevConn(proto ProxyProtocol, pAddr string, lis net.Listener) error {
	switch proto {
	case TCP:
		for {
			rconn, err := lis.Accept()
			if err != nil {
				log.Errorf("accept error: %v", err)
				return err
			}
			log.Info("local dial tcp", pAddr)
			lconn, err := net.Dial("tcp", pAddr)
			if err != nil {
				log.Warn(err)
				rconn.Close()
				return err
			}
			// start forward local proxy
			pc := &proxyConn{
				lconn: lconn,
				rconn: rconn,
				stats: proxyStats,
			}
			go pc.start()
		}
	case HTTP:
		rp := &httputil.ReverseProxy{
			Director: func(req *http.Request) {
				req.Host = pAddr
				req.URL.Scheme = "http"
				req.URL.Host = pAddr
			},
		}
		return http.Serve(lis, rp)
	default:
		log.Println("Unknown protocol:", proto)
		return ErrUnknownProtocol
	}
}