requests_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. package httpx
  2. import (
  3. "errors"
  4. "net/http"
  5. "net/http/httptest"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "testing"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/zeromicro/go-zero/rest/internal/header"
  12. "github.com/zeromicro/go-zero/rest/pathvar"
  13. )
  14. func TestParseForm(t *testing.T) {
  15. var v struct {
  16. Name string `form:"name"`
  17. Age int `form:"age"`
  18. Percent float64 `form:"percent,optional"`
  19. }
  20. r, err := http.NewRequest(http.MethodGet, "/a?name=hello&age=18&percent=3.4", http.NoBody)
  21. assert.Nil(t, err)
  22. assert.Nil(t, Parse(r, &v))
  23. assert.Equal(t, "hello", v.Name)
  24. assert.Equal(t, 18, v.Age)
  25. assert.Equal(t, 3.4, v.Percent)
  26. }
  27. func TestParseForm_Error(t *testing.T) {
  28. var v struct {
  29. Name string `form:"name"`
  30. Age int `form:"age"`
  31. }
  32. r := httptest.NewRequest(http.MethodGet, "/a?name=hello;", http.NoBody)
  33. assert.NotNil(t, ParseForm(r, &v))
  34. }
  35. func TestParseHeader(t *testing.T) {
  36. tests := []struct {
  37. name string
  38. value string
  39. expect map[string]string
  40. }{
  41. {
  42. name: "empty",
  43. value: "",
  44. expect: map[string]string{},
  45. },
  46. {
  47. name: "regular",
  48. value: "key=value",
  49. expect: map[string]string{"key": "value"},
  50. },
  51. {
  52. name: "next empty",
  53. value: "key=value;",
  54. expect: map[string]string{"key": "value"},
  55. },
  56. {
  57. name: "regular",
  58. value: "key=value;foo",
  59. expect: map[string]string{"key": "value"},
  60. },
  61. }
  62. for _, test := range tests {
  63. test := test
  64. t.Run(test.name, func(t *testing.T) {
  65. t.Parallel()
  66. m := ParseHeader(test.value)
  67. assert.EqualValues(t, test.expect, m)
  68. })
  69. }
  70. }
  71. func TestParsePath(t *testing.T) {
  72. var v struct {
  73. Name string `path:"name"`
  74. Age int `path:"age"`
  75. }
  76. r := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
  77. r = pathvar.WithVars(r, map[string]string{
  78. "name": "foo",
  79. "age": "18",
  80. })
  81. err := Parse(r, &v)
  82. assert.Nil(t, err)
  83. assert.Equal(t, "foo", v.Name)
  84. assert.Equal(t, 18, v.Age)
  85. }
  86. func TestParsePath_Error(t *testing.T) {
  87. var v struct {
  88. Name string `path:"name"`
  89. Age int `path:"age"`
  90. }
  91. r := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
  92. r = pathvar.WithVars(r, map[string]string{
  93. "name": "foo",
  94. })
  95. assert.NotNil(t, Parse(r, &v))
  96. }
  97. func TestParseFormOutOfRange(t *testing.T) {
  98. var v struct {
  99. Age int `form:"age,range=[10:20)"`
  100. }
  101. tests := []struct {
  102. url string
  103. pass bool
  104. }{
  105. {
  106. url: "/a?age=5",
  107. pass: false,
  108. },
  109. {
  110. url: "/a?age=10",
  111. pass: true,
  112. },
  113. {
  114. url: "/a?age=15",
  115. pass: true,
  116. },
  117. {
  118. url: "/a?age=20",
  119. pass: false,
  120. },
  121. {
  122. url: "/a?age=28",
  123. pass: false,
  124. },
  125. }
  126. for _, test := range tests {
  127. r, err := http.NewRequest(http.MethodGet, test.url, http.NoBody)
  128. assert.Nil(t, err)
  129. err = Parse(r, &v)
  130. if test.pass {
  131. assert.Nil(t, err)
  132. } else {
  133. assert.NotNil(t, err)
  134. }
  135. }
  136. }
  137. func TestParseMultipartForm(t *testing.T) {
  138. var v struct {
  139. Name string `form:"name"`
  140. Age int `form:"age"`
  141. }
  142. body := strings.Replace(`----------------------------220477612388154780019383
  143. Content-Disposition: form-data; name="name"
  144. kevin
  145. ----------------------------220477612388154780019383
  146. Content-Disposition: form-data; name="age"
  147. 18
  148. ----------------------------220477612388154780019383--`, "\n", "\r\n", -1)
  149. r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
  150. r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383")
  151. assert.Nil(t, Parse(r, &v))
  152. assert.Equal(t, "kevin", v.Name)
  153. assert.Equal(t, 18, v.Age)
  154. }
  155. func TestParseMultipartFormWrongBoundary(t *testing.T) {
  156. var v struct {
  157. Name string `form:"name"`
  158. Age int `form:"age"`
  159. }
  160. body := strings.Replace(`----------------------------22047761238815478001938
  161. Content-Disposition: form-data; name="name"
  162. kevin
  163. ----------------------------22047761238815478001938
  164. Content-Disposition: form-data; name="age"
  165. 18
  166. ----------------------------22047761238815478001938--`, "\n", "\r\n", -1)
  167. r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
  168. r.Header.Set(ContentType, "multipart/form-data; boundary=--------------------------220477612388154780019383")
  169. assert.NotNil(t, Parse(r, &v))
  170. }
  171. func TestParseJsonBody(t *testing.T) {
  172. t.Run("has body", func(t *testing.T) {
  173. var v struct {
  174. Name string `json:"name"`
  175. Age int `json:"age"`
  176. }
  177. body := `{"name":"kevin", "age": 18}`
  178. r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
  179. r.Header.Set(ContentType, header.JsonContentType)
  180. if assert.NoError(t, Parse(r, &v)) {
  181. assert.Equal(t, "kevin", v.Name)
  182. assert.Equal(t, 18, v.Age)
  183. }
  184. })
  185. t.Run("bad body", func(t *testing.T) {
  186. var v struct {
  187. Name string `json:"name"`
  188. Age int `json:"age"`
  189. }
  190. body := `{"name":"kevin", "ag": 18}`
  191. r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
  192. r.Header.Set(ContentType, header.JsonContentType)
  193. assert.Error(t, Parse(r, &v))
  194. })
  195. t.Run("hasn't body", func(t *testing.T) {
  196. var v struct {
  197. Name string `json:"name,optional"`
  198. Age int `json:"age,optional"`
  199. }
  200. r := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
  201. assert.Nil(t, Parse(r, &v))
  202. assert.Equal(t, "", v.Name)
  203. assert.Equal(t, 0, v.Age)
  204. })
  205. t.Run("array body", func(t *testing.T) {
  206. var v []struct {
  207. Name string `json:"name"`
  208. Age int `json:"age"`
  209. }
  210. body := `[{"name":"kevin", "age": 18}]`
  211. r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
  212. r.Header.Set(ContentType, header.JsonContentType)
  213. assert.NoError(t, ParseJsonBody(r, &v))
  214. assert.Equal(t, 1, len(v))
  215. assert.Equal(t, "kevin", v[0].Name)
  216. assert.Equal(t, 18, v[0].Age)
  217. })
  218. }
  219. func TestParseRequired(t *testing.T) {
  220. v := struct {
  221. Name string `form:"name"`
  222. Percent float64 `form:"percent"`
  223. }{}
  224. r, err := http.NewRequest(http.MethodGet, "/a?name=hello", http.NoBody)
  225. assert.Nil(t, err)
  226. assert.NotNil(t, Parse(r, &v))
  227. }
  228. func TestParseOptions(t *testing.T) {
  229. v := struct {
  230. Position int8 `form:"pos,options=1|2"`
  231. }{}
  232. r, err := http.NewRequest(http.MethodGet, "/a?pos=4", http.NoBody)
  233. assert.Nil(t, err)
  234. assert.NotNil(t, Parse(r, &v))
  235. }
  236. func TestParseHeaders(t *testing.T) {
  237. type AnonymousStruct struct {
  238. XRealIP string `header:"x-real-ip"`
  239. Accept string `header:"Accept,optional"`
  240. }
  241. v := struct {
  242. Name string `header:"name,optional"`
  243. Percent string `header:"percent"`
  244. Addrs []string `header:"addrs"`
  245. XForwardedFor string `header:"X-Forwarded-For,optional"`
  246. AnonymousStruct
  247. }{}
  248. request, err := http.NewRequest("POST", "/", http.NoBody)
  249. if err != nil {
  250. t.Fatal(err)
  251. }
  252. request.Header.Set("name", "chenquan")
  253. request.Header.Set("percent", "1")
  254. request.Header.Add("addrs", "addr1")
  255. request.Header.Add("addrs", "addr2")
  256. request.Header.Add("X-Forwarded-For", "10.0.10.11")
  257. request.Header.Add("x-real-ip", "10.0.11.10")
  258. request.Header.Add("Accept", header.JsonContentType)
  259. err = ParseHeaders(request, &v)
  260. if err != nil {
  261. t.Fatal(err)
  262. }
  263. assert.Equal(t, "chenquan", v.Name)
  264. assert.Equal(t, "1", v.Percent)
  265. assert.Equal(t, []string{"addr1", "addr2"}, v.Addrs)
  266. assert.Equal(t, "10.0.10.11", v.XForwardedFor)
  267. assert.Equal(t, "10.0.11.10", v.XRealIP)
  268. assert.Equal(t, header.JsonContentType, v.Accept)
  269. }
  270. func TestParseHeaders_Error(t *testing.T) {
  271. v := struct {
  272. Name string `header:"name"`
  273. Age int `header:"age"`
  274. }{}
  275. r := httptest.NewRequest("POST", "/", http.NoBody)
  276. r.Header.Set("name", "foo")
  277. assert.NotNil(t, Parse(r, &v))
  278. }
  279. func TestParseWithValidator(t *testing.T) {
  280. SetValidator(mockValidator{})
  281. var v struct {
  282. Name string `form:"name"`
  283. Age int `form:"age"`
  284. Percent float64 `form:"percent,optional"`
  285. }
  286. r, err := http.NewRequest(http.MethodGet, "/a?name=hello&age=18&percent=3.4", http.NoBody)
  287. assert.Nil(t, err)
  288. if assert.NoError(t, Parse(r, &v)) {
  289. assert.Equal(t, "hello", v.Name)
  290. assert.Equal(t, 18, v.Age)
  291. assert.Equal(t, 3.4, v.Percent)
  292. }
  293. }
  294. func TestParseWithValidatorWithError(t *testing.T) {
  295. SetValidator(mockValidator{})
  296. var v struct {
  297. Name string `form:"name"`
  298. Age int `form:"age"`
  299. Percent float64 `form:"percent,optional"`
  300. }
  301. r, err := http.NewRequest(http.MethodGet, "/a?name=world&age=18&percent=3.4", http.NoBody)
  302. assert.Nil(t, err)
  303. assert.Error(t, Parse(r, &v))
  304. }
  305. func TestParseWithValidatorRequest(t *testing.T) {
  306. SetValidator(mockValidator{})
  307. var v mockRequest
  308. r, err := http.NewRequest(http.MethodGet, "/a?&age=18", http.NoBody)
  309. assert.Nil(t, err)
  310. assert.Error(t, Parse(r, &v))
  311. }
  312. func BenchmarkParseRaw(b *testing.B) {
  313. r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody)
  314. if err != nil {
  315. b.Fatal(err)
  316. }
  317. for i := 0; i < b.N; i++ {
  318. v := struct {
  319. Name string `form:"name"`
  320. Age int `form:"age"`
  321. Percent float64 `form:"percent,optional"`
  322. }{}
  323. v.Name = r.FormValue("name")
  324. v.Age, err = strconv.Atoi(r.FormValue("age"))
  325. if err != nil {
  326. b.Fatal(err)
  327. }
  328. v.Percent, err = strconv.ParseFloat(r.FormValue("percent"), 64)
  329. if err != nil {
  330. b.Fatal(err)
  331. }
  332. }
  333. }
  334. func BenchmarkParseAuto(b *testing.B) {
  335. r, err := http.NewRequest(http.MethodGet, "http://hello.com/a?name=hello&age=18&percent=3.4", http.NoBody)
  336. if err != nil {
  337. b.Fatal(err)
  338. }
  339. for i := 0; i < b.N; i++ {
  340. v := struct {
  341. Name string `form:"name"`
  342. Age int `form:"age"`
  343. Percent float64 `form:"percent,optional"`
  344. }{}
  345. if err = Parse(r, &v); err != nil {
  346. b.Fatal(err)
  347. }
  348. }
  349. }
  350. type mockValidator struct{}
  351. func (m mockValidator) Validate(r *http.Request, data any) error {
  352. if r.URL.Path == "/a" {
  353. val := reflect.ValueOf(data).Elem().FieldByName("Name").String()
  354. if val != "hello" {
  355. return errors.New("name is not hello")
  356. }
  357. }
  358. return nil
  359. }
  360. type mockRequest struct {
  361. Name string `json:"name,optional"`
  362. }
  363. func (m mockRequest) Validate() error {
  364. if m.Name != "hello" {
  365. return errors.New("name is not hello")
  366. }
  367. return nil
  368. }