github.com/furusax0621/goa-v1@v1.4.3/encoding.go (about)

     1  package goa
     2  
     3  import (
     4  	"encoding/gob"
     5  	"encoding/json"
     6  	"encoding/xml"
     7  	"fmt"
     8  	"io"
     9  	"mime"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  type (
    15  	// DecoderFunc instantiates a decoder that decodes data read from the given io reader.
    16  	DecoderFunc func(r io.Reader) Decoder
    17  
    18  	// A Decoder unmarshals an io.Reader into an interface.
    19  	Decoder interface {
    20  		Decode(v interface{}) error
    21  	}
    22  
    23  	// ResettableDecoder is used to determine whether or not a Decoder can be reset and thus
    24  	// safely reused in a sync.Pool.
    25  	ResettableDecoder interface {
    26  		Decoder
    27  		Reset(r io.Reader)
    28  	}
    29  
    30  	// decoderPool smartly determines whether to instantiate a new Decoder or reuse one from a
    31  	// sync.Pool.
    32  	decoderPool struct {
    33  		fn   DecoderFunc
    34  		pool *sync.Pool
    35  	}
    36  
    37  	// EncoderFunc instantiates an encoder that encodes data into the given writer.
    38  	EncoderFunc func(w io.Writer) Encoder
    39  
    40  	// An Encoder marshals from an interface into an io.Writer.
    41  	Encoder interface {
    42  		Encode(v interface{}) error
    43  	}
    44  
    45  	// The ResettableEncoder is used to determine whether or not a Encoder can be reset and
    46  	// thus safely reused in a sync.Pool.
    47  	ResettableEncoder interface {
    48  		Encoder
    49  		Reset(w io.Writer)
    50  	}
    51  
    52  	// encoderPool smartly determines whether to instantiate a new Encoder or reuse one from a
    53  	// sync.Pool.
    54  	encoderPool struct {
    55  		fn   EncoderFunc
    56  		pool *sync.Pool
    57  	}
    58  
    59  	// HTTPDecoder is a Decoder that decodes HTTP request or response bodies given a set of
    60  	// known Content-Type to decoder mapping.
    61  	HTTPDecoder struct {
    62  		pools map[string]*decoderPool // Registered decoders
    63  	}
    64  
    65  	// HTTPEncoder is a Encoder that encodes HTTP request or response bodies given a set of
    66  	// known Content-Type to encoder mapping.
    67  	HTTPEncoder struct {
    68  		pools        map[string]*encoderPool // Registered encoders
    69  		contentTypes []string                // List of content types for type negotiation
    70  	}
    71  )
    72  
    73  // NewJSONEncoder is an adapter for the encoding package JSON encoder.
    74  func NewJSONEncoder(w io.Writer) Encoder { return json.NewEncoder(w) }
    75  
    76  // NewJSONDecoder is an adapter for the encoding package JSON decoder.
    77  func NewJSONDecoder(r io.Reader) Decoder { return json.NewDecoder(r) }
    78  
    79  // NewXMLEncoder is an adapter for the encoding package XML encoder.
    80  func NewXMLEncoder(w io.Writer) Encoder { return xml.NewEncoder(w) }
    81  
    82  // NewXMLDecoder is an adapter for the encoding package XML decoder.
    83  func NewXMLDecoder(r io.Reader) Decoder { return xml.NewDecoder(r) }
    84  
    85  // NewGobEncoder is an adapter for the encoding package gob encoder.
    86  func NewGobEncoder(w io.Writer) Encoder { return gob.NewEncoder(w) }
    87  
    88  // NewGobDecoder is an adapter for the encoding package gob decoder.
    89  func NewGobDecoder(r io.Reader) Decoder { return gob.NewDecoder(r) }
    90  
    91  // NewHTTPEncoder creates an encoder that maps HTTP content types to low level encoders.
    92  func NewHTTPEncoder() *HTTPEncoder {
    93  	return &HTTPEncoder{
    94  		pools: make(map[string]*encoderPool),
    95  	}
    96  }
    97  
    98  // NewHTTPDecoder creates a decoder that maps HTTP content types to low level decoders.
    99  func NewHTTPDecoder() *HTTPDecoder {
   100  	return &HTTPDecoder{
   101  		pools: make(map[string]*decoderPool),
   102  	}
   103  }
   104  
   105  // Decode uses registered Decoders to unmarshal a body based on the contentType.
   106  func (decoder *HTTPDecoder) Decode(v interface{}, body io.Reader, contentType string) error {
   107  	now := time.Now()
   108  	defer MeasureSince([]string{"goa", "decode", contentType}, now)
   109  	var p *decoderPool
   110  	if contentType == "" {
   111  		// Default to JSON
   112  		contentType = "application/json"
   113  	} else {
   114  		if mediaType, _, err := mime.ParseMediaType(contentType); err == nil {
   115  			contentType = mediaType
   116  		}
   117  	}
   118  	p = decoder.pools[contentType]
   119  	if p == nil {
   120  		p = decoder.pools["*/*"]
   121  	}
   122  	if p == nil {
   123  		return nil
   124  	}
   125  
   126  	// the decoderPool will handle whether or not a pool is actually in use
   127  	d := p.Get(body)
   128  	defer p.Put(d)
   129  	return d.Decode(v)
   130  }
   131  
   132  // Register sets a specific decoder to be used for the specified content types. If a decoder is
   133  // already registered, it is overwritten.
   134  func (decoder *HTTPDecoder) Register(f DecoderFunc, contentTypes ...string) {
   135  	p := newDecodePool(f)
   136  
   137  	for _, contentType := range contentTypes {
   138  		mediaType, _, err := mime.ParseMediaType(contentType)
   139  		if err != nil {
   140  			mediaType = contentType
   141  		}
   142  		decoder.pools[mediaType] = p
   143  	}
   144  }
   145  
   146  // newDecodePool checks to see if the DecoderFunc returns reusable decoders and if so, creates a
   147  // pool.
   148  func newDecodePool(f DecoderFunc) *decoderPool {
   149  	// get a new decoder and type assert to see if it can be reset
   150  	d := f(nil)
   151  	rd, ok := d.(ResettableDecoder)
   152  
   153  	p := &decoderPool{fn: f}
   154  
   155  	// if the decoder can be reset, create a pool and put the typed decoder in
   156  	if ok {
   157  		p.pool = &sync.Pool{
   158  			New: func() interface{} { return f(nil) },
   159  		}
   160  		p.pool.Put(rd)
   161  	}
   162  
   163  	return p
   164  }
   165  
   166  // Get returns an already reset Decoder from the pool or creates a new one if necessary.
   167  func (p *decoderPool) Get(r io.Reader) Decoder {
   168  	if p.pool == nil {
   169  		return p.fn(r)
   170  	}
   171  
   172  	d := p.pool.Get().(ResettableDecoder)
   173  	d.Reset(r)
   174  	return d
   175  }
   176  
   177  // Put returns a Decoder into the pool if possible.
   178  func (p *decoderPool) Put(d Decoder) {
   179  	if p.pool == nil {
   180  		return
   181  	}
   182  	p.pool.Put(d)
   183  }
   184  
   185  // Encode uses the registered encoders and given content type to marshal and write the given value
   186  // using the given writer.
   187  func (encoder *HTTPEncoder) Encode(v interface{}, resp io.Writer, accept string) error {
   188  	now := time.Now()
   189  	if accept == "" {
   190  		accept = "*/*"
   191  	}
   192  	var contentType string
   193  	for _, t := range encoder.contentTypes {
   194  		if accept == "*/*" || accept == t {
   195  			contentType = accept
   196  			break
   197  		}
   198  	}
   199  	defer MeasureSince([]string{"goa", "encode", contentType}, now)
   200  	p := encoder.pools[contentType]
   201  	if p == nil && contentType != "*/*" {
   202  		p = encoder.pools["*/*"]
   203  	}
   204  	if p == nil {
   205  		return fmt.Errorf("No encoder registered for %s and no default encoder", contentType)
   206  	}
   207  
   208  	// the encoderPool will handle whether or not a pool is actually in use
   209  	e := p.Get(resp)
   210  	if err := e.Encode(v); err != nil {
   211  		return err
   212  	}
   213  	p.Put(e)
   214  
   215  	return nil
   216  }
   217  
   218  // Register sets a specific encoder to be used for the specified content types. If an encoder is
   219  // already registered, it is overwritten.
   220  func (encoder *HTTPEncoder) Register(f EncoderFunc, contentTypes ...string) {
   221  	p := newEncodePool(f)
   222  	for _, contentType := range contentTypes {
   223  		mediaType, _, err := mime.ParseMediaType(contentType)
   224  		if err != nil {
   225  			mediaType = contentType
   226  		}
   227  		encoder.pools[mediaType] = p
   228  	}
   229  
   230  	// Rebuild a unique index of registered content encoders to be used in EncodeResponse
   231  	encoder.contentTypes = make([]string, 0, len(encoder.pools))
   232  	for contentType := range encoder.pools {
   233  		encoder.contentTypes = append(encoder.contentTypes, contentType)
   234  	}
   235  }
   236  
   237  // newEncodePool checks to see if the EncoderFactory returns reusable encoders and if so, creates
   238  // a pool.
   239  func newEncodePool(f EncoderFunc) *encoderPool {
   240  	// get a new encoder and type assert to see if it can be reset
   241  	e := f(nil)
   242  	re, ok := e.(ResettableEncoder)
   243  
   244  	p := &encoderPool{fn: f}
   245  
   246  	// if the encoder can be reset, create a pool and put the typed encoder in
   247  	if ok {
   248  		p.pool = &sync.Pool{
   249  			New: func() interface{} { return f(nil) },
   250  		}
   251  		p.pool.Put(re)
   252  	}
   253  
   254  	return p
   255  }
   256  
   257  // Get returns an already reset Encoder from the pool or creates a new one if necessary.
   258  func (p *encoderPool) Get(w io.Writer) Encoder {
   259  	if p.pool == nil {
   260  		return p.fn(w)
   261  	}
   262  
   263  	e := p.pool.Get().(ResettableEncoder)
   264  	e.Reset(w)
   265  	return e
   266  }
   267  
   268  // Put returns a Decoder into the pool if possible.
   269  func (p *encoderPool) Put(e Encoder) {
   270  	if p.pool == nil {
   271  		return
   272  	}
   273  	p.pool.Put(e)
   274  }