1// Copyright 2015 go-swagger maintainers 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package runtime 16 17import ( 18 "bytes" 19 "encoding" 20 "errors" 21 "fmt" 22 "io" 23 "reflect" 24 25 "github.com/go-openapi/swag" 26) 27 28func defaultCloser() error { return nil } 29 30type byteStreamOpt func(opts *byteStreamOpts) 31 32// ClosesStream when the bytestream consumer or producer is finished 33func ClosesStream(opts *byteStreamOpts) { 34 opts.Close = true 35} 36 37type byteStreamOpts struct { 38 Close bool 39} 40 41// ByteStreamConsumer creates a consmer for byte streams, 42// takes a Writer/BinaryUnmarshaler interface or binary slice by reference, 43// and reads from the provided reader 44func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { 45 var vals byteStreamOpts 46 for _, opt := range opts { 47 opt(&vals) 48 } 49 50 return ConsumerFunc(func(reader io.Reader, data interface{}) error { 51 if reader == nil { 52 return errors.New("ByteStreamConsumer requires a reader") // early exit 53 } 54 55 close := defaultCloser 56 if vals.Close { 57 if cl, ok := reader.(io.Closer); ok { 58 close = cl.Close 59 } 60 } 61 defer close() 62 63 if wrtr, ok := data.(io.Writer); ok { 64 _, err := io.Copy(wrtr, reader) 65 return err 66 } 67 68 buf := new(bytes.Buffer) 69 _, err := buf.ReadFrom(reader) 70 if err != nil { 71 return err 72 } 73 b := buf.Bytes() 74 75 if bu, ok := data.(encoding.BinaryUnmarshaler); ok { 76 return bu.UnmarshalBinary(b) 77 } 78 79 if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr { 80 v := reflect.Indirect(reflect.ValueOf(data)) 81 if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { 82 v.SetBytes(b) 83 return nil 84 } 85 } 86 87 return fmt.Errorf("%v (%T) is not supported by the ByteStreamConsumer, %s", 88 data, data, "can be resolved by supporting Writer/BinaryUnmarshaler interface") 89 }) 90} 91 92// ByteStreamProducer creates a producer for byte streams, 93// takes a Reader/BinaryMarshaler interface or binary slice, 94// and writes to a writer (essentially a pipe) 95func ByteStreamProducer(opts ...byteStreamOpt) Producer { 96 var vals byteStreamOpts 97 for _, opt := range opts { 98 opt(&vals) 99 } 100 return ProducerFunc(func(writer io.Writer, data interface{}) error { 101 if writer == nil { 102 return errors.New("ByteStreamProducer requires a writer") // early exit 103 } 104 close := defaultCloser 105 if vals.Close { 106 if cl, ok := writer.(io.Closer); ok { 107 close = cl.Close 108 } 109 } 110 defer close() 111 112 if rc, ok := data.(io.ReadCloser); ok { 113 defer rc.Close() 114 } 115 116 if rdr, ok := data.(io.Reader); ok { 117 _, err := io.Copy(writer, rdr) 118 return err 119 } 120 121 if bm, ok := data.(encoding.BinaryMarshaler); ok { 122 bytes, err := bm.MarshalBinary() 123 if err != nil { 124 return err 125 } 126 127 _, err = writer.Write(bytes) 128 return err 129 } 130 131 if data != nil { 132 if e, ok := data.(error); ok { 133 _, err := writer.Write([]byte(e.Error())) 134 return err 135 } 136 137 v := reflect.Indirect(reflect.ValueOf(data)) 138 if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { 139 _, err := writer.Write(v.Bytes()) 140 return err 141 } 142 if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { 143 b, err := swag.WriteJSON(data) 144 if err != nil { 145 return err 146 } 147 _, err = writer.Write(b) 148 return err 149 } 150 } 151 152 return fmt.Errorf("%v (%T) is not supported by the ByteStreamProducer, %s", 153 data, data, "can be resolved by supporting Reader/BinaryMarshaler interface") 154 }) 155} 156