request.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright 2011 Andy Balholm. 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. // Reading and parsing of ICAP requests.
  5. // Package icap provides an extensible ICAP server.
  6. package icap
  7. import (
  8. "bufio"
  9. "bytes"
  10. "fmt"
  11. "io"
  12. "io/ioutil"
  13. "net/http"
  14. "net/textproto"
  15. "net/url"
  16. "strconv"
  17. "strings"
  18. )
  19. type badStringError struct {
  20. what string
  21. str string
  22. }
  23. func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
  24. // A Request represents a parsed ICAP request.
  25. type Request struct {
  26. Method string // REQMOD, RESPMOD, OPTIONS, etc.
  27. RawURL string // The URL given in the request.
  28. URL *url.URL // Parsed URL.
  29. Proto string // The protocol version.
  30. Header textproto.MIMEHeader // The ICAP header
  31. RemoteAddr string // the address of the computer sending the request
  32. Preview []byte // the body data for an ICAP preview
  33. // The HTTP messages.
  34. Request *http.Request
  35. Response *http.Response
  36. }
  37. // ReadRequest reads and parses a request from b.
  38. func ReadRequest(b *bufio.ReadWriter) (req *Request, err error) {
  39. tp := textproto.NewReader(b.Reader)
  40. req = new(Request)
  41. // Read first line.
  42. var s string
  43. s, err = tp.ReadLine()
  44. if err != nil {
  45. if err == io.EOF {
  46. err = io.ErrUnexpectedEOF
  47. }
  48. return nil, err
  49. }
  50. f := strings.SplitN(s, " ", 3)
  51. if len(f) < 3 {
  52. return nil, &badStringError{"malformed ICAP request", s}
  53. }
  54. req.Method, req.RawURL, req.Proto = f[0], f[1], f[2]
  55. req.URL, err = url.ParseRequestURI(req.RawURL)
  56. if err != nil {
  57. return nil, err
  58. }
  59. req.Header, err = tp.ReadMIMEHeader()
  60. if err != nil {
  61. return nil, err
  62. }
  63. s = req.Header.Get("Encapsulated")
  64. if s == "" {
  65. return req, nil // No HTTP headers or body.
  66. }
  67. eList := strings.Split(s, ", ")
  68. var initialOffset, reqHdrLen, respHdrLen int
  69. var hasBody bool
  70. var prevKey string
  71. var prevValue int
  72. for _, item := range eList {
  73. eq := strings.Index(item, "=")
  74. if eq == -1 {
  75. return nil, &badStringError{"malformed Encapsulated: header", s}
  76. }
  77. key := item[:eq]
  78. value, err := strconv.Atoi(item[eq+1:])
  79. if err != nil {
  80. return nil, &badStringError{"malformed Encapsulated: header", s}
  81. }
  82. // Calculate the length of the previous section.
  83. switch prevKey {
  84. case "":
  85. initialOffset = value
  86. case "req-hdr":
  87. reqHdrLen = value - prevValue
  88. case "res-hdr":
  89. respHdrLen = value - prevValue
  90. case "req-body", "opt-body", "res-body", "null-body":
  91. return nil, fmt.Errorf("%s must be the last section", prevKey)
  92. }
  93. switch key {
  94. case "req-hdr", "res-hdr", "null-body":
  95. case "req-body", "res-body", "opt-body":
  96. hasBody = true
  97. default:
  98. return nil, &badStringError{"invalid key for Encapsulated: header", key}
  99. }
  100. prevValue = value
  101. prevKey = key
  102. }
  103. // Read the HTTP headers.
  104. var rawReqHdr, rawRespHdr []byte
  105. if initialOffset > 0 {
  106. junk := make([]byte, initialOffset)
  107. _, err = io.ReadFull(b, junk)
  108. if err != nil {
  109. return nil, err
  110. }
  111. }
  112. if reqHdrLen > 0 {
  113. rawReqHdr = make([]byte, reqHdrLen)
  114. _, err = io.ReadFull(b, rawReqHdr)
  115. if err != nil {
  116. return nil, err
  117. }
  118. }
  119. if respHdrLen > 0 {
  120. rawRespHdr = make([]byte, respHdrLen)
  121. _, err = io.ReadFull(b, rawRespHdr)
  122. if err != nil {
  123. return nil, err
  124. }
  125. }
  126. var bodyReader io.ReadCloser = emptyReader(0)
  127. if hasBody {
  128. if p := req.Header.Get("Preview"); p != "" {
  129. moreBody := true
  130. req.Preview, err = ioutil.ReadAll(newChunkedReader(b))
  131. if err != nil {
  132. if strings.Contains(err.Error(), "ieof") {
  133. // The data ended with "0; ieof", which the HTTP chunked reader doesn't understand.
  134. moreBody = false
  135. err = nil
  136. } else {
  137. return nil, err
  138. }
  139. }
  140. var r io.Reader = bytes.NewBuffer(req.Preview)
  141. if moreBody {
  142. r = io.MultiReader(r, &continueReader{buf: b})
  143. }
  144. bodyReader = ioutil.NopCloser(r)
  145. } else {
  146. bodyReader = ioutil.NopCloser(newChunkedReader(b))
  147. }
  148. }
  149. // Construct the http.Request.
  150. if rawReqHdr != nil {
  151. invalidURLEscapeFixed := false
  152. req.Request, err = http.ReadRequest(bufio.NewReader(bytes.NewBuffer(rawReqHdr)))
  153. if err != nil && strings.Contains(err.Error(), "invalid URL escape") {
  154. //Fix the request url
  155. // Convert the rawReqHdr to string
  156. // find the url\path start and end(sould be in the status line
  157. // convert the percents into %25
  158. // Then reparse the whole request
  159. rawReqHdrStr := string(rawReqHdr)
  160. result := strings.Split(rawReqHdrStr, "\n")
  161. result[0] = strings.Replace(result[0], "%", "%25", -1)
  162. // The next is a compromise since when adding "\r\n" it causes the request parsing to fail
  163. newReq := strings.Join(result, "\n")
  164. req.Request, err = http.ReadRequest(bufio.NewReader(bytes.NewBuffer([]byte(newReq))))
  165. if err != nil {
  166. return req, fmt.Errorf("error while parsing HTTP request: %v", err)
  167. }
  168. invalidURLEscapeFixed = true
  169. }
  170. if err != nil && !invalidURLEscapeFixed {
  171. return req, fmt.Errorf("error while parsing HTTP request: %v", err)
  172. }
  173. if req.Method == "REQMOD" {
  174. req.Request.Body = bodyReader
  175. } else {
  176. req.Request.Body = emptyReader(0)
  177. }
  178. }
  179. // Construct the http.Response.
  180. if rawRespHdr != nil {
  181. request := req.Request
  182. if request == nil {
  183. request, _ = http.NewRequest("GET", "/", nil)
  184. }
  185. req.Response, err = http.ReadResponse(bufio.NewReader(bytes.NewBuffer(rawRespHdr)), request)
  186. if err != nil {
  187. return req, fmt.Errorf("error while parsing HTTP response: %v", err)
  188. }
  189. if req.Method == "RESPMOD" {
  190. req.Response.Body = bodyReader
  191. } else {
  192. req.Response.Body = emptyReader(0)
  193. }
  194. }
  195. return
  196. }
  197. // An emptyReader is an io.ReadCloser that always returns os.EOF.
  198. type emptyReader byte
  199. func (emptyReader) Read(p []byte) (n int, err error) {
  200. return 0, io.EOF
  201. }
  202. func (emptyReader) Close() error {
  203. return nil
  204. }
  205. // A continueReader sends a "100 Continue" message the first time Read
  206. // is called, creates a ChunkedReader, and reads from that.
  207. type continueReader struct {
  208. buf *bufio.ReadWriter // the underlying connection
  209. cr io.Reader // the ChunkedReader
  210. }
  211. func (c *continueReader) Read(p []byte) (n int, err error) {
  212. if c.cr == nil {
  213. _, err := c.buf.WriteString("ICAP/1.0 100 Continue\r\n\r\n")
  214. if err != nil {
  215. return 0, err
  216. }
  217. err = c.buf.Flush()
  218. if err != nil {
  219. return 0, err
  220. }
  221. c.cr = newChunkedReader(c.buf)
  222. }
  223. return c.cr.Read(p)
  224. }