GEFN (good enough for now)
This commit is contained in:
		
							parent
							
								
									2b88da7a71
								
							
						
					
					
						commit
						3943c14845
					
				| @ -13,6 +13,10 @@ import ( | |||||||
| 	restful "github.com/emicklei/go-restful" | 	restful "github.com/emicklei/go-restful" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type JsonMsg struct { | ||||||
|  | 	Messages []*chatMsg `json:"messages"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type myHttpServer struct { | type myHttpServer struct { | ||||||
| 	chans chan bufferedConn | 	chans chan bufferedConn | ||||||
| 	net.Listener | 	net.Listener | ||||||
| @ -43,10 +47,7 @@ type authReq struct { | |||||||
| func serveStatic(req *restful.Request, resp *restful.Response) { | func serveStatic(req *restful.Request, resp *restful.Response) { | ||||||
| 	actual := path.Join(config.RootPath, req.PathParameter("subpath")) | 	actual := path.Join(config.RootPath, req.PathParameter("subpath")) | ||||||
| 	fmt.Printf("serving %s ... (from %s)\n", actual, req.PathParameter("subpath")) | 	fmt.Printf("serving %s ... (from %s)\n", actual, req.PathParameter("subpath")) | ||||||
| 	http.ServeFile( | 	http.ServeFile(resp.ResponseWriter, req.Request, actual) | ||||||
| 		resp.ResponseWriter, |  | ||||||
| 		req.Request, |  | ||||||
| 		actual) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func serveHello(req *restful.Request, resp *restful.Response) { | func serveHello(req *restful.Request, resp *restful.Response) { | ||||||
| @ -93,7 +94,7 @@ func requestAuth(req *restful.Request, resp *restful.Response) { | |||||||
| 	} | 	} | ||||||
| 	ar.Otp = otp | 	ar.Otp = otp | ||||||
| 
 | 
 | ||||||
| 	// Cheat code in case you didn't set up mailgun keys | 	// Cheat code in case you didn't set up mailgun keys in the config file | ||||||
| 	fmt.Fprintf(os.Stdout, "\n== HTTP AUTHORIZATION ==\n[cheat code for %s]: %s\n", ar.Subject, ar.Otp) | 	fmt.Fprintf(os.Stdout, "\n== HTTP AUTHORIZATION ==\n[cheat code for %s]: %s\n", ar.Subject, ar.Otp) | ||||||
| 
 | 
 | ||||||
| 	cid, _ := genAuthCode() | 	cid, _ := genAuthCode() | ||||||
| @ -102,10 +103,8 @@ func requestAuth(req *restful.Request, resp *restful.Response) { | |||||||
| 	} | 	} | ||||||
| 	ar.Cid = cid | 	ar.Cid = cid | ||||||
| 
 | 
 | ||||||
| 	newAuthReqs <- ar | 	authReqs <- ar | ||||||
| 
 | 
 | ||||||
| 	// Not sure why this works... technically there needs to be some sort of "end" |  | ||||||
| 	// maybe it just figures that if I've returned |  | ||||||
| 	fmt.Fprintf(resp, "{ \"success\": true, \"cid\": \""+ar.Cid+"\" }") | 	fmt.Fprintf(resp, "{ \"success\": true, \"cid\": \""+ar.Cid+"\" }") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -118,7 +117,6 @@ func issueToken(req *restful.Request, resp *restful.Response) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//err := json.NewDecoder(r.Body).Decode(&ar) |  | ||||||
| 	err := req.ReadEntity(&ar) | 	err := req.ReadEntity(&ar) | ||||||
| 	if nil != err { | 	if nil != err { | ||||||
| 		fmt.Fprintf(resp, "{ \"error\": { \"message\": \"bad json in request body\"} }") | 		fmt.Fprintf(resp, "{ \"error\": { \"message\": \"bad json in request body\"} }") | ||||||
| @ -152,12 +150,12 @@ func issueToken(req *restful.Request, resp *restful.Response) { | |||||||
| 		// to overwrite the original with the updated | 		// to overwrite the original with the updated | ||||||
| 		// (these are copies, not pointers, IIRC) | 		// (these are copies, not pointers, IIRC) | ||||||
| 		// and it seems like this is how I might write to a DB anyway | 		// and it seems like this is how I might write to a DB anyway | ||||||
| 		newAuthReqs <- av | 		authReqs <- av | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	av.DidAuth = true | 	av.DidAuth = true | ||||||
| 	ar.VerifiedAt = time.Now() | 	ar.VerifiedAt = time.Now() | ||||||
| 	newAuthReqs <- av | 	authReqs <- av | ||||||
| 
 | 
 | ||||||
| 	// TODO I would use a JWT, but I need to wrap up this project | 	// TODO I would use a JWT, but I need to wrap up this project | ||||||
| 	fmt.Fprintf(resp, "{ \"success\": true, \"token\": \""+ar.Cid+"\" }") | 	fmt.Fprintf(resp, "{ \"success\": true, \"token\": \""+ar.Cid+"\" }") | ||||||
| @ -201,9 +199,8 @@ func requireToken(req *restful.Request, resp *restful.Response, chain *restful.F | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func listMsgs(req *restful.Request, resp *restful.Response) { | func listMsgs(req *restful.Request, resp *restful.Response) { | ||||||
| 	// TODO support ?since=<ISO_TS> | 	// TODO support ?since=<ISO_TS>, but for now we'll just let the client sort the list | ||||||
| 	// Also, data race? the list could be added to while this is iterating? | 	// TODO Could this have a data race if the list were added to while this is iterating? | ||||||
| 	// For now we'll just let the client sort the list |  | ||||||
| 	resp.WriteEntity(&JsonMsg{ | 	resp.WriteEntity(&JsonMsg{ | ||||||
| 		Messages: myChatHist.msgs[:myChatHist.c], | 		Messages: myChatHist.msgs[:myChatHist.c], | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
| @ -9,6 +9,13 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type telnetUser struct { | ||||||
|  | 	bufConn   bufferedConn | ||||||
|  | 	userCount chan int | ||||||
|  | 	email     string | ||||||
|  | 	newMsg    chan string | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Trying to keep it slim with just one goroutine per client for each reads and writes. | // Trying to keep it slim with just one goroutine per client for each reads and writes. | ||||||
| // Initially I was spawning a goroutine per write in the main select, but my guess is that | // Initially I was spawning a goroutine per write in the main select, but my guess is that | ||||||
| // constantly allocating and cleaning up 4k of memory (or perhaps less these days | // constantly allocating and cleaning up 4k of memory (or perhaps less these days | ||||||
| @ -26,7 +33,7 @@ func handleTelnetConn(bufConn bufferedConn) { | |||||||
| 
 | 
 | ||||||
| 	// Handle all subsequent packets | 	// Handle all subsequent packets | ||||||
| 	buffer := make([]byte, 1024) | 	buffer := make([]byte, 1024) | ||||||
| 	var u *tcpUser | 	var u *telnetUser | ||||||
| 	for { | 	for { | ||||||
| 		//fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n") | 		//fmt.Fprintf(os.Stdout, "[raw] Waiting for message...\n") | ||||||
| 		count, err := bufConn.Read(buffer) | 		count, err := bufConn.Read(buffer) | ||||||
| @ -136,7 +143,7 @@ func handleTelnetConn(bufConn bufferedConn) { | |||||||
| 				authn = true | 				authn = true | ||||||
| 				time.Sleep(150 * time.Millisecond) | 				time.Sleep(150 * time.Millisecond) | ||||||
| 				fmt.Fprintf(bufConn, "\n") | 				fmt.Fprintf(bufConn, "\n") | ||||||
| 				u = &tcpUser{ | 				u = &telnetUser{ | ||||||
| 					bufConn:   bufConn, | 					bufConn:   bufConn, | ||||||
| 					email:     email, | 					email:     email, | ||||||
| 					userCount: make(chan int, 1), | 					userCount: make(chan int, 1), | ||||||
| @ -156,6 +163,7 @@ func handleTelnetConn(bufConn bufferedConn) { | |||||||
| 				time.Sleep(50 * time.Millisecond) | 				time.Sleep(50 * time.Millisecond) | ||||||
| 				fmt.Fprintf(bufConn, "\n") | 				fmt.Fprintf(bufConn, "\n") | ||||||
| 				time.Sleep(50 * time.Millisecond) | 				time.Sleep(50 * time.Millisecond) | ||||||
|  | 				// It turns out that ANSI characters work in Telnet just fine | ||||||
| 				fmt.Fprintf(bufConn, "\033[1;32m"+"Welcome to #general (%d users)!"+"\033[22;39m", count) | 				fmt.Fprintf(bufConn, "\033[1;32m"+"Welcome to #general (%d users)!"+"\033[22;39m", count) | ||||||
| 				time.Sleep(50 * time.Millisecond) | 				time.Sleep(50 * time.Millisecond) | ||||||
| 				fmt.Fprintf(bufConn, "\n") | 				fmt.Fprintf(bufConn, "\n") | ||||||
| @ -166,7 +174,8 @@ func handleTelnetConn(bufConn bufferedConn) { | |||||||
| 				fmt.Fprintf(bufConn, "\n") | 				fmt.Fprintf(bufConn, "\n") | ||||||
| 
 | 
 | ||||||
| 				// Would be cool to write a prompt... | 				// Would be cool to write a prompt... | ||||||
| 				// I wonder if I could send the correct ANSI codes for that... | 				// I wonder if I could send fudge some ANSI codes to keep the prompt | ||||||
|  | 				// even when new messages come in, but not overwrite what he user typed... | ||||||
| 				//fmt.Fprintf(bufConn, "\n%s> ", email) | 				//fmt.Fprintf(bufConn, "\n%s> ", email) | ||||||
| 
 | 
 | ||||||
| 				go handleTelnetBroadcast(u) | 				go handleTelnetBroadcast(u) | ||||||
| @ -185,7 +194,7 @@ func handleTelnetConn(bufConn bufferedConn) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Writes (post Auth) | // Writes (post Auth) | ||||||
| func handleTelnetBroadcast(u *tcpUser) { | func handleTelnetBroadcast(u *telnetUser) { | ||||||
| 	for { | 	for { | ||||||
| 		msg, more := <-u.newMsg | 		msg, more := <-u.newMsg | ||||||
| 		if !more { | 		if !more { | ||||||
|  | |||||||
| @ -39,13 +39,6 @@ type ConfMailer struct { | |||||||
| 	From   string `yaml:"from,omitempty"` | 	From   string `yaml:"from,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type tcpUser struct { |  | ||||||
| 	bufConn   bufferedConn |  | ||||||
| 	userCount chan int |  | ||||||
| 	email     string |  | ||||||
| 	newMsg    chan string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // So we can peek at net.Conn, which we can't do natively | // So we can peek at net.Conn, which we can't do natively | ||||||
| // https://stackoverflow.com/questions/51472020/how-to-get-the-size-of-available-tcp-data | // https://stackoverflow.com/questions/51472020/how-to-get-the-size-of-available-tcp-data | ||||||
| type bufferedConn struct { | type bufferedConn struct { | ||||||
| @ -80,28 +73,29 @@ type chatMsg struct { | |||||||
| 	Channel    string    `json:"channel"` | 	Channel    string    `json:"channel"` | ||||||
| 	User       string    `json:"user"` | 	User       string    `json:"user"` | ||||||
| } | } | ||||||
| type JsonMsg struct { |  | ||||||
| 	Messages []*chatMsg `json:"messages"` |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
|  | // Poor-Man's container/ring (circular buffer) | ||||||
| type chatHist struct { | type chatHist struct { | ||||||
| 	msgs []*chatMsg | 	msgs []*chatMsg | ||||||
| 	i    int | 	i    int // current index | ||||||
| 	c    int | 	c    int // current count (number of elements) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Multi-use | ||||||
|  | var config Conf | ||||||
|  | var virginConns chan net.Conn | ||||||
|  | var gotClientHello chan bufferedConn | ||||||
| var myChatHist chatHist | var myChatHist chatHist | ||||||
| var broadcastMsg chan chatMsg | var broadcastMsg chan chatMsg | ||||||
| 
 | 
 | ||||||
| var virginConns chan net.Conn | // Telnet | ||||||
| var wantsServerHello chan bufferedConn | var wantsServerHello chan bufferedConn | ||||||
| var authTelnet chan tcpUser | var authTelnet chan telnetUser | ||||||
| var cleanTelnet chan tcpUser | var cleanTelnet chan telnetUser | ||||||
| var gotClientHello chan bufferedConn |  | ||||||
| 
 | 
 | ||||||
| // HTTP | // HTTP | ||||||
| var demuxHttpClient chan bufferedConn | var demuxHttpClient chan bufferedConn | ||||||
| var newAuthReqs chan authReq | var authChallenge chan authReq | ||||||
| var valAuthReqs chan authReq | var valAuthReqs chan authReq | ||||||
| var delAuthReqs chan authReq | var delAuthReqs chan authReq | ||||||
| 
 | 
 | ||||||
| @ -184,7 +178,7 @@ func muxTcp(conn bufferedConn) { | |||||||
| 	demuxHttpClient <- conn | 	demuxHttpClient <- conn | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func handleConnection(netConn net.Conn) { | func testForHello(netConn net.Conn) { | ||||||
| 	ts := time.Now() | 	ts := time.Now() | ||||||
| 	fmt.Fprintf(os.Stdout, "[New Connection] (%s) welcome %s\n", ts, netConn.RemoteAddr().String()) | 	fmt.Fprintf(os.Stdout, "[New Connection] (%s) welcome %s\n", ts, netConn.RemoteAddr().String()) | ||||||
| 
 | 
 | ||||||
| @ -193,13 +187,11 @@ func handleConnection(netConn net.Conn) { | |||||||
| 
 | 
 | ||||||
| 	bufConn := newBufferedConn(netConn) | 	bufConn := newBufferedConn(netConn) | ||||||
| 	go func() { | 	go func() { | ||||||
| 		// Handle First Packet | 		// Cause first packet to be loaded into buffer | ||||||
| 		_, err := bufConn.Peek(1) | 		_, err := bufConn.Peek(1) | ||||||
| 		//fmsg, err := bufConn.Peek(1) |  | ||||||
| 		if nil != err { | 		if nil != err { | ||||||
| 			panic(err) | 			panic(err) | ||||||
| 		} | 		} | ||||||
| 		//fmt.Fprintf(os.Stdout, "[First Byte] %s\n", fmsg) |  | ||||||
| 
 | 
 | ||||||
| 		m.Lock() | 		m.Lock() | ||||||
| 		if virgin { | 		if virgin { | ||||||
| @ -211,14 +203,17 @@ func handleConnection(netConn net.Conn) { | |||||||
| 		m.Unlock() | 		m.Unlock() | ||||||
| 	}() | 	}() | ||||||
| 
 | 
 | ||||||
|  | 	// Wait for a hello packet of some sort from the client | ||||||
|  | 	// (obviously this wouldn't work in extremely high latency situations) | ||||||
| 	time.Sleep(250 * 1000000) | 	time.Sleep(250 * 1000000) | ||||||
|  | 
 | ||||||
| 	// If we still haven't received data from the client | 	// If we still haven't received data from the client | ||||||
| 	// assume that the client must be expecting a welcome from us | 	// assume that the client must be expecting a welcome from us | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	if virgin { | 	if virgin { | ||||||
| 		virgin = false | 		virgin = false | ||||||
| 		// don't block for this | 		// Defer as to not block and prolonging the mutex | ||||||
| 		// let it be handled after the unlock | 		// (not that those few cycles much matter...) | ||||||
| 		defer fmt.Fprintf(netConn, | 		defer fmt.Fprintf(netConn, | ||||||
| 			"\n\nWelcome to Sample Chat! You appear to be using Telnet (http is also available on this port)."+ | 			"\n\nWelcome to Sample Chat! You appear to be using Telnet (http is also available on this port)."+ | ||||||
| 				"\nYou must authenticate via email to participate\n\nEmail: ") | 				"\nYou must authenticate via email to participate\n\nEmail: ") | ||||||
| @ -251,7 +246,7 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) { | |||||||
| 	if nil != err { | 	if nil != err { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 	//req.PostForm = form | 	//req.PostForm = form ?? | ||||||
| 	req.Header.Add("User-Agent", "golang http.Client - Sample Chat App Authenticator") | 	req.Header.Add("User-Agent", "golang http.Client - Sample Chat App Authenticator") | ||||||
| 	req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | 	req.Header.Add("Content-Type", "application/x-www-form-urlencoded") | ||||||
| 	req.SetBasicAuth("api", cnf.ApiKey) | 	req.SetBasicAuth("api", cnf.ApiKey) | ||||||
| @ -276,8 +271,6 @@ func sendAuthCode(cnf ConfMailer, to string) (string, error) { | |||||||
| 	return code, nil | 	return code, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var config Conf |  | ||||||
| 
 |  | ||||||
| func main() { | func main() { | ||||||
| 	flag.Usage = usage | 	flag.Usage = usage | ||||||
| 	port := flag.Uint("port", 0, "tcp telnet chat port") | 	port := flag.Uint("port", 0, "tcp telnet chat port") | ||||||
| @ -295,7 +288,8 @@ func main() { | |||||||
| 		config = Conf{} | 		config = Conf{} | ||||||
| 	} | 	} | ||||||
| 	if "" == config.RootPath { | 	if "" == config.RootPath { | ||||||
| 		// TODO Embed the public dir at the default | 		// TODO Maybe embed the public dir into the binary | ||||||
|  | 		// (and provide a flag with path for override - like gitea) | ||||||
| 		config.RootPath = "./public" | 		config.RootPath = "./public" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -303,28 +297,24 @@ func main() { | |||||||
| 	virginConns = make(chan net.Conn, 128) | 	virginConns = make(chan net.Conn, 128) | ||||||
| 
 | 
 | ||||||
| 	// TCP & Authentication | 	// TCP & Authentication | ||||||
| 	telnetConns := make(map[bufferedConn]tcpUser) | 	telnetConns := make(map[bufferedConn]telnetUser) | ||||||
| 	wantsServerHello = make(chan bufferedConn, 128) | 	wantsServerHello = make(chan bufferedConn, 128) | ||||||
| 	authTelnet = make(chan tcpUser, 128) | 	authTelnet = make(chan telnetUser, 128) | ||||||
| 
 | 
 | ||||||
| 	// HTTP & Authentication | 	// HTTP & Authentication | ||||||
| 	myAuthReqs := make(map[string]authReq) | 	myAuthReqs := make(map[string]authReq) | ||||||
| 	newAuthReqs = make(chan authReq, 128) | 	authReqs = make(chan authReq, 128) | ||||||
| 	valAuthReqs = make(chan authReq, 128) | 	valAuthReqs = make(chan authReq, 128) | ||||||
| 	delAuthReqs = make(chan authReq, 128) | 	delAuthReqs = make(chan authReq, 128) | ||||||
| 	gotClientHello = make(chan bufferedConn, 128) | 	gotClientHello = make(chan bufferedConn, 128) | ||||||
| 	demuxHttpClient = make(chan bufferedConn, 128) | 	demuxHttpClient = make(chan bufferedConn, 128) | ||||||
| 
 | 
 | ||||||
| 	// cruft to delete |  | ||||||
| 	//myRooms = make(map[string](chan chatMsg)) |  | ||||||
| 
 |  | ||||||
| 	//myRooms["general"] = make(chan chatMsg, 128) | 	//myRooms["general"] = make(chan chatMsg, 128) | ||||||
| 	// Note: I had considered dynamically select on channels for rooms. | 	// Note: I had considered dynamically select on channels for rooms. | ||||||
| 	// https://stackoverflow.com/questions/19992334/how-to-listen-to-n-channels-dynamic-select-statement | 	// https://stackoverflow.com/questions/19992334/how-to-listen-to-n-channels-dynamic-select-statement | ||||||
| 	// I don't think that's actually the best approach, but I just wanted to save the link | 	// I don't think that's actually the best approach, but I just wanted to save the link | ||||||
| 
 | 
 | ||||||
| 	broadcastMsg = make(chan chatMsg, 128) | 	broadcastMsg = make(chan chatMsg, 128) | ||||||
| 	// Poor-Man's container/ring (circular buffer) |  | ||||||
| 	myChatHist.msgs = make([]*chatMsg, 128) | 	myChatHist.msgs = make([]*chatMsg, 128) | ||||||
| 
 | 
 | ||||||
| 	var addr string | 	var addr string | ||||||
| @ -391,7 +381,7 @@ func main() { | |||||||
| 		select { | 		select { | ||||||
| 		case conn := <-virginConns: | 		case conn := <-virginConns: | ||||||
| 			// This is short lived | 			// This is short lived | ||||||
| 			go handleConnection(conn) | 			go testForHello(conn) | ||||||
| 		case u := <-authTelnet: | 		case u := <-authTelnet: | ||||||
| 			// allow to receive messages | 			// allow to receive messages | ||||||
| 			// (and be counted among the users) | 			// (and be counted among the users) | ||||||
| @ -405,7 +395,7 @@ func main() { | |||||||
| 				Channel:    "general", | 				Channel:    "general", | ||||||
| 				User:       "system", | 				User:       "system", | ||||||
| 			} | 			} | ||||||
| 		case ar := <-newAuthReqs: | 		case ar := <-authReqs: | ||||||
| 			myAuthReqs[ar.Cid] = ar | 			myAuthReqs[ar.Cid] = ar | ||||||
| 		case ar := <-valAuthReqs: | 		case ar := <-valAuthReqs: | ||||||
| 			// TODO In this case it's probably more conventional (and efficient) to | 			// TODO In this case it's probably more conventional (and efficient) to | ||||||
| @ -455,7 +445,7 @@ func main() { | |||||||
| 				sender = "system" | 				sender = "system" | ||||||
| 			} | 			} | ||||||
| 			// Tangential thought: | 			// Tangential thought: | ||||||
| 			// I wonder if we could use IP detection to get the client's tz | 			// I wonder if we could use IP detection to get a Telnet client's tz | ||||||
| 			// ... could probably make time for this in the authentication loop | 			// ... could probably make time for this in the authentication loop | ||||||
| 			zone, _ := msg.ReceivedAt.Zone() | 			zone, _ := msg.ReceivedAt.Zone() | ||||||
| 			fmt.Fprintf(os.Stdout, tf+" [%s] (%s): %s\r\n", | 			fmt.Fprintf(os.Stdout, tf+" [%s] (%s): %s\r\n", | ||||||
| @ -484,23 +474,6 @@ func main() { | |||||||
| 					// It can reconnect. | 					// It can reconnect. | ||||||
| 					cleanTelnet <- u | 					cleanTelnet <- u | ||||||
| 				} | 				} | ||||||
| 
 |  | ||||||
| 				/* |  | ||||||
| 					// To ask: Why do I have to pass in conn to prevent a data race? Is it garbage collection? |  | ||||||
| 					// Don't block the rest of the loop |  | ||||||
| 					// TODONE maybe use a chan to send to the socket's event loop |  | ||||||
| 					// (left this in to remind myself to ask questions) |  | ||||||
| 					go func(conn bufferedConn) { |  | ||||||
| 						// Protect against malicious clients to prevent DoS |  | ||||||
| 						// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/ |  | ||||||
| 						timeoutDuration := 2 * time.Second |  | ||||||
| 						conn.SetWriteDeadline(time.Now().Add(timeoutDuration)) |  | ||||||
| 						_, err := fmt.Fprintf(conn, msg) |  | ||||||
| 						if nil != err { |  | ||||||
| 							cleanTelnet <- u |  | ||||||
| 						} |  | ||||||
| 					}(conn) |  | ||||||
| 				*/ |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user