mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-10-27 23:33:38 +00:00
Lets have a global vendor base on virtcontainers. Signed-off-by: Julio Montes <julio.montes@intel.com> Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com> Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
214 lines
6.4 KiB
Go
214 lines
6.4 KiB
Go
// Copyright (c) 2016 Intel Corporation
|
|
//
|
|
// 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 api
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// minHeaderLength is the length of the header in the version 2 of protocol.
|
|
// It is guaranteed later versions will have a header at least that big.
|
|
const minHeaderLength = 12 // in bytes
|
|
|
|
// A Request is a JSON message sent from a client to the proxy. This message
|
|
// embed a payload identified by "id". A payload can have data associated with
|
|
// it. It's useful to think of Request as an RPC call with "id" as function
|
|
// name and "data" as arguments.
|
|
//
|
|
// The list of possible payloads are documented in this package.
|
|
//
|
|
// Each Request has a corresponding Response message sent back from the proxy.
|
|
type Request struct {
|
|
ID string `json:"id"`
|
|
Data json.RawMessage `json:"data,omitempty"`
|
|
}
|
|
|
|
// A Response is a JSON message sent back from the proxy to a client after a
|
|
// Request has been issued. The Response holds the result of the Request,
|
|
// including its success state and optional data. It's useful to think of
|
|
// Response as the result of an RPC call with ("success", "error") describing
|
|
// if the call has been successful and "data" holding the optional results.
|
|
type Response struct {
|
|
Success bool `json:"success"`
|
|
Error string `json:"error,omitempty"`
|
|
Data map[string]interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
// Offsets (in bytes) of frame headers fields.
|
|
const (
|
|
versionOffset = 0
|
|
headerLengthOffset = 2
|
|
typeOffset = 6
|
|
flagsOffset = 6
|
|
opcodeOffset = 7
|
|
payloadLengthOffset = 8
|
|
)
|
|
|
|
// Size (in bytes) of frame header fields (when larger than 1 byte).
|
|
const (
|
|
versionSize = 2
|
|
payloadLengthSize = 4
|
|
)
|
|
|
|
// Masks needed to extract fields
|
|
const (
|
|
typeMask = 0x0f
|
|
flagsMask = 0xf0
|
|
)
|
|
|
|
func maxOpcodeForFrameType(t FrameType) int {
|
|
switch t {
|
|
default:
|
|
fallthrough
|
|
case TypeCommand:
|
|
return int(CmdMax)
|
|
case TypeResponse:
|
|
return int(CmdMax)
|
|
case TypeStream:
|
|
return int(StreamMax)
|
|
case TypeNotification:
|
|
return int(NotificationMax)
|
|
}
|
|
}
|
|
|
|
// ReadFrame reads a full frame (header and payload) from r.
|
|
func ReadFrame(r io.Reader) (*Frame, error) {
|
|
// Read the header.
|
|
buf := make([]byte, minHeaderLength)
|
|
n, err := r.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != minHeaderLength {
|
|
return nil, errors.New("frame: couldn't read the full header")
|
|
}
|
|
|
|
// Decode it.
|
|
frame := &Frame{}
|
|
header := &frame.Header
|
|
header.Version = int(binary.BigEndian.Uint16(buf[versionOffset : versionOffset+versionSize]))
|
|
if header.Version < 2 || header.Version > Version {
|
|
return nil, fmt.Errorf("frame: bad version %d", header.Version)
|
|
}
|
|
header.HeaderLength = int(buf[headerLengthOffset]) * 4
|
|
header.Type = FrameType(buf[typeOffset] & typeMask)
|
|
flags := buf[flagsOffset] & flagsMask
|
|
if flags&flagInError != 0 {
|
|
header.InError = true
|
|
}
|
|
if header.Type >= TypeMax {
|
|
return nil, fmt.Errorf("frame: bad type %s", header.Type)
|
|
}
|
|
header.Opcode = int(buf[opcodeOffset])
|
|
if header.Opcode >= maxOpcodeForFrameType(header.Type) {
|
|
return nil, fmt.Errorf("frame: bad opcode (%d) for type %s", header.Opcode,
|
|
header.Type)
|
|
}
|
|
header.PayloadLength = int(binary.BigEndian.Uint32(buf[payloadLengthOffset : payloadLengthOffset+payloadLengthSize]))
|
|
|
|
// Read the payload.
|
|
received := 0
|
|
need := header.HeaderLength - minHeaderLength + header.PayloadLength
|
|
payload := make([]byte, need)
|
|
for received < need {
|
|
n, err := r.Read(payload[received:need])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
received += n
|
|
}
|
|
|
|
// Skip the bytes part of a bigger header than expected to just keep
|
|
// the payload.
|
|
frame.Payload = payload[header.HeaderLength-minHeaderLength : need]
|
|
|
|
return frame, nil
|
|
}
|
|
|
|
const (
|
|
flagInError = 1 << (4 + iota)
|
|
)
|
|
|
|
// WriteFrame writes a frame into w.
|
|
//
|
|
// Note that frame.Header.PayloadLength dictates the amount of data of
|
|
// frame.Payload to write, so frame.Header.Payload must be less or equal to
|
|
// len(frame.Payload).
|
|
func WriteFrame(w io.Writer, frame *Frame) error {
|
|
header := &frame.Header
|
|
|
|
if len(frame.Payload) < header.PayloadLength {
|
|
return fmt.Errorf("frame: bad payload length %d",
|
|
header.PayloadLength)
|
|
}
|
|
|
|
// Prepare the header.
|
|
len := minHeaderLength + header.PayloadLength
|
|
buf := make([]byte, len)
|
|
binary.BigEndian.PutUint16(buf[versionOffset:versionOffset+versionSize], uint16(header.Version))
|
|
buf[headerLengthOffset] = byte(header.HeaderLength / 4)
|
|
flags := byte(0)
|
|
if frame.Header.InError {
|
|
flags |= flagInError
|
|
}
|
|
buf[typeOffset] = flags | byte(header.Type)&typeMask
|
|
buf[opcodeOffset] = byte(header.Opcode)
|
|
binary.BigEndian.PutUint32(buf[payloadLengthOffset:payloadLengthOffset+payloadLengthSize],
|
|
uint32(header.PayloadLength))
|
|
|
|
// Write payload if needed
|
|
if header.PayloadLength > 0 {
|
|
copy(buf[minHeaderLength:], frame.Payload[0:header.PayloadLength])
|
|
}
|
|
|
|
n, err := w.Write(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if n != len {
|
|
return errors.New("frame: couldn't write frame")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteCommand is a convenience wrapper around WriteFrame to send commands.
|
|
func WriteCommand(w io.Writer, op Command, payload []byte) error {
|
|
return WriteFrame(w, NewFrame(TypeCommand, int(op), payload))
|
|
}
|
|
|
|
// WriteResponse is a convenience wrapper around WriteFrame to send responses.
|
|
func WriteResponse(w io.Writer, op Command, inError bool, payload []byte) error {
|
|
frame := NewFrame(TypeResponse, int(op), payload)
|
|
frame.Header.InError = inError
|
|
return WriteFrame(w, frame)
|
|
}
|
|
|
|
// WriteStream is a convenience wrapper around WriteFrame to send stream packets.
|
|
func WriteStream(w io.Writer, op Stream, payload []byte) error {
|
|
return WriteFrame(w, NewFrame(TypeStream, int(op), payload))
|
|
}
|
|
|
|
// WriteNotification is a convenience wrapper around WriteFrame to send notifications.
|
|
func WriteNotification(w io.Writer, op Notification, payload []byte) error {
|
|
return WriteFrame(w, NewFrame(TypeNotification, int(op), payload))
|
|
}
|