package main import ( "crypto/sha256" "encoding/base64" "encoding/json" "errors" "fmt" "io" "net/http" "os" "strconv" "strings" "time" ) const TimestampFormat = "2006-01-02T15-01-05.999" type Response struct { Message string } type Request struct { User string Data string } type Message struct { User string Data string Timestamp int64 } func parseRequest(w http.ResponseWriter, r *http.Request) (Request, string, error) { var req Request body, _ := io.ReadAll(r.Body) bodyStr := string(body) parts := strings.Split(bodyStr, ".") if len(parts) != 2 { _ = badRequest(w) return req, "", errors.New("request doesn't contain exactly two parts") } payload := parts[0] signature := parts[1] requestBody, err := base64.StdEncoding.DecodeString(payload) if err != nil { return req, "", err } err = json.Unmarshal(requestBody, &req) if err != nil { _ = badRequest(w) return req, "", err } return req, signature, nil } func jsonResponse(w http.ResponseWriter, resp Response) error { data, err := json.Marshal(resp) if err != nil { return err } _, err = w.Write(data) return err } func methodNotAllowedResponse(w http.ResponseWriter) error { w.WriteHeader(http.StatusMethodNotAllowed) return jsonResponse(w, Response{ Message: "К серверу разрешены обращения только по методу POST", }) } func badRequest(w http.ResponseWriter) error { w.WriteHeader(http.StatusBadRequest) return jsonResponse(w, Response{ Message: "Не удалось обработать запрос", }) } func serverError(w http.ResponseWriter) error { w.WriteHeader(http.StatusInternalServerError) return jsonResponse(w, Response{ Message: "Произошла внутренняя ошибка", }) } func register(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { _ = methodNotAllowedResponse(w) return } req, signature, err := parseRequest(w, r) if err != nil { return } // TODO(andrew): Разобраться с процессом регистрации с подписью _ = fmt.Sprintf("%s", signature) userRegistered, err := db.checkUserRegistered(req.User) if err != nil { _ = serverError(w) return } if userRegistered { w.WriteHeader(http.StatusBadRequest) _ = jsonResponse(w, Response{ Message: "Пользователь с таким именем уже зарегистрирован", }) return } // TODO(andrew): Добавить проверку действительности ключа if false { w.WriteHeader(http.StatusBadRequest) _ = jsonResponse(w, Response{ Message: "Указанный ключ не является действительным", }) return } err = db.registerUser(req.User, req.Data) if err != nil { w.WriteHeader(http.StatusInternalServerError) _ = jsonResponse(w, Response{ Message: fmt.Sprintf("%s", err), }) return } _ = jsonResponse(w, Response{ Message: "Пользователь успешно зарегистрирован", }) } func handleAuthentication(req Request, signature string) (error, error) { userRegistered, dbError := db.checkUserRegistered(req.User) if dbError != nil { return nil, dbError } if !userRegistered { return errors.New("такой пользователь не зарегистрирован"), nil } key, err := db.getUserKey(req.User) if err != nil { return nil, err } // TODO(andrew): Добавить проверку подписи signature reqBytes, _ := json.Marshal(req) req64 := base64.StdEncoding.EncodeToString(reqBytes) h := sha256.Sum256([]byte(req64)) generatedSignature := fmt.Sprintf("%x", h) //fmt.Println(generatedSignature) //fmt.Println(signature) _ = fmt.Sprint(generatedSignature == signature) _ = fmt.Sprint(key) return nil, nil } func sendMessage(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 } msg := Message{ User: req.User, Data: req.Data, 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) _ = jsonResponse(w, Response{ Message: fmt.Sprintf("%s", err), }) return } _ = jsonResponse(w, Response{ Message: "Сообщение успешно сохранено", }) } func pollMessages(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 } timestamp, err := strconv.ParseInt(req.Data, 10, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) _ = jsonResponse(w, Response{ Message: "Указана неверная временная отметка", }) return } messages, err := db.getMessagesSince(timestamp) if err != nil { w.WriteHeader(http.StatusBadRequest) _ = jsonResponse(w, Response{ Message: fmt.Sprint(err), }) //_ = serverError(w) return } data, _ := json.Marshal(messages) _ = jsonResponse(w, Response{ Message: string(data), }) } func getUserKey(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 } userRegistered, err := db.checkUserRegistered(req.Data) if !userRegistered { w.WriteHeader(http.StatusBadRequest) _ = jsonResponse(w, Response{ Message: "Пользователь с таким именем не зарегистрирован", }) return } key, err := db.getUserKey(req.Data) if err != nil { _ = serverError(w) return } _ = jsonResponse(w, Response{ Message: key, }) } 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() { err := db.init("chat.sqlite3") if err != nil { fmt.Println(err) os.Exit(1) } http.HandleFunc("/api/register", register) 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) os.Exit(1) } }