blob: 448fc9d3f714f87c776daf1c9fa3c7809cab77b9 [file] [log] [blame]
/*
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cherry
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
)
const (
MessageHeaderSize = 8 // msgSize:int32, msgType:int32
)
type Message interface {
GetType () MessageType
Serialize (io.Writer) error
Deserialize (io.Reader) error
}
// \todo [petri] implement serialization using reflection instead of manually?
type MessageType int32
const (
// Commands.
MSG_HELLO MessageType = 100
MSG_TEST = 101
MSG_KEEPALIVE = 102
MSG_EXECUTE_BINARY = 111
MSG_STOP_EXECUTION = 112
// Responses.
MSG_PROCESS_STARTED = 200
MSG_PROCESS_LAUNCH_FAILED = 201
MSG_PROCESS_FINISHED = 202
MSG_PROCESS_LOG_DATA = 203
MSG_INFO = 204
)
func createMessage (msgType MessageType) (Message, error) {
switch msgType {
case MSG_HELLO: return &MsgHello{}, nil
case MSG_TEST: return &MsgTest{}, nil
case MSG_KEEPALIVE: return &MsgKeepAlive{}, nil
case MSG_EXECUTE_BINARY: return &MsgExecuteBinary{}, nil
case MSG_STOP_EXECUTION: return &MsgStopExecution{}, nil
case MSG_PROCESS_STARTED: return &MsgProcessStarted{}, nil
case MSG_PROCESS_LAUNCH_FAILED: return &MsgProcessLaunchFailed{}, nil
case MSG_PROCESS_FINISHED: return &MsgProcessFinished{}, nil
case MSG_PROCESS_LOG_DATA: return &MsgProcessLogData{}, nil
case MSG_INFO: return &MsgInfo{}, nil
}
return nil, fmt.Errorf("CreateMessage(): invalid message type %v", msgType)
}
// Util
func read (r io.Reader, v interface{}) error {
return binary.Read(r, binary.BigEndian, v)
}
func write (w io.Writer, v interface{}) error {
return binary.Write(w, binary.BigEndian, v)
}
func readString (r io.Reader, v *string) error {
/* \todo [petri] this is probably horribly slow implementation.. */
buf := make([]byte, 0)
for {
var b byte
err := read(r, &b)
if err != nil { return err }
if b == 0 { break }
buf = append(buf, b)
}
*v = string(buf)
return nil
}
func writeString (w io.Writer, v string) error {
_, err := w.Write([]byte(v))
if err != nil { return err }
var eos byte = 0
err = write(w, eos)
return err
}
// MsgHello
type MsgHello struct {
Version int32
}
func (msg *MsgHello) GetType () MessageType { return MSG_HELLO }
func (msg *MsgHello) Serialize (w io.Writer) error {
/* \todo [petri] chain err through write() calls? */
if err := write(w, msg.Version); err != nil { return err }
return nil
}
func (msg *MsgHello) Deserialize (r io.Reader) error {
if err := read(r, &msg.Version); err != nil { return err }
return nil
}
// MsgTest
type MsgTest struct {
}
func (msg *MsgTest) GetType () MessageType { return MSG_TEST }
func (msg *MsgTest) Serialize (w io.Writer) error {
return nil
}
func (msg *MsgTest) Deserialize (r io.Reader) error {
return nil
}
// MsgKeepAlive
type MsgKeepAlive struct {
}
func (msg *MsgKeepAlive) GetType () MessageType { return MSG_KEEPALIVE }
func (msg *MsgKeepAlive) Serialize (w io.Writer) error {
return nil
}
func (msg *MsgKeepAlive) Deserialize (r io.Reader) error {
return nil
}
// MsgExecuteBinary
type MsgExecuteBinary struct {
BinaryName string
Parameters string
WorkingDir string
CaseList string
}
func (msg *MsgExecuteBinary) GetType () MessageType { return MSG_EXECUTE_BINARY }
func (msg *MsgExecuteBinary) Serialize (w io.Writer) error {
if err := writeString(w, msg.BinaryName); err != nil { return err }
if err := writeString(w, msg.Parameters); err != nil { return err }
if err := writeString(w, msg.WorkingDir); err != nil { return err }
if err := writeString(w, msg.CaseList); err != nil { return err }
return nil
}
func (msg *MsgExecuteBinary) Deserialize (r io.Reader) error {
if err := readString(r, &msg.BinaryName); err != nil { return err }
if err := readString(r, &msg.Parameters); err != nil { return err }
if err := readString(r, &msg.WorkingDir); err != nil { return err }
if err := readString(r, &msg.CaseList); err != nil { return err }
return nil
}
// MsgStopExecution
type MsgStopExecution struct {
}
func (msg *MsgStopExecution) GetType () MessageType { return MSG_STOP_EXECUTION }
func (msg *MsgStopExecution) Serialize (w io.Writer) error {
return nil
}
func (msg *MsgStopExecution) Deserialize (r io.Reader) error {
return nil
}
// MsgProcessStarted
type MsgProcessStarted struct {
}
func (msg *MsgProcessStarted) GetType () MessageType { return MSG_PROCESS_STARTED }
func (msg *MsgProcessStarted) Serialize (w io.Writer) error {
return nil
}
func (msg *MsgProcessStarted) Deserialize (r io.Reader) error {
return nil
}
// MsgProcessLaunchFailed
type MsgProcessLaunchFailed struct {
Reason string
}
func (msg *MsgProcessLaunchFailed) GetType () MessageType { return MSG_PROCESS_LAUNCH_FAILED }
func (msg *MsgProcessLaunchFailed) Serialize (w io.Writer) error {
if err := writeString(w, msg.Reason); err != nil { return err }
return nil
}
func (msg *MsgProcessLaunchFailed) Deserialize (r io.Reader) error {
if err := readString(r, &msg.Reason); err != nil { return err }
return nil
}
// MsgProcessFinished
type MsgProcessFinished struct {
ExitCode int32
}
func (msg *MsgProcessFinished) GetType () MessageType { return MSG_PROCESS_FINISHED }
func (msg *MsgProcessFinished) Serialize (w io.Writer) error {
if err := write(w, msg.ExitCode); err != nil { return err }
return nil
}
func (msg *MsgProcessFinished) Deserialize (r io.Reader) error {
if err := read(r, &msg.ExitCode); err != nil { return err }
return nil
}
// MsgProcessLogData
type MsgProcessLogData struct {
Content string
}
func (msg *MsgProcessLogData) GetType () MessageType { return MSG_PROCESS_LOG_DATA }
func (msg *MsgProcessLogData) Serialize (w io.Writer) error {
if err := writeString(w, msg.Content); err != nil { return err }
return nil
}
func (msg *MsgProcessLogData) Deserialize (r io.Reader) error {
if err := readString(r, &msg.Content); err != nil { return err }
return nil
}
// MsgInfo
type MsgInfo struct {
Content string
}
func (msg *MsgInfo) GetType () MessageType { return MSG_INFO }
func (msg *MsgInfo) Serialize (w io.Writer) error {
if err := writeString(w, msg.Content); err != nil { return err }
return nil
}
func (msg *MsgInfo) Deserialize (r io.Reader) error {
if err := readString(r, &msg.Content); err != nil { return err }
return nil
}
// Utility for reading/writing messages with headers.
func WriteMessage (w io.Writer, msg Message) error {
// Serialize payload.
payload := bytes.NewBuffer([]byte{})
err := msg.Serialize(payload)
if err != nil { return err }
// Write header.
var msgSize = (int32)(MessageHeaderSize + payload.Len())
var msgType MessageType = msg.GetType()
err = write(w, msgSize)
if err != nil { return err }
err = write(w, msgType)
if err != nil { return err }
// Write payload.
bytes := payload.Bytes()
numWritten, err := w.Write(bytes)
if err != nil { return err }
if numWritten != len(bytes) {
log.Printf("[msg] WARNING: WROTE %d OF %d BYTES\n", numWritten, len(bytes))
}
return nil
}
func ReadMessage (r io.Reader) (Message, error) {
// \todo [petri] implement
var msgLen int32
var msgType MessageType
err := read(r, &msgLen)
if err != nil { return nil, err }
err = read(r, &msgType)
if err != nil { return nil, err }
msg, err := createMessage(msgType)
if err != nil { return nil, err }
err = msg.Deserialize(r)
if err != nil { return nil, err }
return msg, nil
}