diff --git a/client/client.go b/client/client.go index ff89631..5923173 100644 --- a/client/client.go +++ b/client/client.go @@ -95,18 +95,26 @@ func (c *Client) GetOperations() (map[string]*Operation, error) { return c.state.GetOperations() } -// GetOperationQRPath returns a path to the image with the QR generated -// for the specified operation. It is supposed that the user will open -// this file herself. -func (c *Client) GetOperationQRPath(operationID string) (string, error) { +func (c *Client) getOperationJSON(operationID string) ([]byte, error) { operation, err := c.state.GetOperationByID(operationID) if err != nil { - return "", fmt.Errorf("failed to get operation: %w", err) + return nil, fmt.Errorf("failed to get operation: %w", err) } operationJSON, err := json.Marshal(operation) if err != nil { - return "", fmt.Errorf("failed to marshal operation: %w", err) + return nil, fmt.Errorf("failed to marshal operation: %w", err) + } + return operationJSON, nil +} + +// GetOperationQRPath returns a path to the image with the QR generated +// for the specified operation. It is supposed that the user will open +// this file herself. +func (c *Client) GetOperationQRPath(operationID string) (string, error) { + operationJSON, err := c.getOperationJSON(operationID) + if err != nil { + return "", fmt.Errorf("failed to get operation in JSON: %w", err) } operationQRPath := filepath.Join(QrCodesDir, operationID) @@ -131,6 +139,10 @@ func (c *Client) ReadProcessedOperation() error { return fmt.Errorf("failed to unmarshal processed operation") } + return c.handleProcessedOperation(operation) +} + +func (c *Client) handleProcessedOperation(operation Operation) error { storedOperation, err := c.state.GetOperationByID(operation.ID) if err != nil { return fmt.Errorf("failed to find matching operation: %w", err) diff --git a/client/http_server.go b/client/http_server.go new file mode 100644 index 0000000..8170d28 --- /dev/null +++ b/client/http_server.go @@ -0,0 +1,179 @@ +package client + +import ( + "encoding/json" + "fmt" + "image" + "io/ioutil" + "log" + "net/http" + + "github.com/depools/dc4bc/qr" + "github.com/depools/dc4bc/storage" +) + +func errorResponse(w http.ResponseWriter, statusCode int, err string) { + log.Println(err) + w.WriteHeader(statusCode) + if _, err := w.Write([]byte(err)); err != nil { + panic(fmt.Sprintf("failed to write response: %v", err)) + } +} + +func successResponse(w http.ResponseWriter, response []byte) { + if _, err := w.Write(response); err != nil { + panic(fmt.Sprintf("failed to write response: %v", err)) + } +} + +func (c *Client) StartHTTPServer(listenAddr string) error { + http.HandleFunc("/sendMessage", c.sendMessageHandler) + http.HandleFunc("/getOperations", c.getOperationsHandler) + http.HandleFunc("/getOperationQRPath", c.getOperationQRPathHandler) + http.HandleFunc("/readProcessedOperationFromCamera", c.readProcessedOperationFromCameraHandler) + + http.HandleFunc("/readProcessedOperation", c.readProcessedOperationFromBodyHandler) + http.HandleFunc("/getOperationQR", c.getOperationQRToBodyHandler) + return http.ListenAndServe(listenAddr, nil) +} + +func (c *Client) sendMessageHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + reqBytes, err := ioutil.ReadAll(r.Body) + if err != nil { + errorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to read request body: %v", err)) + return + } + + var msg storage.Message + if err = json.Unmarshal(reqBytes, &msg); err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to unmarshal message: %v", err)) + return + } + + if err = c.SendMessage(msg); err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to send message to the storage: %v", err)) + return + } + + successResponse(w, []byte("ok")) +} + +func (c *Client) getOperationsHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + + operations, err := c.GetOperations() + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operations: %v", err)) + return + } + + response, err := json.Marshal(operations) + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to marshal operations: %v", err)) + return + } + + successResponse(w, response) +} + +func (c *Client) getOperationQRPathHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + operationID := r.URL.Query().Get("operationID") + + qrPath, err := c.GetOperationQRPath(operationID) + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operation QR path: %v", err)) + return + } + + successResponse(w, []byte(qrPath)) +} + +func (c *Client) getOperationQRToBodyHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + operationID := r.URL.Query().Get("operationID") + + operationJSON, err := c.getOperationJSON(operationID) + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to get operation in JSON: %v", err)) + return + } + + encodedData, err := qr.EncodeQR(operationJSON) + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to encode operation: %v", err)) + return + } + + w.Header().Set("Content-Type", "image/jpeg") + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(encodedData))) + successResponse(w, encodedData) +} + +func (c *Client) readProcessedOperationFromCameraHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + + if err := c.ReadProcessedOperation(); err != nil { + errorResponse(w, http.StatusInternalServerError, + fmt.Sprintf("failed to handle processed operation from camera path: %v", err)) + return + } + + successResponse(w, []byte("ok")) +} + +func (c *Client) readProcessedOperationFromBodyHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + errorResponse(w, http.StatusBadRequest, "Wrong HTTP method") + return + } + + if err := r.ParseMultipartForm(10 << 20); err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to parse multipat form: %v", err)) + return + } + + file, _, err := r.FormFile("qr") + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to retrieve a file: %v", err)) + return + } + defer file.Close() + img, _, err := image.Decode(file) + if err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to decode an image: %v", err)) + return + } + + qrData, err := qr.ReadDataFromQR(img) + if err != nil { + return + } + + var operation Operation + if err = json.Unmarshal(qrData, &operation); err != nil { + errorResponse(w, http.StatusInternalServerError, + fmt.Sprintf("failed to unmarshal processed operation: %v", err)) + return + } + if err := c.handleProcessedOperation(operation); err != nil { + errorResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to handle an operation: %v", err)) + return + } +} diff --git a/fsm/cmd/test/test.go b/fsm/cmd/test/test.go index 2379dd3..42fa7ba 100644 --- a/fsm/cmd/test/test.go +++ b/fsm/cmd/test/test.go @@ -1,9 +1,10 @@ package main import ( + "log" + "github.com/depools/dc4bc/fsm/state_machines" "github.com/depools/dc4bc/fsm/types/requests" - "log" ) func main() { diff --git a/fsm/fsm/fsm_machines_data_test.go b/fsm/fsm/fsm_machines_data_test.go index 46dc196..5aa2dba 100644 --- a/fsm/fsm/fsm_machines_data_test.go +++ b/fsm/fsm/fsm_machines_data_test.go @@ -8,6 +8,7 @@ const ( FSM1Name = "fsm1" // Init process from global idle state FSM1StateInit = StateGlobalIdle + FSM2StateInit = StateGlobalIdle // Set up data FSM1StateStage1 = State("state_fsm1_stage1") // Process data diff --git a/fsm/fsm_pool/fsm_pool.go b/fsm/fsm_pool/fsm_pool.go index bba34b4..6bbe288 100644 --- a/fsm/fsm_pool/fsm_pool.go +++ b/fsm/fsm_pool/fsm_pool.go @@ -2,6 +2,7 @@ package fsm_pool import ( "errors" + "github.com/depools/dc4bc/fsm/fsm" ) diff --git a/fsm/state_machines/provider.go b/fsm/state_machines/provider.go index 185bbf0..8bcebc2 100644 --- a/fsm/state_machines/provider.go +++ b/fsm/state_machines/provider.go @@ -3,6 +3,7 @@ package state_machines import ( "encoding/json" "errors" + "github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/fsm_pool" "github.com/depools/dc4bc/fsm/state_machines/internal" diff --git a/fsm/state_machines/signature_proposal_fsm/actions.go b/fsm/state_machines/signature_proposal_fsm/actions.go index 0a1d272..190a49c 100644 --- a/fsm/state_machines/signature_proposal_fsm/actions.go +++ b/fsm/state_machines/signature_proposal_fsm/actions.go @@ -2,11 +2,12 @@ package signature_proposal_fsm import ( "errors" + "log" + "github.com/depools/dc4bc/fsm/fsm" "github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/types/requests" "github.com/depools/dc4bc/fsm/types/responses" - "log" ) // init -> awaitingConfirmations diff --git a/fsm/state_machines/signature_proposal_fsm/helpers.go b/fsm/state_machines/signature_proposal_fsm/helpers.go index 452a7ee..4954b81 100644 --- a/fsm/state_machines/signature_proposal_fsm/helpers.go +++ b/fsm/state_machines/signature_proposal_fsm/helpers.go @@ -3,9 +3,10 @@ package signature_proposal_fsm import ( "crypto/sha256" "encoding/base64" + "math/rand" + "github.com/depools/dc4bc/fsm/state_machines/internal" "github.com/depools/dc4bc/fsm/types/responses" - "math/rand" ) // Request and response mutators diff --git a/fsm/types/requests/signature_proposal.go b/fsm/types/requests/signature_proposal.go index dc6b72c..c6a5b10 100644 --- a/fsm/types/requests/signature_proposal.go +++ b/fsm/types/requests/signature_proposal.go @@ -2,6 +2,7 @@ package requests import ( "errors" + "github.com/depools/dc4bc/fsm/config" ) diff --git a/mocks/clientMocks/state_mock.go b/mocks/clientMocks/state_mock.go index 04db683..2a5c74f 100644 --- a/mocks/clientMocks/state_mock.go +++ b/mocks/clientMocks/state_mock.go @@ -5,9 +5,10 @@ package clientMocks import ( + reflect "reflect" + client "github.com/depools/dc4bc/client" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) // MockState is a mock of State interface diff --git a/mocks/storageMocks/storage_mock.go b/mocks/storageMocks/storage_mock.go index 4fcf0d9..642fae5 100644 --- a/mocks/storageMocks/storage_mock.go +++ b/mocks/storageMocks/storage_mock.go @@ -5,9 +5,10 @@ package storageMocks import ( + reflect "reflect" + storage "github.com/depools/dc4bc/storage" gomock "github.com/golang/mock/gomock" - reflect "reflect" ) // MockStorage is a mock of Storage interface diff --git a/qr/qr.go b/qr/qr.go index 454651b..b264437 100644 --- a/qr/qr.go +++ b/qr/qr.go @@ -2,6 +2,7 @@ package qr import ( "fmt" + "image" "log" "time" @@ -66,18 +67,7 @@ loop: return nil, fmt.Errorf("failed to get image object: %w", err) } - bmp, err := gozxing.NewBinaryBitmapFromImage(imgObject) - if err != nil { - return nil, fmt.Errorf("failed to get NewBinaryBitmapFromImage: %w", err) - } - - qrReader := qrcode.NewQRCodeReader() - result, err := qrReader.Decode(bmp, nil) - if err != nil { - return nil, fmt.Errorf("failed to decode the QR-code contents: %w", err) - } - - return result.GetRawBytes(), err + return ReadDataFromQR(imgObject) } func (p *CameraProcessor) WriteQR(path string, data []byte) error { @@ -88,3 +78,21 @@ func (p *CameraProcessor) WriteQR(path string, data []byte) error { return nil } + +func ReadDataFromQR(img image.Image) ([]byte, error) { + bmp, err := gozxing.NewBinaryBitmapFromImage(img) + if err != nil { + return nil, fmt.Errorf("failed to get NewBinaryBitmapFromImage: %w", err) + } + + qrReader := qrcode.NewQRCodeReader() + result, err := qrReader.Decode(bmp, nil) + if err != nil { + return nil, fmt.Errorf("failed to decode the QR-code contents: %w", err) + } + return result.GetRawBytes(), nil +} + +func EncodeQR(data []byte) ([]byte, error) { + return encoder.Encode(string(data), encoder.Medium, 512) +}