github.com/Kolosok86/http@v0.1.2/textproto/writer.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package textproto 6 7 import ( 8 "bufio" 9 "fmt" 10 "io" 11 ) 12 13 // A Writer implements convenience methods for writing 14 // requests or responses to a text protocol network connection. 15 type Writer struct { 16 W *bufio.Writer 17 dot *dotWriter 18 } 19 20 // NewWriter returns a new Writer writing to w. 21 func NewWriter(w *bufio.Writer) *Writer { 22 return &Writer{W: w} 23 } 24 25 var crnl = []byte{'\r', '\n'} 26 var dotcrnl = []byte{'.', '\r', '\n'} 27 28 // PrintfLine writes the formatted output followed by \r\n. 29 func (w *Writer) PrintfLine(format string, args ...any) error { 30 w.closeDot() 31 fmt.Fprintf(w.W, format, args...) 32 w.W.Write(crnl) 33 return w.W.Flush() 34 } 35 36 // DotWriter returns a writer that can be used to write a dot-encoding to w. 37 // It takes care of inserting leading dots when necessary, 38 // translating line-ending \n into \r\n, and adding the final .\r\n line 39 // when the DotWriter is closed. The caller should close the 40 // DotWriter before the next call to a method on w. 41 // 42 // See the documentation for Reader's DotReader method for details about dot-encoding. 43 func (w *Writer) DotWriter() io.WriteCloser { 44 w.closeDot() 45 w.dot = &dotWriter{w: w} 46 return w.dot 47 } 48 49 func (w *Writer) closeDot() { 50 if w.dot != nil { 51 w.dot.Close() // sets w.dot = nil 52 } 53 } 54 55 type dotWriter struct { 56 w *Writer 57 state int 58 } 59 60 const ( 61 wstateBegin = iota // initial state; must be zero 62 wstateBeginLine // beginning of line 63 wstateCR // wrote \r (possibly at end of line) 64 wstateData // writing data in middle of line 65 ) 66 67 func (d *dotWriter) Write(b []byte) (n int, err error) { 68 bw := d.w.W 69 for n < len(b) { 70 c := b[n] 71 switch d.state { 72 case wstateBegin, wstateBeginLine: 73 d.state = wstateData 74 if c == '.' { 75 // escape leading dot 76 bw.WriteByte('.') 77 } 78 fallthrough 79 80 case wstateData: 81 if c == '\r' { 82 d.state = wstateCR 83 } 84 if c == '\n' { 85 bw.WriteByte('\r') 86 d.state = wstateBeginLine 87 } 88 89 case wstateCR: 90 d.state = wstateData 91 if c == '\n' { 92 d.state = wstateBeginLine 93 } 94 } 95 if err = bw.WriteByte(c); err != nil { 96 break 97 } 98 n++ 99 } 100 return 101 } 102 103 func (d *dotWriter) Close() error { 104 if d.w.dot == d { 105 d.w.dot = nil 106 } 107 bw := d.w.W 108 switch d.state { 109 default: 110 bw.WriteByte('\r') 111 fallthrough 112 case wstateCR: 113 bw.WriteByte('\n') 114 fallthrough 115 case wstateBeginLine: 116 bw.Write(dotcrnl) 117 } 118 return bw.Flush() 119 }