1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Update docs + fix endian issue

This commit is contained in:
Guillaume J. Charmes 2013-09-26 15:59:02 -07:00
parent 082d142024
commit cb18a6e1b9
No known key found for this signature in database
GPG key ID: B33E4642CB6E3FF3
6 changed files with 63 additions and 95 deletions

View file

@ -951,7 +951,7 @@ func TestPostContainersAttach(t *testing.T) {
}) })
setTimeout(t, "read/write assertion timed out", 2*time.Second, func() { setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
if err := assertPipe("hello\n", string([]byte{1, 0, 0, 0, 6, 0, 0, 0})+"hello", stdout, stdinPipe, 15); err != nil { if err := assertPipe("hello\n", string([]byte{1, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 15); err != nil {
t.Fatal(err) t.Fatal(err)
} }
}) })
@ -1040,7 +1040,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
}) })
setTimeout(t, "read/write assertion timed out", 2*time.Second, func() { setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
if err := assertPipe("hello\n", string([]byte{2, 0, 0, 0, 6, 0, 0, 0})+"hello", stdout, stdinPipe, 15); err != nil { if err := assertPipe("hello\n", string([]byte{2, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 15); err != nil {
t.Fatal(err) t.Fatal(err)
} }
}) })

View file

@ -1,52 +0,0 @@
:title: Attach stream API
:description: API Documentation for the Attach command in Docker
:keywords: API, Docker, Attach, Stream, REST, documentation
=================
Docker Attach stream API
=================
.. contents:: Table of Contents
1. Brief introduction
=====================
- This is the Attach stream API for Docker
2. Format
=========
The attach format is a Header and a Payload (frame).
2.1 Header
^^^^^^^^^^
The header will contain the information on which stream write
the stream (stdout or stderr).
It also contain the size of the associated frame encoded on the last 4 bytes (uint32).
It is encoded on the first 8 bytes like this:
header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
STREAM_TYPE can be:
- 0: stdin (will be writen on stdout)
- 1: stdout
- 2: stderr
SIZE1, SIZE2, SIZE3, SIZE4 are the 4 bytes of the uint32 size.
2.1 Payload (frame)
^^^^^^^^^^^^^^^^^^^
The payload is the raw stream.
3. Implementation
=================
The simplest way to implement the Attach protocol is the following:
1) Read 8 bytes
2) chose stdout or stderr depending on the first byte
3) Extract the frame size from the last 4 byets
4) Read the extracted size and output it on the correct output
5) Goto 1)

View file

@ -44,10 +44,10 @@ What's new
.. http:post:: /containers/(id)/attach .. http:post:: /containers/(id)/attach
**New!** You can now split stderr from stdout. This is done by prefixing **New!** You can now split stderr from stdout. This is done by prefixing
a header to each transmition. See :doc:`attach_api_1.6`. a header to each transmition. See :http:post:`/containers/(id)/attach`.
The WebSocket attach is unchanged. The WebSocket attach is unchanged.
Note that attach calls on previous API version didn't change. Stdout and Note that attach calls on the previous API version didn't change. Stdout and
stderr are merge. stderr are merged.
:doc:`docker_remote_api_v1.5` :doc:`docker_remote_api_v1.5`

View file

@ -468,7 +468,7 @@ Attach to a container
HTTP/1.1 200 OK HTTP/1.1 200 OK
Content-Type: application/vnd.docker.raw-stream Content-Type: application/vnd.docker.raw-stream
{{ PREFIXED STREAM }} See :doc:`attach_api_1.6` {{ STREAM }}
:query logs: 1/True/true or 0/False/false, return logs. Default false :query logs: 1/True/true or 0/False/false, return logs. Default false
:query stream: 1/True/true or 0/False/false, return stream. Default false :query stream: 1/True/true or 0/False/false, return stream. Default false
@ -480,6 +480,49 @@ Attach to a container
:statuscode 404: no such container :statuscode 404: no such container
:statuscode 500: server error :statuscode 500: server error
**Stream details**:
When using the TTY setting is enabled in
:http:post:`/containers/create`, the stream is the raw data
from the process PTY and client's stdin. When the TTY is
disabled, then the stream is multiplexed to separate stdout
and stderr.
The format is a **Header** and a **Payload** (frame).
**HEADER**
The header will contain the information on which stream write
the stream (stdout or stderr). It also contain the size of
the associated frame encoded on the last 4 bytes (uint32).
It is encoded on the first 8 bytes like this::
header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
``STREAM_TYPE`` can be:
- 0: stdin (will be writen on stdout)
- 1: stdout
- 2: stderr
``SIZE1, SIZE2, SIZE3, SIZE4`` are the 4 bytes of the uint32 size encoded as big endian.
**PAYLOAD**
The payload is the raw stream.
**IMPLEMENTATION**
The simplest way to implement the Attach protocol is the following:
1) Read 8 bytes
2) chose stdout or stderr depending on the first byte
3) Extract the frame size from the last 4 byets
4) Read the extracted size and output it on the correct output
5) Goto 1)
Wait a container Wait a container
**************** ****************

View file

@ -1208,16 +1208,20 @@ func (srv *Server) ContainerAttach(name string, logs, stream, stdin, stdout, std
} else { } else {
dec := json.NewDecoder(cLog) dec := json.NewDecoder(cLog)
for { for {
var l utils.JSONLog l := &utils.JSONLog{}
if err := dec.Decode(&l); err == io.EOF {
if err := dec.Decode(l); err == io.EOF {
break break
} else if err != nil { } else if err != nil {
utils.Debugf("Error streaming logs: %s", err) utils.Debugf("Error streaming logs: %s", err)
break break
} }
if (l.Stream == "stdout" && stdout) || (l.Stream == "stderr" && stderr) { if l.Stream == "stdout" && stdout {
fmt.Fprintf(outStream, "%s", l.Log) fmt.Fprintf(outStream, "%s", l.Log)
} }
if l.Stream == "stderr" && stderr {
fmt.Fprintf(errStream, "%s", l.Log)
}
} }
} }
} }

View file

@ -4,18 +4,8 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
"unsafe"
) )
func CheckBigEndian() bool {
var x uint32 = 0x01020304
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
return true
}
return false
}
const ( const (
StdWriterPrefixLen = 8 StdWriterPrefixLen = 8
StdWriterFdIndex = 0 StdWriterFdIndex = 0
@ -32,16 +22,15 @@ var (
type StdWriter struct { type StdWriter struct {
io.Writer io.Writer
prefix StdType prefix StdType
sizeBuf []byte sizeBuf []byte
byteOrder binary.ByteOrder
} }
func (w *StdWriter) Write(buf []byte) (n int, err error) { func (w *StdWriter) Write(buf []byte) (n int, err error) {
if w == nil || w.Writer == nil { if w == nil || w.Writer == nil {
return 0, errors.New("Writer not instanciated") return 0, errors.New("Writer not instanciated")
} }
w.byteOrder.PutUint32(w.prefix[4:], uint32(len(buf))) binary.BigEndian.PutUint32(w.prefix[4:], uint32(len(buf)))
buf = append(w.prefix[:], buf...) buf = append(w.prefix[:], buf...)
n, err = w.Writer.Write(buf) n, err = w.Writer.Write(buf)
@ -55,18 +44,10 @@ func NewStdWriter(w io.Writer, t StdType) *StdWriter {
return nil return nil
} }
var bo binary.ByteOrder
if CheckBigEndian() {
bo = binary.BigEndian
} else {
bo = binary.LittleEndian
}
return &StdWriter{ return &StdWriter{
Writer: w, Writer: w,
prefix: t, prefix: t,
sizeBuf: make([]byte, 4), sizeBuf: make([]byte, 4),
byteOrder: bo,
} }
} }
@ -91,17 +72,9 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
nr, nw int nr, nw int
er, ew error er, ew error
out io.Writer out io.Writer
byteOrder binary.ByteOrder
frameSize int frameSize int
) )
// Check the machine's endianness
if CheckBigEndian() {
byteOrder = binary.BigEndian
} else {
byteOrder = binary.LittleEndian
}
for { for {
// Make sure we have at least a full header // Make sure we have at least a full header
for nr < StdWriterPrefixLen { for nr < StdWriterPrefixLen {
@ -132,7 +105,7 @@ func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error)
} }
// Retrieve the size of the frame // Retrieve the size of the frame
frameSize = int(byteOrder.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4])) frameSize = int(binary.BigEndian.Uint32(buf[StdWriterSizeIndex : StdWriterSizeIndex+4]))
// Check if the buffer is big enough to read the frame. // Check if the buffer is big enough to read the frame.
// Extend it if necessary. // Extend it if necessary.