func main() { flag.Parse() klog.SetPrinter( klog.Chain(klog.NewFilterREST("", klog.FilterOut).AddSuffix("debug", "timeout"), klog.Chain(klog.NewDedup(), klog.Fork( klog.NewRingREST("", 1000), klog.GetPrinter())))) body, err := ioutil.ReadFile(*config) if err != nil { log.Fatalf("unable to read file '%s': %s", *config, err.Error()) } controller := new(nfork.Controller) if err := json.Unmarshal(body, &controller.Inbounds); err != nil { log.Fatalf("unable to parse config '%s': %s", *config, err.Error()) } klog.KPrintf("init.info", "starting nfork control on %s\n", *listen) controller.Start() rest.AddService(controller) rest.ListenAndServe(*listen, nil) }
func (inbound *Inbound) error(title, outbound string, err error, t0 time.Time) error { if urlErr, ok := err.(*url.Error); ok { return inbound.error(title, outbound, urlErr.Err, t0) } else if netErr, ok := err.(*net.OpError); ok { if errno, ok := netErr.Err.(syscall.Errno); ok && errno == syscall.ECONNREFUSED { klog.KPrintf(klog.Keyf("%s.%s.%s.timeout", inbound.Name, outbound, title), "%T -> %v", err, err) inbound.record(outbound, Event{Timeout: true, Latency: time.Since(t0)}) return err } return inbound.error(title, outbound, netErr.Err, t0) } switch err.Error() { // Prevents spamming the logs with closed connections even though they were // not properly closed. case "EOF": inbound.record(outbound, Event{Error: true, Latency: time.Since(t0)}) return err // I hate this but net and net/http provides no useful errors or indicators // that a request ended up in a timeout. Furthermore, most of the errors are // either not exported or are just randomly created as string. In other // words, this is a crappy interface that needs to be fixed bad. case "use of closed network connection": // net.errClosing fallthrough case "net/http: transport closed before response was received": fallthrough case "net/http: request canceled while waiting for connection": klog.KPrintf(klog.Keyf("%s.%s.%s.timeout", inbound.Name, outbound, title), "%T -> %v", err, err) inbound.record(outbound, Event{Timeout: true, Latency: time.Since(t0)}) return err } klog.KPrintf(klog.Keyf("%s.%s.%s.error", inbound.Name, outbound, title), "%T -> %v", err, err) inbound.record(outbound, Event{Error: true, Latency: time.Since(t0)}) return err }
// ActivateOutbound activates the given outbound for the given inbound. func (control *Controller) ActivateOutbound(inbound, outbound string) error { control.mutex.Lock() defer control.mutex.Unlock() server, ok := control.inbounds[inbound] if !ok { return fmt.Errorf("unknown inbound '%s'", inbound) } klog.KPrintf("controller.info", "ActivateOutbound(%s, %s)", inbound, outbound) return server.ActivateOutbound(outbound) }
// NewInboundServer creates and starts a new HTTP server associated with the // given Inbound. func NewInboundServer(inbound *Inbound) (*InboundServer, error) { server := new(InboundServer) if err := inbound.Validate(); err != nil { return nil, err } inbound.Init() server.setInbound(inbound) listener, err := net.Listen("tcp", inbound.Listen) if err != nil { klog.KPrintf(klog.Keyf("%s.listen", inbound.Name), "unable to listen on %s: %s", inbound.Listen, err) return nil, err } server.listener = listener go func() { err := http.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)}, server) klog.KPrintf(klog.Keyf("%s.close", server.getInbound().Name), "server closed with: %s", err) }() return server, nil }
// RemoveInbound kills and removes the given inbound. func (control *Controller) RemoveInbound(inbound string) error { control.mutex.Lock() defer control.mutex.Unlock() server, ok := control.inbounds[inbound] if !ok { return fmt.Errorf("unknown inbound '%s'", inbound) } klog.KPrintf("controller.info", "RemoveInbound(%s)", inbound) server.Close() delete(control.inbounds, inbound) return nil }
// AddInbound creates a new InboundServer for the given inbound and launches it. func (control *Controller) AddInbound(inbound *Inbound) error { control.mutex.Lock() defer control.mutex.Unlock() if _, ok := control.inbounds[inbound.Name]; ok { return fmt.Errorf("inbound '%s' already exists", inbound.Name) } server, err := NewInboundServer(inbound) if err != nil { return fmt.Errorf("unable to add inbound '%s': %s", inbound.Name, err) } klog.KPrintf("controller.info", "AddInbound(%s, %s)", inbound.Name, inbound.Listen) control.inbounds[inbound.Name] = server return nil }