util_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package server
  2. import (
  3. "bytes"
  4. "crypto/rand"
  5. "fmt"
  6. "heckel.io/ntfy/v2/user"
  7. "net/http"
  8. "net/netip"
  9. "strings"
  10. "testing"
  11. "github.com/stretchr/testify/require"
  12. )
  13. func TestReadBoolParam(t *testing.T) {
  14. r, _ := http.NewRequest("GET", "https://ntfy.sh/mytopic?up=1&firebase=no", nil)
  15. up := readBoolParam(r, false, "x-up", "up")
  16. firebase := readBoolParam(r, true, "x-firebase", "firebase")
  17. require.Equal(t, true, up)
  18. require.Equal(t, false, firebase)
  19. r, _ = http.NewRequest("GET", "https://ntfy.sh/mytopic", nil)
  20. r.Header.Set("X-Up", "yes")
  21. r.Header.Set("X-Firebase", "0")
  22. up = readBoolParam(r, false, "x-up", "up")
  23. firebase = readBoolParam(r, true, "x-firebase", "firebase")
  24. require.Equal(t, true, up)
  25. require.Equal(t, false, firebase)
  26. r, _ = http.NewRequest("GET", "https://ntfy.sh/mytopic", nil)
  27. up = readBoolParam(r, false, "x-up", "up")
  28. firebase = readBoolParam(r, true, "x-up", "up")
  29. require.Equal(t, false, up)
  30. require.Equal(t, true, firebase)
  31. }
  32. func TestRenderHTTPRequest_ValidShort(t *testing.T) {
  33. r, _ := http.NewRequest("POST", "http://ntfy.sh/mytopic?p=2", strings.NewReader("some message"))
  34. r.Header.Set("Title", "A title")
  35. expected := `POST /mytopic?p=2 HTTP/1.1
  36. Title: A title
  37. some message`
  38. require.Equal(t, expected, renderHTTPRequest(r))
  39. }
  40. func TestRenderHTTPRequest_ValidLong(t *testing.T) {
  41. body := strings.Repeat("a", 5000)
  42. r, _ := http.NewRequest("POST", "http://ntfy.sh/mytopic?p=2", strings.NewReader(body))
  43. r.Header.Set("Accept", "*/*")
  44. expected := `POST /mytopic?p=2 HTTP/1.1
  45. Accept: */*
  46. ` + strings.Repeat("a", 4096) + " ... (peeked 4096 bytes)"
  47. require.Equal(t, expected, renderHTTPRequest(r))
  48. }
  49. func TestRenderHTTPRequest_InvalidShort(t *testing.T) {
  50. body := []byte{0xc3, 0x28}
  51. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", bytes.NewReader(body))
  52. r.Header.Set("Accept", "*/*")
  53. expected := `GET /mytopic/json?since=all HTTP/1.1
  54. Accept: */*
  55. (peeked bytes not UTF-8, 2 bytes, hex: c328)`
  56. require.Equal(t, expected, renderHTTPRequest(r))
  57. }
  58. func TestRenderHTTPRequest_InvalidLong(t *testing.T) {
  59. body := make([]byte, 5000)
  60. rand.Read(body)
  61. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", bytes.NewReader(body))
  62. r.Header.Set("Accept", "*/*")
  63. expected := `GET /mytopic/json?since=all HTTP/1.1
  64. Accept: */*
  65. (peeked bytes not UTF-8, peek limit of 4096 bytes reached, hex: ` + fmt.Sprintf("%x", body[:4096]) + ` ...)`
  66. require.Equal(t, expected, renderHTTPRequest(r))
  67. }
  68. func TestMaybeIgnoreSpecialHeader(t *testing.T) {
  69. require.Empty(t, maybeIgnoreSpecialHeader("priority", "u=1"))
  70. require.Empty(t, maybeIgnoreSpecialHeader("Priority", "u=1"))
  71. require.Empty(t, maybeIgnoreSpecialHeader("Priority", "u=1, i"))
  72. }
  73. func TestMaybeDecodeHeaders(t *testing.T) {
  74. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", nil)
  75. r.Header.Set("Priority", "u=1") // Cloudflare priority header
  76. r.Header.Set("X-Priority", "5") // ntfy priority header
  77. require.Equal(t, "5", readHeaderParam(r, "x-priority", "priority", "p"))
  78. }
  79. func TestExtractIPAddress(t *testing.T) {
  80. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", nil)
  81. r.RemoteAddr = "10.0.0.1:1234"
  82. r.Header.Set("X-Forwarded-For", " 1.2.3.4 , 5.6.7.8")
  83. r.Header.Set("X-Client-IP", "9.10.11.12")
  84. r.Header.Set("X-Real-IP", "13.14.15.16, 1.1.1.1")
  85. r.Header.Set("Forwarded", "for=17.18.19.20;by=proxy.example.com, by=2.2.2.2;for=1.1.1.1")
  86. trustedProxies := []netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")}
  87. require.Equal(t, "5.6.7.8", extractIPAddress(r, true, "X-Forwarded-For", trustedProxies).String())
  88. require.Equal(t, "9.10.11.12", extractIPAddress(r, true, "X-Client-IP", trustedProxies).String())
  89. require.Equal(t, "13.14.15.16", extractIPAddress(r, true, "X-Real-IP", trustedProxies).String())
  90. require.Equal(t, "17.18.19.20", extractIPAddress(r, true, "Forwarded", trustedProxies).String())
  91. require.Equal(t, "10.0.0.1", extractIPAddress(r, false, "X-Forwarded-For", trustedProxies).String())
  92. }
  93. func TestExtractIPAddress_UnixSocket(t *testing.T) {
  94. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", nil)
  95. r.RemoteAddr = "@"
  96. r.Header.Set("X-Forwarded-For", "1.2.3.4, 5.6.7.8, 1.1.1.1")
  97. r.Header.Set("Forwarded", "by=bla.example.com;for=17.18.19.20")
  98. trustedProxies := []netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")}
  99. require.Equal(t, "5.6.7.8", extractIPAddress(r, true, "X-Forwarded-For", trustedProxies).String())
  100. require.Equal(t, "17.18.19.20", extractIPAddress(r, true, "Forwarded", trustedProxies).String())
  101. require.Equal(t, "0.0.0.0", extractIPAddress(r, false, "X-Forwarded-For", trustedProxies).String())
  102. }
  103. func TestExtractIPAddress_MixedIPv4IPv6(t *testing.T) {
  104. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", nil)
  105. r.RemoteAddr = "[2001:db8:abcd::1]:1234"
  106. r.Header.Set("X-Forwarded-For", "1.2.3.4, 2001:db8:abcd::2, 5.6.7.8")
  107. trustedProxies := []netip.Prefix{netip.MustParsePrefix("1.2.3.0/24")}
  108. require.Equal(t, "5.6.7.8", extractIPAddress(r, true, "X-Forwarded-For", trustedProxies).String())
  109. }
  110. func TestExtractIPAddress_TrustedIPv6Prefix(t *testing.T) {
  111. r, _ := http.NewRequest("GET", "http://ntfy.sh/mytopic/json?since=all", nil)
  112. r.RemoteAddr = "[2001:db8:abcd::1]:1234"
  113. r.Header.Set("X-Forwarded-For", "2001:db8:aaaa::1, 2001:db8:aaaa::2, 2001:db8:abcd:2::3")
  114. trustedProxies := []netip.Prefix{netip.MustParsePrefix("2001:db8:aaaa::/48")}
  115. require.Equal(t, "2001:db8:abcd:2::3", extractIPAddress(r, true, "X-Forwarded-For", trustedProxies).String())
  116. }
  117. func TestVisitorID(t *testing.T) {
  118. confWithDefaults := &Config{
  119. VisitorPrefixBitsIPv4: 32,
  120. VisitorPrefixBitsIPv6: 64,
  121. }
  122. confWithShortenedPrefixes := &Config{
  123. VisitorPrefixBitsIPv4: 16,
  124. VisitorPrefixBitsIPv6: 56,
  125. }
  126. userWithTier := &user.User{
  127. ID: "u_123",
  128. Tier: &user.Tier{},
  129. }
  130. require.Equal(t, "ip:1.2.3.4", visitorID(netip.MustParseAddr("1.2.3.4"), nil, confWithDefaults))
  131. require.Equal(t, "ip:2a01:599:b26:2397::", visitorID(netip.MustParseAddr("2a01:599:b26:2397:dbe7:5aa2:95ce:1e83"), nil, confWithDefaults))
  132. require.Equal(t, "ip:2001:db8:25:86::", visitorID(netip.MustParseAddr("2001:db8:25:86:1::1"), nil, confWithDefaults))
  133. require.Equal(t, "ip:2001:db8:25:86::", visitorID(netip.MustParseAddr("2001:db8:25:86:2::1"), nil, confWithDefaults))
  134. require.Equal(t, "user:u_123", visitorID(netip.MustParseAddr("1.2.3.4"), userWithTier, confWithDefaults))
  135. require.Equal(t, "user:u_123", visitorID(netip.MustParseAddr("2a01:599:b26:2397:dbe7:5aa2:95ce:1e83"), userWithTier, confWithDefaults))
  136. require.Equal(t, "ip:1.2.0.0", visitorID(netip.MustParseAddr("1.2.3.4"), nil, confWithShortenedPrefixes))
  137. require.Equal(t, "ip:2a01:599:b26:2300::", visitorID(netip.MustParseAddr("2a01:599:b26:2397:dbe7:5aa2:95ce:1e83"), nil, confWithShortenedPrefixes))
  138. }