init
						commit
						178a583e76
					
				| @ -0,0 +1,36 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	testclient "testclient/internal" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  | 	if err := testclient.InitHttpClient(); err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error initialising http client", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	if err := testclient.Initialise(); err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "Error setting up tester:", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	testclient.Register() | ||||||
|  | 	testclient.WsConnect() | ||||||
|  | 
 | ||||||
|  | 	if testclient.LoadMessages { | ||||||
|  | 		testclient.LoadDefaultMessages() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fmt.Println("username:", testclient.Username) | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		select { | ||||||
|  | 		case <-testclient.Done: | ||||||
|  | 			fmt.Println("websocket connection closed") | ||||||
|  | 			return | ||||||
|  | 		case l := <-testclient.Line: | ||||||
|  | 			testclient.SendMessage(l) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | module testclient | ||||||
|  | 
 | ||||||
|  | go 1.18 | ||||||
|  | 
 | ||||||
|  | require github.com/gorilla/websocket v1.5.0 | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= | ||||||
|  | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||||
| @ -0,0 +1,151 @@ | |||||||
|  | package testclient | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"math/rand" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var chatAddress = "localhost:8000" | ||||||
|  | var loginAddress = "localhost:8001" | ||||||
|  | var rate = 1000 | ||||||
|  | var wordPerMessage = 3 | ||||||
|  | var refresh = 240 | ||||||
|  | var listen bool | ||||||
|  | var autoSend bool | ||||||
|  | var stdinSend bool | ||||||
|  | var LoadMessages bool | ||||||
|  | var debug bool | ||||||
|  | var serverName string | ||||||
|  | var test bool | ||||||
|  | 
 | ||||||
|  | var wordsSource = "https://gist.githubusercontent.com/deekayen/4148741/raw/98d35708fa344717d8eee15d11987de6c8e26d7d/1-1000.txt" | ||||||
|  | var wordsList []string | ||||||
|  | var rng *rand.Rand | ||||||
|  | 
 | ||||||
|  | var Username string | ||||||
|  | var token string | ||||||
|  | var session string | ||||||
|  | 
 | ||||||
|  | var Done chan (struct{}) | ||||||
|  | var Line chan string | ||||||
|  | 
 | ||||||
|  | func debugPrint(args ...any) { | ||||||
|  | 	if debug { | ||||||
|  | 		fmt.Print("DEBUG: ") | ||||||
|  | 		fmt.Println(args...) | ||||||
|  | 		fmt.Println() // spacer
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Initialise() error { | ||||||
|  | 	parseArgs() | ||||||
|  | 
 | ||||||
|  | 	if test { | ||||||
|  | 		listen, autoSend, LoadMessages, debug = true, true, true, true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if list, err := buildWordsList(wordsSource); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else { | ||||||
|  | 		wordsList = list | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rng = rand.New(rand.NewSource(time.Now().Unix())) | ||||||
|  | 	generateUsername() | ||||||
|  | 
 | ||||||
|  | 	if stdinSend { | ||||||
|  | 		go sendFromStdin() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	go manageToken() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func generateUsername() { | ||||||
|  | 	Username = wordsList[rng.Intn(len(wordsList))] + "_" + wordsList[rng.Intn(len(wordsList))] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func parseArgs() { | ||||||
|  | 	flag.StringVar(&chatAddress, "chataddr", chatAddress, "address for chat server") | ||||||
|  | 	flag.StringVar(&loginAddress, "loginaddr", loginAddress, "address for login server") | ||||||
|  | 	flag.StringVar(&serverName, "server", serverName, "server name (blank for main)") | ||||||
|  | 	flag.IntVar(&rate, "rate", rate, "time in ms between messages") | ||||||
|  | 	flag.IntVar(&wordPerMessage, "wpm", wordPerMessage, "words per message") | ||||||
|  | 	flag.BoolVar(&listen, "listen", listen, "listen and echo every message received") | ||||||
|  | 	flag.BoolVar(&autoSend, "autosend", autoSend, "auto generate and send messages of length and rate specified") | ||||||
|  | 	flag.BoolVar(&LoadMessages, "load", LoadMessages, "load most recent messages when connecting to chat") | ||||||
|  | 	flag.BoolVar(&debug, "debug", debug, "print additional debug messages e.g. http responses, auth token") | ||||||
|  | 	flag.IntVar(&refresh, "refresh", refresh, "auth token refresh rate in seconds") | ||||||
|  | 	flag.BoolVar(&stdinSend, "send", stdinSend, "send messages from CLI") | ||||||
|  | 	flag.BoolVar(&test, "test", test, "test all functionality (implies -listen, -autosend, -load, -debug)") | ||||||
|  | 
 | ||||||
|  | 	flag.Parse() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func createMessage() (message string) { | ||||||
|  | 	for i := 0; i < wordPerMessage; i++ { | ||||||
|  | 		message += wordsList[rng.Intn(len(wordsList))] + " " | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func buildWordsList(url string) (wordsList []string, err error) { | ||||||
|  | 	res, err := http.Get(url) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	wordsList = make([]string, 0) | ||||||
|  | 	body, err := ioutil.ReadAll(res.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error reading response body:", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var word []byte | ||||||
|  | 	for _, char := range body { | ||||||
|  | 		if char == '\r' { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if char == '\n' { | ||||||
|  | 			wordsList = append(wordsList, string(word)) | ||||||
|  | 			word = make([]byte, 0) | ||||||
|  | 		} else { | ||||||
|  | 			word = append(word, char) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func sendFromStdin() { | ||||||
|  | 	defer func() { | ||||||
|  | 		fmt.Println("stopped reading from stdin") | ||||||
|  | 	}() | ||||||
|  | 
 | ||||||
|  | 	Line = make(chan string) | ||||||
|  | 
 | ||||||
|  | 	reader := bufio.NewReader(os.Stdin) | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		l, _, err := reader.ReadLine() | ||||||
|  | 		if err != nil { | ||||||
|  | 			fmt.Println(err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		Line <- string(l) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func manageToken() { | ||||||
|  | 	ticker := time.NewTicker(time.Second * time.Duration(refresh)) | ||||||
|  | 
 | ||||||
|  | 	for { | ||||||
|  | 		<-ticker.C | ||||||
|  | 		refreshToken() | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,200 @@ | |||||||
|  | package testclient | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/cookiejar" | ||||||
|  | 	"net/url" | ||||||
|  | 	"os" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gorilla/websocket" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type TokenObj struct { | ||||||
|  | 	Token string `json:"session"` | ||||||
|  | } | ||||||
|  | type SessionObj struct { | ||||||
|  | 	Session string `json:"session"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var client *http.Client | ||||||
|  | var conn *websocket.Conn | ||||||
|  | 
 | ||||||
|  | func InitHttpClient() (err error) { | ||||||
|  | 	jar, err := cookiejar.New(nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error initialising cookie jar", err.Error()) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	client = &http.Client{ | ||||||
|  | 		Jar: jar, | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func Register() error { | ||||||
|  | 	postBody := []byte(`{"username":"` + Username + `","password":"Password1!"}`) | ||||||
|  | 	req, err := http.NewRequest("POST", "http://"+loginAddress+"/register", bytes.NewBuffer(postBody)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, err.Error()) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	res, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "could not reach login server", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	if res.StatusCode != http.StatusCreated { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "Status code", res.StatusCode, "received") | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	body, err := ioutil.ReadAll(res.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error reading register body:", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debugPrint("sent ", string(postBody), "received", res.StatusCode, "from", req.URL, "with response body", string(body)) | ||||||
|  | 
 | ||||||
|  | 	type TokenObj struct { | ||||||
|  | 		Token string `json:"token"` | ||||||
|  | 	} | ||||||
|  | 	var tokenObj TokenObj | ||||||
|  | 
 | ||||||
|  | 	if err := json.Unmarshal(body, &tokenObj); err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error unmarshalling token:", err.Error()) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	token = tokenObj.Token | ||||||
|  | 
 | ||||||
|  | 	debugPrint("initial token set to", token) | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func WsConnect() { | ||||||
|  |     u := url.URL{Scheme: "ws", Host: chatAddress, Path: "/connect", RawQuery: "name=" + serverName} | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  |     header := http.Header{} | ||||||
|  |     header.Set("Token", token) | ||||||
|  | 
 | ||||||
|  |     conn, _, err = websocket.DefaultDialer.Dial(u.String(), header) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error connecting to ws: ", err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	Done = make(chan struct{}) | ||||||
|  | 
 | ||||||
|  | 	debugPrint("connected to websocket server", serverName, "(blank for root) at", u.String()) | ||||||
|  | 
 | ||||||
|  | 	go func() { | ||||||
|  | 		defer conn.Close() | ||||||
|  | 		for { | ||||||
|  | 			_, mess, err := conn.ReadMessage() | ||||||
|  | 			if err != nil { | ||||||
|  | 				fmt.Println(err) | ||||||
|  | 				Done <- struct{}{} | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if listen { | ||||||
|  | 				fmt.Println(string(mess)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 
 | ||||||
|  | 	if autoSend { | ||||||
|  | 		go func() { | ||||||
|  | 			t := time.NewTicker(time.Millisecond * time.Duration(rate)) | ||||||
|  | 			for { | ||||||
|  | 				<-t.C | ||||||
|  | 				SendMessage(createMessage()) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func SendMessage(message string) { | ||||||
|  | 	mess := []byte(`{"text":"` + message + `","user":"` + token + `"}`) | ||||||
|  | 
 | ||||||
|  | 	req, err := http.NewRequest("POST", "http://"+chatAddress+"/send?name="+serverName, bytes.NewBuffer(mess)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "creating post request error:", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	req.Header.Set("Token", token) | ||||||
|  | 	res, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "sending post request error:", err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if res.StatusCode != http.StatusOK { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "received", res.StatusCode, "when sending message") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debugPrint("sent", message, "received", res.StatusCode) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func LoadDefaultMessages() { | ||||||
|  | 	req, err := http.NewRequest("GET", "http://"+chatAddress+"/messages?name="+serverName, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "creating get request error:", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	req.Header.Set("Token", token) | ||||||
|  | 	res, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "sending get request error:", err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if res.StatusCode != http.StatusOK { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "received", res.StatusCode, "when loading message") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	body, err := ioutil.ReadAll(res.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error reading response body:", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	debugPrint("load messages: status", res.StatusCode, "body:", string(body)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func refreshToken() { | ||||||
|  | 	req, err := http.NewRequest("GET", "http://"+loginAddress+"/auth", nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "creating get request error:", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	res, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "could not reach login server", err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	body, err := ioutil.ReadAll(res.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error reading auth response body:", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if res.StatusCode != http.StatusOK { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "/auth received status code", res.StatusCode, "with body", string(body)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	type TokenObj struct { | ||||||
|  | 		Token string `json:"token"` | ||||||
|  | 	} | ||||||
|  | 	var tokenObj TokenObj | ||||||
|  | 
 | ||||||
|  | 	if err := json.Unmarshal(body, &tokenObj); err != nil { | ||||||
|  | 		fmt.Fprintln(os.Stderr, "error unmarshalling token:", err.Error()) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	token = tokenObj.Token | ||||||
|  | 	debugPrint("refreshing token to", token) | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in New Issue