You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devices/videosink/writer.go

75 lines
1.7 KiB
Go

// Copyright 2021 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package videosink
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"net/textproto"
"strconv"
)
// randomBoundary generates a MIME multipart boundary compatible with RFC 2046
// (section 5.1.1).
func randomBoundary() string {
var buf [34]byte
if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil {
panic(err)
}
return fmt.Sprintf("%x", buf[:])
}
type partWriter struct {
u io.Writer
boundary string
started bool
}
func makePartWriter(u io.Writer) partWriter {
return partWriter{
u: u,
boundary: randomBoundary(),
}
}
// writeFrame sends a single part of a MIME multipart entity, ensuring it's
// fully written by the time the function returns.
//
// The caller-owned headers are modified to set a Content-Length header.
//
// Go has a writer for MIME multipart messages in "mime/multipart".Writer. As
// of Go 1.17 it's not suitable for writing a neverending stream of parts where
// each must be flushed to the client with the part-ending boundary line.
func (w *partWriter) writeFrame(header textproto.MIMEHeader, body []byte) error {
header.Set("Content-Length", strconv.FormatInt(int64(len(body)), 10))
var buf bytes.Buffer
if !w.started {
fmt.Fprintf(&buf, "--%s\r\n", w.boundary)
w.started = true
}
for name := range header {
for _, value := range header[name] {
fmt.Fprintf(&buf, "%s: %s\r\n", name, value)
}
}
buf.WriteString("\r\n")
_, err := buf.WriteTo(w.u)
if err == nil {
_, err = io.Copy(w.u, bytes.NewReader(body))
if err == nil {
_, err = fmt.Fprintf(w.u, "\r\n--%s\r\n", w.boundary)
}
}
return err
}