diff options
| author | Andrew <saintruler@gmail.com> | 2021-04-28 18:09:00 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-04-28 18:09:00 +0400 |
| commit | 7d6270f64b1dc00d91230b5c793bc49991f0fcf8 (patch) | |
| tree | ee41d9e909968d56a83a09cf83fbb6e5ff6dbb1c | |
| parent | 355dab5bdc924202b1d877b18ae31f04154a4aee (diff) | |
Added http client and tryAuth view to server.
| -rw-r--r-- | http-client/client.go | 106 | ||||
| -rw-r--r-- | http-client/go.mod | 5 | ||||
| -rw-r--r-- | http-client/main.go | 155 | ||||
| -rw-r--r-- | http-server/main.go | 48 |
4 files changed, 310 insertions, 4 deletions
diff --git a/http-client/client.go b/http-client/client.go new file mode 100644 index 0000000..ee3e044 --- /dev/null +++ b/http-client/client.go @@ -0,0 +1,106 @@ +package main + +import ( + "bytes" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "fmt" + "fyne.io/fyne/v2/data/binding" + "io" + "net/http" + "sync" + "time" +) + +const ( + URL = "http://localhost:8080/api" + Register = "register" + SendMessage = "sendMessage" + PollMessages = "pollMessages" + GetUserKey = "getUserKey" + TryAuth = "tryAuth" +) + +const TimestampFormat = "2006-01-02T15-01-05.999" + +var storage struct { + sync.RWMutex + messages []Message + data []string + binding binding.ExternalStringList +} + +type Request struct { + User string + Data string +} + +type Response struct { + Message string +} + +type Message struct { + User string + Data string + Timestamp string +} + +func (msg *Message) toString() string { + return fmt.Sprintf("%s: %s", msg.User, msg.Data) +} + +func signData(data []byte) string { + data64 := base64.StdEncoding.EncodeToString(data) + h := sha256.Sum256([]byte(data64)) + signature := fmt.Sprintf("%x", h) + return fmt.Sprintf("%s.%s", data64, signature) +} + +func makeRequest(request Request, apiMethod string) (*http.Response, error) { + req, _ := json.Marshal(request) + signedRequest := signData(req) + reader := bytes.NewReader([]byte(signedRequest)) + return http.Post(URL+"/"+apiMethod, "application/json", reader) +} + +func sendMessage(user UserData, message string) { + // TODO(andrew): Добавить отображение ошибки в интерфейс + _, _ = makeRequest(Request{ + User: user.Username, + Data: message, + }, SendMessage) +} + +func runClient(user UserData) { + lastPoll := time.Now().UnixNano() + for { + httpResp, _ := makeRequest(Request{ + User: user.Username, + Data: fmt.Sprint(lastPoll), + }, PollMessages) + lastPoll = time.Now().UnixNano() + + if httpResp.StatusCode == http.StatusOK { + body, _ := io.ReadAll(httpResp.Body) + var resp Response + _ = json.Unmarshal(body, &resp) + var messages []Message + _ = json.Unmarshal([]byte(resp.Message), &messages) + + storage.Lock() + for _, msg := range messages { + fmt.Printf("Polled new message from %s: %s (%s)\n", msg.User, msg.Data, msg.Timestamp) + _ = storage.binding.Append(msg.toString()) + storage.messages = append(storage.messages, msg) + } + storage.Unlock() + } else { + fmt.Println(httpResp.StatusCode) + ae, _ := io.ReadAll(httpResp.Body) + fmt.Println(string(ae)) + } + + time.Sleep(100 * time.Millisecond) + } +} diff --git a/http-client/go.mod b/http-client/go.mod new file mode 100644 index 0000000..a62b9f0 --- /dev/null +++ b/http-client/go.mod @@ -0,0 +1,5 @@ +module vasthecat.ru/coursework/http-client + +go 1.16 + +require fyne.io/fyne/v2 v2.0.2 diff --git a/http-client/main.go b/http-client/main.go new file mode 100644 index 0000000..14419e5 --- /dev/null +++ b/http-client/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/data/binding" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/widget" + "image/color" +) + +type UserData struct { + Username string +} + +func chatLayout(window fyne.Window, user UserData, c chan string) { + window.SetTitle("Сообщения") + storage.binding = binding.BindStringList(&storage.data) + go runClient(user) + + list := widget.NewListWithData(storage.binding, + func() fyne.CanvasObject { + return widget.NewLabel("template") + }, + func(i binding.DataItem, o fyne.CanvasObject) { + o.(*widget.Label).Bind(i.(binding.String)) + }) + + entry := widget.NewEntry() + submit := widget.NewButton("Отправить", func() { + go sendMessage(user, entry.Text) + entry.SetText("") + + storage.RLock() + lastIdx := storage.binding.Length() - 1 + list.Select(lastIdx) + list.Unselect(lastIdx) + storage.RUnlock() + }) + + window.SetContent(container.NewBorder( + nil, + container.NewBorder(nil, nil, nil, submit, entry), + nil, nil, + list, + )) +} + +func loginLayout(window fyne.Window, c chan string) { + window.SetTitle("Войти в чат") + + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 + + loginLabel := widget.NewLabel("Логин: ") + loginEntry := widget.NewEntry() + + chooseKey := widget.NewButton("Выбрать ключ для подписи", func() { + dialog.ShowFileOpen(func(closer fyne.URIReadCloser, err error) { + if closer == nil { + return + } + bufSize := 4096 + data := make([]byte, 0) + buf := make([]byte, bufSize) + n := bufSize + + for n >= bufSize { + n, _ = closer.Read(buf) + data = append(data, buf...) + } + + _ = closer.Close() + }, window) + }) + + loginButton := widget.NewButton("Войти", func() { + var user = UserData{ + Username: loginEntry.Text, + } + chatLayout(window, user, c) + }) + + back := widget.NewButton("Назад", func() { + startLayout(window, c) + }) + + mainContainer := container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), + container.NewBorder(nil, nil, loginLabel, nil, loginEntry), + chooseKey, + loginButton, + back, + )) + window.SetContent(mainContainer) +} + +func registerLayout(window fyne.Window, c chan string) { + window.SetTitle("Регистрация") + + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 + + loginLabel := widget.NewLabel("Логин: ") + loginEntry := widget.NewEntry() + + generateKeys := widget.NewButton("Сгенерировать ключи подписи", func() { + + }) + + registerButton := widget.NewButton("Зарегистрироваться", func() { + + }) + + back := widget.NewButton("Назад", func() { + startLayout(window, c) + }) + + window.SetContent(container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), + container.NewBorder(nil, nil, loginLabel, nil, loginEntry), + generateKeys, + registerButton, + back, + ))) +} + +func startLayout(window fyne.Window, c chan string) { + window.SetTitle("Chat") + + label := canvas.NewText("Chat", color.White) + label.TextSize = 40 + login := widget.NewButton("Войти", func() { + loginLayout(window, c) + }) + signup := widget.NewButton("Зарегистрироваться", func() { + registerLayout(window, c) + }) + + mainContainer := container.NewCenter(container.NewVBox( + container.NewCenter(label), layout.NewSpacer(), login, signup)) + window.SetContent(mainContainer) +} + +func main() { + myApp := app.New() + window := myApp.NewWindow("") + channel := make(chan string) + + startLayout(window, channel) + window.ShowAndRun() +} diff --git a/http-server/main.go b/http-server/main.go index e2402b2..f77c32f 100644 --- a/http-server/main.go +++ b/http-server/main.go @@ -14,6 +14,8 @@ import ( "time" ) +const TimestampFormat = "2006-01-02T15-01-05.999" + type Response struct { Message string } @@ -157,8 +159,10 @@ func handleAuthentication(req Request, signature string) (error, error) { req64 := base64.StdEncoding.EncodeToString(reqBytes) h := sha256.Sum256([]byte(req64)) generatedSignature := fmt.Sprintf("%x", h) - fmt.Println(generatedSignature == signature) - fmt.Println(key) + //fmt.Println(generatedSignature) + //fmt.Println(signature) + _ = fmt.Sprint(generatedSignature == signature) + _ = fmt.Sprint(key) return nil, nil } @@ -190,9 +194,11 @@ func sendMessage(w http.ResponseWriter, r *http.Request) { msg := Message{ User: req.User, Data: req.Data, - Timestamp: time.Now().Unix(), + Timestamp: time.Now().UnixNano(), } + fmt.Printf("Got message from %s: %s\n", req.User, req.Data) + err = db.saveMessage(msg) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -242,7 +248,11 @@ func pollMessages(w http.ResponseWriter, r *http.Request) { messages, err := db.getMessagesSince(timestamp) if err != nil { - _ = serverError(w) + w.WriteHeader(http.StatusBadRequest) + _ = jsonResponse(w, Response{ + Message: fmt.Sprint(err), + }) + //_ = serverError(w) return } @@ -296,6 +306,35 @@ func getUserKey(w http.ResponseWriter, r *http.Request) { }) } +func tryAuth(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + _ = methodNotAllowedResponse(w) + return + } + + req, signature, err := parseRequest(w, r) + if err != nil { + return + } + + authErr, dbErr := handleAuthentication(req, signature) + if authErr != nil { + w.WriteHeader(http.StatusForbidden) + _ = jsonResponse(w, Response{ + Message: "Запрос не прошёл аутентификацию", + }) + return + } + if dbErr != nil { + _ = serverError(w) + return + } + + _ = jsonResponse(w, Response{ + Message: "Запрос прошёл аутентификацию", + }) +} + var db SQLConnection func main() { @@ -309,6 +348,7 @@ func main() { http.HandleFunc("/api/sendMessage", sendMessage) http.HandleFunc("/api/pollMessages", pollMessages) http.HandleFunc("/api/getUserKey", getUserKey) + http.HandleFunc("/api/tryAuth", tryAuth) err = http.ListenAndServe("localhost:8080", nil) if err != nil { fmt.Println(err) |