bytesconv.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //go:generate go run bytesconv_table_gen.go
  2. package fasthttp
  3. import (
  4. "bufio"
  5. "bytes"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net"
  10. "net/http"
  11. "strconv"
  12. "sync"
  13. "time"
  14. )
  15. // AppendHTMLEscape appends html-escaped s to dst and returns the extended dst.
  16. func AppendHTMLEscape(dst []byte, s string) []byte {
  17. var (
  18. prev int
  19. sub string
  20. )
  21. for i, n := 0, len(s); i < n; i++ {
  22. sub = ""
  23. switch s[i] {
  24. case '&':
  25. sub = "&amp;"
  26. case '<':
  27. sub = "&lt;"
  28. case '>':
  29. sub = "&gt;"
  30. case '"':
  31. sub = "&#34;" // "&#34;" is shorter than "&quot;".
  32. case '\'':
  33. sub = "&#39;" // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
  34. }
  35. if sub != "" {
  36. dst = append(dst, s[prev:i]...)
  37. dst = append(dst, sub...)
  38. prev = i + 1
  39. }
  40. }
  41. return append(dst, s[prev:]...)
  42. }
  43. // AppendHTMLEscapeBytes appends html-escaped s to dst and returns
  44. // the extended dst.
  45. func AppendHTMLEscapeBytes(dst, s []byte) []byte {
  46. return AppendHTMLEscape(dst, b2s(s))
  47. }
  48. // AppendIPv4 appends string representation of the given ip v4 to dst
  49. // and returns the extended dst.
  50. func AppendIPv4(dst []byte, ip net.IP) []byte {
  51. ip = ip.To4()
  52. if ip == nil {
  53. return append(dst, "non-v4 ip passed to AppendIPv4"...)
  54. }
  55. dst = AppendUint(dst, int(ip[0]))
  56. for i := 1; i < 4; i++ {
  57. dst = append(dst, '.')
  58. dst = AppendUint(dst, int(ip[i]))
  59. }
  60. return dst
  61. }
  62. var errEmptyIPStr = errors.New("empty ip address string")
  63. var httpDateGMT = time.FixedZone("GMT", 0)
  64. // ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
  65. func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) {
  66. if len(ipStr) == 0 {
  67. return dst, errEmptyIPStr
  68. }
  69. if len(dst) < net.IPv4len || len(dst) > net.IPv4len {
  70. dst = make([]byte, net.IPv4len)
  71. }
  72. copy(dst, net.IPv4zero)
  73. dst = dst.To4() // dst is always non-nil here
  74. b := ipStr
  75. for i := range 3 {
  76. n := bytes.IndexByte(b, '.')
  77. if n < 0 {
  78. return dst, fmt.Errorf("cannot find dot in ipStr %q", ipStr)
  79. }
  80. octet, parsed, err := parseIPv4Octet(b[:n])
  81. if err != nil {
  82. if errors.Is(err, errIPv4PartTooLarge) {
  83. return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, parsed)
  84. }
  85. return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
  86. }
  87. dst[i] = octet
  88. b = b[n+1:]
  89. }
  90. octet, parsed, err := parseIPv4Octet(b)
  91. if err != nil {
  92. if errors.Is(err, errIPv4PartTooLarge) {
  93. return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, parsed)
  94. }
  95. return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
  96. }
  97. dst[3] = octet
  98. return dst, nil
  99. }
  100. // AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
  101. // to dst and returns the extended dst.
  102. func AppendHTTPDate(dst []byte, date time.Time) []byte {
  103. dst = date.In(time.UTC).AppendFormat(dst, time.RFC1123)
  104. copy(dst[len(dst)-3:], strGMT)
  105. return dst
  106. }
  107. // ParseHTTPDate parses HTTP-compliant (RFC1123) date.
  108. func ParseHTTPDate(date []byte) (time.Time, error) {
  109. if t, ok := parseRFC1123DateGMT(date); ok {
  110. return t, nil
  111. }
  112. return time.Parse(http.TimeFormat, b2s(date))
  113. }
  114. func parseRFC1123DateGMT(b []byte) (time.Time, bool) {
  115. // Expects "Mon, 02 Jan 2006 15:04:05 GMT".
  116. if len(b) != 29 {
  117. return time.Time{}, false
  118. }
  119. if !isWeekday3(b[0], b[1], b[2]) {
  120. return time.Time{}, false
  121. }
  122. if b[3] != ',' || b[4] != ' ' || b[7] != ' ' || b[11] != ' ' ||
  123. b[16] != ' ' || b[19] != ':' || b[22] != ':' || b[25] != ' ' {
  124. return time.Time{}, false
  125. }
  126. if b[26] != 'G' || b[27] != 'M' || b[28] != 'T' {
  127. return time.Time{}, false
  128. }
  129. day, ok := parse2Digits(b[5], b[6])
  130. if !ok || day < 1 || day > 31 {
  131. return time.Time{}, false
  132. }
  133. month, ok := parseMonth3(b[8], b[9], b[10])
  134. if !ok {
  135. return time.Time{}, false
  136. }
  137. year, ok := parse4Digits(b[12], b[13], b[14], b[15])
  138. if !ok {
  139. return time.Time{}, false
  140. }
  141. hour, ok := parse2Digits(b[17], b[18])
  142. if !ok || hour > 23 {
  143. return time.Time{}, false
  144. }
  145. minute, ok := parse2Digits(b[20], b[21])
  146. if !ok || minute > 59 {
  147. return time.Time{}, false
  148. }
  149. second, ok := parse2Digits(b[23], b[24])
  150. if !ok || second > 59 {
  151. return time.Time{}, false
  152. }
  153. t := time.Date(year, month, day, hour, minute, second, 0, httpDateGMT)
  154. // Reject calendar-invalid dates like "31 Feb", which time.Date normalizes.
  155. if t.Year() != year || t.Month() != month || t.Day() != day {
  156. return time.Time{}, false
  157. }
  158. return t, true
  159. }
  160. func isWeekday3(a, b, c byte) bool {
  161. a |= 0x20
  162. b |= 0x20
  163. c |= 0x20
  164. k := uint32(a)<<16 | uint32(b)<<8 | uint32(c)
  165. switch k {
  166. case uint32('m')<<16 | uint32('o')<<8 | uint32('n'),
  167. uint32('t')<<16 | uint32('u')<<8 | uint32('e'),
  168. uint32('w')<<16 | uint32('e')<<8 | uint32('d'),
  169. uint32('t')<<16 | uint32('h')<<8 | uint32('u'),
  170. uint32('f')<<16 | uint32('r')<<8 | uint32('i'),
  171. uint32('s')<<16 | uint32('a')<<8 | uint32('t'),
  172. uint32('s')<<16 | uint32('u')<<8 | uint32('n'):
  173. return true
  174. default:
  175. return false
  176. }
  177. }
  178. func parse2Digits(a, b byte) (int, bool) {
  179. if a < '0' || a > '9' || b < '0' || b > '9' {
  180. return 0, false
  181. }
  182. return int(a-'0')*10 + int(b-'0'), true
  183. }
  184. func parse4Digits(a, b, c, d byte) (int, bool) {
  185. v1, ok := parse2Digits(a, b)
  186. if !ok {
  187. return 0, false
  188. }
  189. v2, ok := parse2Digits(c, d)
  190. if !ok {
  191. return 0, false
  192. }
  193. return v1*100 + v2, true
  194. }
  195. func parseMonth3(a, b, c byte) (time.Month, bool) {
  196. a |= 0x20
  197. b |= 0x20
  198. c |= 0x20
  199. k := uint32(a)<<16 | uint32(b)<<8 | uint32(c)
  200. switch k {
  201. case uint32('j')<<16 | uint32('a')<<8 | uint32('n'):
  202. return time.January, true
  203. case uint32('f')<<16 | uint32('e')<<8 | uint32('b'):
  204. return time.February, true
  205. case uint32('m')<<16 | uint32('a')<<8 | uint32('r'):
  206. return time.March, true
  207. case uint32('a')<<16 | uint32('p')<<8 | uint32('r'):
  208. return time.April, true
  209. case uint32('m')<<16 | uint32('a')<<8 | uint32('y'):
  210. return time.May, true
  211. case uint32('j')<<16 | uint32('u')<<8 | uint32('n'):
  212. return time.June, true
  213. case uint32('j')<<16 | uint32('u')<<8 | uint32('l'):
  214. return time.July, true
  215. case uint32('a')<<16 | uint32('u')<<8 | uint32('g'):
  216. return time.August, true
  217. case uint32('s')<<16 | uint32('e')<<8 | uint32('p'):
  218. return time.September, true
  219. case uint32('o')<<16 | uint32('c')<<8 | uint32('t'):
  220. return time.October, true
  221. case uint32('n')<<16 | uint32('o')<<8 | uint32('v'):
  222. return time.November, true
  223. case uint32('d')<<16 | uint32('e')<<8 | uint32('c'):
  224. return time.December, true
  225. }
  226. return 0, false
  227. }
  228. // AppendUint appends n to dst and returns the extended dst.
  229. func AppendUint(dst []byte, n int) []byte {
  230. if n < 0 {
  231. // developer sanity-check
  232. panic("BUG: int must be positive")
  233. }
  234. return strconv.AppendUint(dst, uint64(n), 10)
  235. }
  236. // ParseUint parses uint from buf.
  237. func ParseUint(buf []byte) (int, error) {
  238. v, n, err := parseUintBuf(buf)
  239. if n != len(buf) {
  240. return -1, errUnexpectedTrailingChar
  241. }
  242. return v, err
  243. }
  244. var (
  245. errEmptyInt = errors.New("empty integer")
  246. errIPv4PartTooLarge = errors.New("ip part cannot exceed 255")
  247. errUnexpectedFirstChar = errors.New("unexpected first char found. Expecting 0-9")
  248. errUnexpectedTrailingChar = errors.New("unexpected trailing char found. Expecting 0-9")
  249. errTooLongInt = errors.New("too long int")
  250. )
  251. func parseUintBuf(b []byte) (int, int, error) {
  252. n := len(b)
  253. if n == 0 {
  254. return -1, 0, errEmptyInt
  255. }
  256. v := 0
  257. for i := range n {
  258. c := b[i]
  259. k := c - '0'
  260. if k > 9 {
  261. if i == 0 {
  262. return -1, i, errUnexpectedFirstChar
  263. }
  264. return v, i, nil
  265. }
  266. vNew := 10*v + int(k)
  267. // Test for overflow.
  268. if vNew < v {
  269. return -1, i, errTooLongInt
  270. }
  271. v = vNew
  272. }
  273. return v, n, nil
  274. }
  275. func parseIPv4Octet(b []byte) (byte, int, error) {
  276. if len(b) == 0 {
  277. return 0, 0, errEmptyInt
  278. }
  279. var (
  280. octet byte
  281. parsed int
  282. )
  283. for i := range len(b) {
  284. c := b[i]
  285. k := c - '0'
  286. if k > 9 {
  287. if i == 0 {
  288. return 0, parsed, errUnexpectedFirstChar
  289. }
  290. return 0, parsed, errUnexpectedTrailingChar
  291. }
  292. parsed = parsed*10 + int(k)
  293. if octet > 25 || (octet == 25 && k > 5) {
  294. return 0, parsed, errIPv4PartTooLarge
  295. }
  296. octet = octet*10 + k
  297. }
  298. return octet, parsed, nil
  299. }
  300. // ParseUfloat parses unsigned float from buf.
  301. func ParseUfloat(buf []byte) (float64, error) {
  302. // The implementation of parsing a float string is not easy.
  303. // We believe that the conservative approach is to call strconv.ParseFloat.
  304. // https://github.com/valyala/fasthttp/pull/1865
  305. res, err := strconv.ParseFloat(b2s(buf), 64)
  306. if res < 0 {
  307. return -1, errors.New("negative input is invalid")
  308. }
  309. if err != nil {
  310. return -1, err
  311. }
  312. return res, err
  313. }
  314. var (
  315. errEmptyHexNum = errors.New("empty hex number")
  316. errTooLargeHexNum = errors.New("too large hex number")
  317. )
  318. func readHexInt(r *bufio.Reader) (int, error) {
  319. var k, i, n int
  320. for {
  321. c, err := r.ReadByte()
  322. if err != nil {
  323. if err == io.EOF && i > 0 {
  324. return n, nil
  325. }
  326. return -1, err
  327. }
  328. k = int(hex2intTable[c])
  329. if k == 16 {
  330. if i == 0 {
  331. return -1, errEmptyHexNum
  332. }
  333. if err := r.UnreadByte(); err != nil {
  334. return -1, err
  335. }
  336. return n, nil
  337. }
  338. if i >= maxHexIntChars {
  339. return -1, errTooLargeHexNum
  340. }
  341. n = (n << 4) | k
  342. i++
  343. }
  344. }
  345. var hexIntBufPool sync.Pool
  346. func writeHexInt(w *bufio.Writer, n int) error {
  347. if n < 0 {
  348. // developer sanity-check
  349. panic("BUG: int must be positive")
  350. }
  351. v := hexIntBufPool.Get()
  352. if v == nil {
  353. v = make([]byte, maxHexIntChars+1)
  354. }
  355. buf := v.([]byte)
  356. i := len(buf) - 1
  357. for {
  358. buf[i] = lowerhex[n&0xf]
  359. n >>= 4
  360. if n == 0 {
  361. break
  362. }
  363. i--
  364. }
  365. _, err := w.Write(buf[i:])
  366. hexIntBufPool.Put(v)
  367. return err
  368. }
  369. const (
  370. upperhex = "0123456789ABCDEF"
  371. lowerhex = "0123456789abcdef"
  372. )
  373. func lowercaseBytes(b []byte) {
  374. for i := range b {
  375. p := &b[i]
  376. *p = toLowerTable[*p]
  377. }
  378. }
  379. // AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
  380. //
  381. // dst may point to src. In this case src will be overwritten.
  382. func AppendUnquotedArg(dst, src []byte) []byte {
  383. return decodeArgAppend(dst, src)
  384. }
  385. // AppendQuotedArg appends url-encoded src to dst and returns appended dst.
  386. func AppendQuotedArg(dst, src []byte) []byte {
  387. for _, c := range src {
  388. switch {
  389. case c == ' ':
  390. dst = append(dst, '+')
  391. case quotedArgShouldEscapeTable[int(c)] != 0:
  392. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  393. default:
  394. dst = append(dst, c)
  395. }
  396. }
  397. return dst
  398. }
  399. func appendQuotedPath(dst, src []byte) []byte {
  400. // Fix issue in https://github.com/golang/go/issues/11202
  401. if len(src) == 1 && src[0] == '*' {
  402. return append(dst, '*')
  403. }
  404. for _, c := range src {
  405. if quotedPathShouldEscapeTable[int(c)] != 0 {
  406. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  407. } else {
  408. dst = append(dst, c)
  409. }
  410. }
  411. return dst
  412. }