p_compressor.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package backends
  2. import (
  3. "bytes"
  4. "compress/zlib"
  5. "github.com/flashmob/go-guerrilla/mail"
  6. "io"
  7. "sync"
  8. )
  9. // ----------------------------------------------------------------------------------
  10. // Processor Name: compressor
  11. // ----------------------------------------------------------------------------------
  12. // Description : Compress the e.Data (email data) and e.DeliveryHeader together
  13. // ----------------------------------------------------------------------------------
  14. // Config Options: None
  15. // --------------:-------------------------------------------------------------------
  16. // Input : e.Data, e.DeliveryHeader generated by Header() processor
  17. // ----------------------------------------------------------------------------------
  18. // Output : sets the pointer to a compressor in e.Info["zlib-compressor"]
  19. // : to write the compressed data, simply use fmt to print as a string,
  20. // : eg. fmt.Println("%s", e.Info["zlib-compressor"])
  21. // : or just call the String() func .Info["zlib-compressor"].String()
  22. // : Note that it can only be outputted once. It destroys the buffer
  23. // : after being printed
  24. // ----------------------------------------------------------------------------------
  25. func init() {
  26. processors["compressor"] = func() Decorator {
  27. return Compressor()
  28. }
  29. }
  30. // compressedData struct will be compressed using zlib when printed via fmt
  31. type compressor struct {
  32. extraHeaders []byte
  33. data *bytes.Buffer
  34. // the pool is used to recycle buffers to ease up on the garbage collector
  35. pool *sync.Pool
  36. }
  37. // newCompressedData returns a new CompressedData
  38. func newCompressor() *compressor {
  39. // grab it from the pool
  40. var p = sync.Pool{
  41. // if not available, then create a new one
  42. New: func() interface{} {
  43. var b bytes.Buffer
  44. return &b
  45. },
  46. }
  47. return &compressor{
  48. pool: &p,
  49. }
  50. }
  51. // Set the extraheaders and buffer of data to compress
  52. func (c *compressor) set(b []byte, d *bytes.Buffer) {
  53. c.extraHeaders = b
  54. c.data = d
  55. }
  56. // String implements the Stringer interface.
  57. // Can only be called once!
  58. // This is because the compression buffer will be reset and compressor will be returned to the pool
  59. func (c *compressor) String() string {
  60. if c.data == nil {
  61. return ""
  62. }
  63. //borrow a buffer form the pool
  64. b := c.pool.Get().(*bytes.Buffer)
  65. // put back in the pool
  66. defer func() {
  67. b.Reset()
  68. c.pool.Put(b)
  69. }()
  70. var r *bytes.Reader
  71. w, _ := zlib.NewWriterLevel(b, zlib.BestSpeed)
  72. r = bytes.NewReader(c.extraHeaders)
  73. io.Copy(w, r)
  74. io.Copy(w, c.data)
  75. w.Close()
  76. return b.String()
  77. }
  78. // clear it, without clearing the pool
  79. func (c *compressor) clear() {
  80. c.extraHeaders = []byte{}
  81. c.data = nil
  82. }
  83. func Compressor() Decorator {
  84. return func(p Processor) Processor {
  85. return ProcessWith(func(e *mail.Envelope, task SelectTask) (Result, error) {
  86. if task == TaskSaveMail {
  87. compressor := newCompressor()
  88. compressor.set([]byte(e.DeliveryHeader), &e.Data)
  89. // put the pointer in there for other processors to use later in the line
  90. e.Values["zlib-compressor"] = compressor
  91. // continue to the next Processor in the decorator stack
  92. return p.Process(e, task)
  93. } else {
  94. return p.Process(e, task)
  95. }
  96. })
  97. }
  98. }