github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/api/common/concat.go (about) 1 package common 2 3 import ( 4 "bytes" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 "sort" 10 ) 11 12 // concatenatorDelimiter is the character which is put between different 13 // parts of the request so that these parts cannot be mixed up. 14 const concatenatorDelimiter = byte(0) 15 16 type concatenator struct { 17 req *http.Request 18 w io.Writer 19 } 20 21 // concatenateTo writes all elementary request parts to the given writer. 22 // parts are: method, URL, headers, body 23 func concatenateTo(req *http.Request, w io.Writer) error { 24 c := &concatenator{ 25 req: req, 26 w: w, 27 } 28 err := c.writeDelimitedString(c.req.Method) 29 if err != nil { 30 return err 31 } 32 33 err = c.URL() 34 if err != nil { 35 return err 36 } 37 38 c.Headers() 39 if err != nil { 40 return err 41 } 42 43 // Body() must be last, as it does not use the length-limited bincontainer; 44 // instead, it writes its data verbatim 45 c.Body() 46 if err != nil { 47 return err 48 } 49 return nil 50 } 51 52 // URL (re)constructs the URL and appends it 53 func (c *concatenator) URL() error { 54 var url url.URL 55 url = *c.req.URL 56 // we do not know the scheme for all cases, so ignore it: 57 url.Scheme = "" 58 if url.Host == "" { 59 url.Host = c.req.Host 60 } 61 62 return c.writeDelimitedString(url.String()) 63 } 64 65 // ignoreHeaders is a list of all headers which are not part of the 66 // resulting output. 67 var ignoreHeaders = map[string]bool{ 68 // Authorization is our header; we can't sign our own signature 69 "Authorization": true, 70 // User-Agent is allowed to vary 71 "User-Agent": true, 72 // Accept-Encoding will also be mangled by the client 73 "Accept-Encoding": true, 74 // Content-Length doesn't matter as the content is signed 75 "Content-Length": true, 76 // Host header does not need to be included as the parsed host header 77 // is included as part of the URL (see URL()) 78 "Host": true, 79 } 80 81 // Headers concatenate the headers. 82 func (c *concatenator) Headers() error { 83 headers := make([]string, len(c.req.Header)) 84 i := 0 85 for header := range c.req.Header { 86 headers[i] = header 87 i++ 88 } 89 sort.Strings(headers) 90 for _, header := range headers { 91 _, isIgnored := ignoreHeaders[header] 92 if isIgnored { 93 continue 94 } 95 err := c.writeDelimitedString(header) 96 if err != nil { 97 return err 98 } 99 for _, value := range c.req.Header[header] { 100 err = c.writeDelimitedString(value) 101 if err != nil { 102 return err 103 } 104 } 105 } 106 return nil 107 } 108 109 // Body concatenates the body. 110 func (c *concatenator) Body() error { 111 if c.req.Body == nil { 112 return c.writeDelimiter() 113 } 114 bodyCopy := &bytes.Buffer{} 115 // we write directly to the writer here and do not use 116 // bincontainer.Encoder; the reason for this is that doing 117 // that would require buffering all the body content to 118 // calculate its length beforehand. 119 // for this to be safe, no other data may be written afterwards. 120 _, err := io.Copy(io.MultiWriter(bodyCopy, c.w), c.req.Body) 121 if err != nil { 122 return err 123 } 124 c.req.Body = ioutil.NopCloser(bodyCopy) 125 return c.writeDelimiter() 126 } 127 128 // writeDelimitedString writes the given string to the writer and 129 // appends the delimiter. 130 func (c *concatenator) writeDelimitedString(s string) error { 131 _, err := c.w.Write([]byte(s)) 132 if err != nil { 133 return err 134 } 135 return c.writeDelimiter() 136 } 137 138 // writeDelimiter outputs the delimiter to the writer. 139 func (c *concatenator) writeDelimiter() error { 140 _, err := c.w.Write([]byte{concatenatorDelimiter}) 141 return err 142 }