woodpecker/pipeline/multipart/reader.go
2023-08-10 11:06:00 +02:00

118 lines
2.6 KiB
Go

// Copyright 2023 Woodpecker Authors
//
// 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 multipart
import (
"bufio"
"bytes"
"io"
"mime/multipart"
"net/textproto"
)
type (
// Reader is an iterator over parts in a multipart log stream.
Reader interface {
// NextPart returns the next part in the multipart or
// an error. When there are no more parts, the error
// io.EOF is returned.
NextPart() (Part, error)
}
// A Part represents a single part in a multipart body.
Part interface {
io.Reader
// Header returns the headers of the body with the
// keys canonicalized.
Header() textproto.MIMEHeader
// FileName returns the filename parameter of the
// Content-Disposition header.
FileName() string
// FormName returns the name parameter if p has a
// Content-Disposition of type form-data.
FormName() string
}
)
// New returns a new multipart Reader.
func New(r io.Reader) Reader {
buf := bufio.NewReader(r)
out, _ := buf.Peek(8)
if bytes.Equal(out, []byte("PIPELINE")) {
return &multipartReader{
reader: multipart.NewReader(buf, "boundary"),
}
}
return &textReader{
reader: buf,
}
}
//
// wraps the stdlib multi-part reader
//
type multipartReader struct {
reader *multipart.Reader
}
func (r *multipartReader) NextPart() (Part, error) {
next, err := r.reader.NextPart()
if err != nil {
return nil, err
}
part := new(part)
part.Reader = next
part.filename = next.FileName()
part.formname = next.FormName()
part.header = next.Header
return part, nil
}
//
// wraps a simple io.Reader to satisfy the multi-part interface
//
type textReader struct {
reader io.Reader
done bool
}
func (r *textReader) NextPart() (Part, error) {
if r.done {
return nil, io.EOF
}
r.done = true
p := new(part)
p.Reader = r.reader
return p, nil
}
type part struct {
io.Reader
filename string
formname string
header textproto.MIMEHeader
}
func (p *part) Header() textproto.MIMEHeader { return p.header }
func (p *part) FileName() string { return p.filename }
func (p *part) FormName() string { return p.filename }