package rest_websocket import ( "bytes" "io" "time" "git.ali33.ru/fcg-xvii/go-tools/json" "git.ali33.ru/fcg-xvii/rest" ) // Int64ToBytes упаковывает int64 в срез байтов заданной длины func Int64ToBytes(num int64, byteCount int) []byte { bytes := make([]byte, byteCount) for i := 0; i < byteCount; i++ { shift := uint((byteCount - 1 - i) * 8) bytes[i] = byte(num >> shift) } return bytes } // BytesToInt64 конвертирует срез байтов в int64 func BytesToInt64(bytes []byte) int64 { var num int64 for _, b := range bytes { num = (num << 8) | int64(b) } return num } func ioError(field string, err error) rest.IErrorArgs { return rest.NewError( "ErrIO", json.Map{ "field": field, "error": err.Error(), }, ) } func ReadMessage(r io.Reader) (*Message, rest.IErrorArgs) { // todo // id sType := make([]byte, 1) if _, err := r.Read(sType); err != nil { return nil, ioError("type", err) } mes := Message{ mType: rest.RequestType(sType[0]), } if mes.mType == rest.RequestTypeMessage || mes.mType == rest.RequestTypeAnswer { sID := make([]byte, 2) if _, err := r.Read(sID); err != nil { return nil, ioError("id", err) } mes.id = BytesToInt64(sID) } if mes.mType == rest.RequestTypeMessage { sTimeout := make([]byte, 8) if _, err := r.Read(sTimeout); err != nil { return nil, ioError("timeout", err) } mes.timeout = time.Unix(BytesToInt64(sTimeout), 0) } sCommandSize := make([]byte, 2) if _, err := r.Read(sCommandSize); err != nil { return nil, ioError("data_size", err) } if BytesToInt64(sCommandSize) > 0 { sCommand := make([]byte, BytesToInt64(sCommandSize)) if _, err := r.Read(sCommand); err != nil { return nil, ioError("data", err) } mes.command = string(sCommand) } sDataSize := make([]byte, 8) if _, err := r.Read(sDataSize); err != nil { return nil, ioError("data_size", err) } sData := make([]byte, BytesToInt64(sDataSize)) if _, err := r.Read(sData); err != nil { return nil, ioError("data", err) } if len(sData) > 0 { if err := json.Unmarshal(sData, &mes.data); err != nil { return nil, ioError("data", err) } } sFilesCount := make([]byte, 2) if _, err := r.Read(sFilesCount); err != nil { return nil, ioError("files_count", err) } filesCount := BytesToInt64(sFilesCount) files := make(map[string]rest.IReadCloserLen) for i := 0; i < int(filesCount); i++ { sFileNameLen := make([]byte, 2) if _, err := r.Read(sFileNameLen); err != nil { return nil, ioError("file_name_length", err) } sFileName := make([]byte, BytesToInt64(sFileNameLen)) if _, err := r.Read(sFileName); err != nil { return nil, ioError("file_name", err) } sFileSize := make([]byte, 8) if _, err := r.Read(sFileSize); err != nil { return nil, ioError("file_size", err) } fileSize := BytesToInt64(sFileSize) if fileSize < 1024*1024 { // RAM buffer sFileData := make([]byte, fileSize) if _, err := r.Read(sFileData); err != nil { return nil, ioError("file_data", err) } buf := rest.NewReadCloserLen( io.NopCloser(bytes.NewBuffer(sFileData)), int64(len(sFileData)), ) files[string(sFileName)] = buf } else { // temporary file tmpF, err := rest.NewTemporaryFile(fileSize, r) if err != nil { return nil, err } files[string(sFileName)] = tmpF } } return &mes, nil } func NewMessage(command string, data json.Map, files map[string]rest.IReadCloserLen, timeout time.Duration, mType rest.RequestType) *Message { return &Message{ command: command, data: data, files: files, mType: mType, timeout: time.Now().Add(timeout), } } type Message struct { id int64 command string mType rest.RequestType timeout time.Time data json.Map files map[string]rest.IReadCloserLen owner *WebSocket } func (s *Message) Data() json.Map { return s.data } func (s *Message) File(name string) (rest.IReadCloserLen, bool) { file, check := s.files[name] return file, check } func (s *Message) FileKeys() []string { keys := make([]string, 0, len(s.files)) for k := range s.files { keys = append(keys, k) } return keys } func (s *Message) Command() string { return s.command } func (s *Message) IsBinagy() bool { return len(s.files) > 0 } func (s *Message) Write(w io.Writer) rest.IErrorArgs { // id if _, err := w.Write(Int64ToBytes(s.id, 2)); err != nil { return ioError("id", err) } // type if _, err := w.Write(Int64ToBytes(int64(s.mType), 1)); err != nil { return ioError("type", err) } // timeout if _, err := w.Write(Int64ToBytes(s.timeout.Unix(), 8)); err != nil { return ioError("timeout", err) } // command length if _, err := w.Write(Int64ToBytes(int64(len(s.command)), 2)); err != nil { return ioError("command_length", err) } // command if len(s.command) > 0 { if _, err := w.Write([]byte(s.command)); err != nil { return ioError("command", err) } } // data data := s.data.JSON() // data size if _, err := w.Write(Int64ToBytes(int64(len(data)), 8)); err != nil { return ioError("data_size", err) } // data body if _, err := w.Write(data); err != nil { return ioError("data_body", err) } // files count filesCount := int64(len(s.files)) if _, err := w.Write(Int64ToBytes(filesCount, 2)); err != nil { return ioError("files_count", err) } // files for name, file := range s.files { // file name size fileNameSize := int64(len(name)) if _, err := w.Write(Int64ToBytes(fileNameSize, 2)); err != nil { return ioError("file_name_size", err) } // file name if _, err := w.Write([]byte(name)); err != nil { return ioError("file_name", err) } // file body size if _, err := w.Write(Int64ToBytes(file.Len(), 8)); err != nil { return ioError("file_body_size", err) } // file body if _, err := io.Copy(w, file); err != nil { return ioError("file_body", err) } } return nil } func (s *Message) Close() { for _, file := range s.files { file.Close() } }