Files
kata-containers/vendor/github.com/clearcontainers/proxy/api/protocol.go
Jose Carlos Venegas Munoz dad59fe092 vendor: Move virtcontainers to project's root directory
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>
2018-03-14 11:42:02 -06:00

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))
}