http_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  1. package etcdhttp
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "io"
  7. "net/http"
  8. "net/http/httptest"
  9. "net/url"
  10. "path"
  11. "reflect"
  12. "strings"
  13. "testing"
  14. "time"
  15. etcdErr "github.com/coreos/etcd/error"
  16. "github.com/coreos/etcd/etcdserver"
  17. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  18. "github.com/coreos/etcd/raft/raftpb"
  19. "github.com/coreos/etcd/store"
  20. "github.com/coreos/etcd/third_party/code.google.com/p/go.net/context"
  21. )
  22. func boolp(b bool) *bool { return &b }
  23. func mustNewURL(t *testing.T, s string) *url.URL {
  24. u, err := url.Parse(s)
  25. if err != nil {
  26. t.Fatalf("error creating URL from %q: %v", s, err)
  27. }
  28. return u
  29. }
  30. // mustNewRequest takes a path, appends it to the standard keysPrefix, and constructs
  31. // a GET *http.Request referencing the resulting URL
  32. func mustNewRequest(t *testing.T, p string) *http.Request {
  33. return mustNewMethodRequest(t, "GET", p)
  34. }
  35. func mustNewMethodRequest(t *testing.T, m, p string) *http.Request {
  36. return &http.Request{
  37. Method: m,
  38. URL: mustNewURL(t, path.Join(keysPrefix, p)),
  39. }
  40. }
  41. // mustNewForm takes a set of Values and constructs a PUT *http.Request,
  42. // with a URL constructed from appending the given path to the standard keysPrefix
  43. func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
  44. u := mustNewURL(t, path.Join(keysPrefix, p))
  45. req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
  46. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  47. if err != nil {
  48. t.Fatalf("error creating new request: %v", err)
  49. }
  50. return req
  51. }
  52. func TestBadParseRequest(t *testing.T) {
  53. tests := []struct {
  54. in *http.Request
  55. wcode int
  56. }{
  57. {
  58. // parseForm failure
  59. &http.Request{
  60. Body: nil,
  61. Method: "PUT",
  62. },
  63. etcdErr.EcodeInvalidForm,
  64. },
  65. {
  66. // bad key prefix
  67. &http.Request{
  68. URL: mustNewURL(t, "/badprefix/"),
  69. },
  70. etcdErr.EcodeInvalidForm,
  71. },
  72. // bad values for prevIndex, waitIndex, ttl
  73. {
  74. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"garbage"}}),
  75. etcdErr.EcodeIndexNaN,
  76. },
  77. {
  78. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"1.5"}}),
  79. etcdErr.EcodeIndexNaN,
  80. },
  81. {
  82. mustNewForm(t, "foo", url.Values{"prevIndex": []string{"-1"}}),
  83. etcdErr.EcodeIndexNaN,
  84. },
  85. {
  86. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"garbage"}}),
  87. etcdErr.EcodeIndexNaN,
  88. },
  89. {
  90. mustNewForm(t, "foo", url.Values{"waitIndex": []string{"??"}}),
  91. etcdErr.EcodeIndexNaN,
  92. },
  93. {
  94. mustNewForm(t, "foo", url.Values{"ttl": []string{"-1"}}),
  95. etcdErr.EcodeTTLNaN,
  96. },
  97. // bad values for recursive, sorted, wait, prevExist, stream
  98. {
  99. mustNewForm(t, "foo", url.Values{"recursive": []string{"hahaha"}}),
  100. etcdErr.EcodeInvalidField,
  101. },
  102. {
  103. mustNewForm(t, "foo", url.Values{"recursive": []string{"1234"}}),
  104. etcdErr.EcodeInvalidField,
  105. },
  106. {
  107. mustNewForm(t, "foo", url.Values{"recursive": []string{"?"}}),
  108. etcdErr.EcodeInvalidField,
  109. },
  110. {
  111. mustNewForm(t, "foo", url.Values{"sorted": []string{"?"}}),
  112. etcdErr.EcodeInvalidField,
  113. },
  114. {
  115. mustNewForm(t, "foo", url.Values{"sorted": []string{"x"}}),
  116. etcdErr.EcodeInvalidField,
  117. },
  118. {
  119. mustNewForm(t, "foo", url.Values{"wait": []string{"?!"}}),
  120. etcdErr.EcodeInvalidField,
  121. },
  122. {
  123. mustNewForm(t, "foo", url.Values{"wait": []string{"yes"}}),
  124. etcdErr.EcodeInvalidField,
  125. },
  126. {
  127. mustNewForm(t, "foo", url.Values{"prevExist": []string{"yes"}}),
  128. etcdErr.EcodeInvalidField,
  129. },
  130. {
  131. mustNewForm(t, "foo", url.Values{"prevExist": []string{"#2"}}),
  132. etcdErr.EcodeInvalidField,
  133. },
  134. {
  135. mustNewForm(t, "foo", url.Values{"stream": []string{"zzz"}}),
  136. etcdErr.EcodeInvalidField,
  137. },
  138. {
  139. mustNewForm(t, "foo", url.Values{"stream": []string{"something"}}),
  140. etcdErr.EcodeInvalidField,
  141. },
  142. // prevValue cannot be empty
  143. {
  144. mustNewForm(t, "foo", url.Values{"prevValue": []string{""}}),
  145. etcdErr.EcodeInvalidField,
  146. },
  147. // wait is only valid with GET requests
  148. {
  149. mustNewMethodRequest(t, "HEAD", "foo?wait=true"),
  150. etcdErr.EcodeInvalidField,
  151. },
  152. // query values are considered
  153. {
  154. mustNewRequest(t, "foo?prevExist=wrong"),
  155. etcdErr.EcodeInvalidField,
  156. },
  157. {
  158. mustNewRequest(t, "foo?ttl=wrong"),
  159. etcdErr.EcodeTTLNaN,
  160. },
  161. // but body takes precedence if both are specified
  162. {
  163. mustNewForm(
  164. t,
  165. "foo?ttl=12",
  166. url.Values{"ttl": []string{"garbage"}},
  167. ),
  168. etcdErr.EcodeTTLNaN,
  169. },
  170. {
  171. mustNewForm(
  172. t,
  173. "foo?prevExist=false",
  174. url.Values{"prevExist": []string{"yes"}},
  175. ),
  176. etcdErr.EcodeInvalidField,
  177. },
  178. }
  179. for i, tt := range tests {
  180. got, err := parseRequest(tt.in, 1234)
  181. if err == nil {
  182. t.Errorf("#%d: unexpected nil error!", i)
  183. continue
  184. }
  185. ee, ok := err.(*etcdErr.Error)
  186. if !ok {
  187. t.Errorf("#%d: err is not etcd.Error!", i)
  188. continue
  189. }
  190. if ee.ErrorCode != tt.wcode {
  191. t.Errorf("#%d: code=%d, want %v", i, ee.ErrorCode, tt.wcode)
  192. t.Logf("cause: %#v", ee.Cause)
  193. }
  194. if !reflect.DeepEqual(got, etcdserverpb.Request{}) {
  195. t.Errorf("#%d: unexpected non-empty Request: %#v", i, got)
  196. }
  197. }
  198. }
  199. func TestGoodParseRequest(t *testing.T) {
  200. tests := []struct {
  201. in *http.Request
  202. w etcdserverpb.Request
  203. }{
  204. {
  205. // good prefix, all other values default
  206. mustNewRequest(t, "foo"),
  207. etcdserverpb.Request{
  208. Id: 1234,
  209. Method: "GET",
  210. Path: "/foo",
  211. },
  212. },
  213. {
  214. // value specified
  215. mustNewForm(
  216. t,
  217. "foo",
  218. url.Values{"value": []string{"some_value"}},
  219. ),
  220. etcdserverpb.Request{
  221. Id: 1234,
  222. Method: "PUT",
  223. Val: "some_value",
  224. Path: "/foo",
  225. },
  226. },
  227. {
  228. // prevIndex specified
  229. mustNewForm(
  230. t,
  231. "foo",
  232. url.Values{"prevIndex": []string{"98765"}},
  233. ),
  234. etcdserverpb.Request{
  235. Id: 1234,
  236. Method: "PUT",
  237. PrevIndex: 98765,
  238. Path: "/foo",
  239. },
  240. },
  241. {
  242. // recursive specified
  243. mustNewForm(
  244. t,
  245. "foo",
  246. url.Values{"recursive": []string{"true"}},
  247. ),
  248. etcdserverpb.Request{
  249. Id: 1234,
  250. Method: "PUT",
  251. Recursive: true,
  252. Path: "/foo",
  253. },
  254. },
  255. {
  256. // sorted specified
  257. mustNewForm(
  258. t,
  259. "foo",
  260. url.Values{"sorted": []string{"true"}},
  261. ),
  262. etcdserverpb.Request{
  263. Id: 1234,
  264. Method: "PUT",
  265. Sorted: true,
  266. Path: "/foo",
  267. },
  268. },
  269. {
  270. // wait specified
  271. mustNewRequest(t, "foo?wait=true"),
  272. etcdserverpb.Request{
  273. Id: 1234,
  274. Method: "GET",
  275. Wait: true,
  276. Path: "/foo",
  277. },
  278. },
  279. {
  280. // zero TTL specified
  281. mustNewRequest(t, "foo?ttl=0"),
  282. etcdserverpb.Request{
  283. Id: 1234,
  284. Method: "GET",
  285. Path: "/foo",
  286. Expiration: 0,
  287. },
  288. },
  289. {
  290. // empty TTL specified
  291. mustNewRequest(t, "foo?ttl="),
  292. etcdserverpb.Request{
  293. Id: 1234,
  294. Method: "GET",
  295. Path: "/foo",
  296. Expiration: 0,
  297. },
  298. },
  299. {
  300. // prevExist should be non-null if specified
  301. mustNewForm(
  302. t,
  303. "foo",
  304. url.Values{"prevExist": []string{"true"}},
  305. ),
  306. etcdserverpb.Request{
  307. Id: 1234,
  308. Method: "PUT",
  309. PrevExist: boolp(true),
  310. Path: "/foo",
  311. },
  312. },
  313. {
  314. // prevExist should be non-null if specified
  315. mustNewForm(
  316. t,
  317. "foo",
  318. url.Values{"prevExist": []string{"false"}},
  319. ),
  320. etcdserverpb.Request{
  321. Id: 1234,
  322. Method: "PUT",
  323. PrevExist: boolp(false),
  324. Path: "/foo",
  325. },
  326. },
  327. // mix various fields
  328. {
  329. mustNewForm(
  330. t,
  331. "foo",
  332. url.Values{
  333. "value": []string{"some value"},
  334. "prevExist": []string{"true"},
  335. "prevValue": []string{"previous value"},
  336. },
  337. ),
  338. etcdserverpb.Request{
  339. Id: 1234,
  340. Method: "PUT",
  341. PrevExist: boolp(true),
  342. PrevValue: "previous value",
  343. Val: "some value",
  344. Path: "/foo",
  345. },
  346. },
  347. // query parameters should be used if given
  348. {
  349. mustNewForm(
  350. t,
  351. "foo?prevValue=woof",
  352. url.Values{},
  353. ),
  354. etcdserverpb.Request{
  355. Id: 1234,
  356. Method: "PUT",
  357. PrevValue: "woof",
  358. Path: "/foo",
  359. },
  360. },
  361. // but form values should take precedence over query parameters
  362. {
  363. mustNewForm(
  364. t,
  365. "foo?prevValue=woof",
  366. url.Values{
  367. "prevValue": []string{"miaow"},
  368. },
  369. ),
  370. etcdserverpb.Request{
  371. Id: 1234,
  372. Method: "PUT",
  373. PrevValue: "miaow",
  374. Path: "/foo",
  375. },
  376. },
  377. }
  378. for i, tt := range tests {
  379. got, err := parseRequest(tt.in, 1234)
  380. if err != nil {
  381. t.Errorf("#%d: err = %v, want %v", i, err, nil)
  382. }
  383. if !reflect.DeepEqual(got, tt.w) {
  384. t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
  385. }
  386. }
  387. // Test TTL separately until we don't rely on the time module...
  388. now := time.Now().UnixNano()
  389. req := mustNewForm(t, "foo", url.Values{"ttl": []string{"100"}})
  390. got, err := parseRequest(req, 1234)
  391. if err != nil {
  392. t.Fatalf("err = %v, want nil", err)
  393. }
  394. if got.Expiration <= now {
  395. t.Fatalf("expiration = %v, wanted > %v", got.Expiration, now)
  396. }
  397. }
  398. // eventingWatcher immediately returns a simple event of the given action on its channel
  399. type eventingWatcher struct {
  400. action string
  401. }
  402. func (w *eventingWatcher) EventChan() chan *store.Event {
  403. ch := make(chan *store.Event)
  404. go func() {
  405. ch <- &store.Event{
  406. Action: w.action,
  407. Node: &store.NodeExtern{},
  408. }
  409. }()
  410. return ch
  411. }
  412. func (w *eventingWatcher) Remove() {}
  413. func TestWriteError(t *testing.T) {
  414. // nil error should not panic
  415. rw := httptest.NewRecorder()
  416. writeError(rw, nil)
  417. h := rw.Header()
  418. if len(h) > 0 {
  419. t.Fatalf("unexpected non-empty headers: %#v", h)
  420. }
  421. b := rw.Body.String()
  422. if len(b) > 0 {
  423. t.Fatalf("unexpected non-empty body: %q", b)
  424. }
  425. tests := []struct {
  426. err error
  427. wcode int
  428. wi string
  429. }{
  430. {
  431. etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/foo/bar", 123),
  432. http.StatusNotFound,
  433. "123",
  434. },
  435. {
  436. etcdErr.NewError(etcdErr.EcodeTestFailed, "/foo/bar", 456),
  437. http.StatusPreconditionFailed,
  438. "456",
  439. },
  440. {
  441. err: errors.New("something went wrong"),
  442. wcode: http.StatusInternalServerError,
  443. },
  444. }
  445. for i, tt := range tests {
  446. rw := httptest.NewRecorder()
  447. writeError(rw, tt.err)
  448. if code := rw.Code; code != tt.wcode {
  449. t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
  450. }
  451. if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
  452. t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
  453. }
  454. }
  455. }
  456. func TestWriteEvent(t *testing.T) {
  457. // nil event should not panic
  458. rw := httptest.NewRecorder()
  459. writeEvent(rw, nil)
  460. h := rw.Header()
  461. if len(h) > 0 {
  462. t.Fatalf("unexpected non-empty headers: %#v", h)
  463. }
  464. b := rw.Body.String()
  465. if len(b) > 0 {
  466. t.Fatalf("unexpected non-empty body: %q", b)
  467. }
  468. tests := []struct {
  469. ev *store.Event
  470. idx string
  471. // TODO(jonboulle): check body as well as just status code
  472. code int
  473. err error
  474. }{
  475. // standard case, standard 200 response
  476. {
  477. &store.Event{
  478. Action: store.Get,
  479. Node: &store.NodeExtern{},
  480. PrevNode: &store.NodeExtern{},
  481. },
  482. "0",
  483. http.StatusOK,
  484. nil,
  485. },
  486. // check new nodes return StatusCreated
  487. {
  488. &store.Event{
  489. Action: store.Create,
  490. Node: &store.NodeExtern{},
  491. PrevNode: &store.NodeExtern{},
  492. },
  493. "0",
  494. http.StatusCreated,
  495. nil,
  496. },
  497. }
  498. for i, tt := range tests {
  499. rw := httptest.NewRecorder()
  500. writeEvent(rw, tt.ev)
  501. if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
  502. t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
  503. }
  504. if gei := rw.Header().Get("X-Etcd-Index"); gei != tt.idx {
  505. t.Errorf("case %d: bad X-Etcd-Index header: got %s, want %s", i, gei, tt.idx)
  506. }
  507. if rw.Code != tt.code {
  508. t.Errorf("case %d: bad response code: got %d, want %v", i, rw.Code, tt.code)
  509. }
  510. }
  511. }
  512. type dummyWatcher struct {
  513. echan chan *store.Event
  514. }
  515. func (w *dummyWatcher) EventChan() chan *store.Event {
  516. return w.echan
  517. }
  518. func (w *dummyWatcher) Remove() {}
  519. func TestV2MachinesEndpoint(t *testing.T) {
  520. tests := []struct {
  521. method string
  522. wcode int
  523. }{
  524. {"GET", http.StatusOK},
  525. {"HEAD", http.StatusOK},
  526. {"POST", http.StatusMethodNotAllowed},
  527. }
  528. m := NewClientHandler(nil, Peers{}, time.Hour)
  529. s := httptest.NewServer(m)
  530. defer s.Close()
  531. for _, tt := range tests {
  532. req, err := http.NewRequest(tt.method, s.URL+machinesPrefix, nil)
  533. if err != nil {
  534. t.Fatal(err)
  535. }
  536. resp, err := http.DefaultClient.Do(req)
  537. if err != nil {
  538. t.Fatal(err)
  539. }
  540. if resp.StatusCode != tt.wcode {
  541. t.Errorf("StatusCode = %d, expected %d", resp.StatusCode, tt.wcode)
  542. }
  543. }
  544. }
  545. func TestServeMachines(t *testing.T) {
  546. peers := Peers{}
  547. peers.Set("0xBEEF0=localhost:8080&0xBEEF1=localhost:8081&0xBEEF2=localhost:8082")
  548. writer := httptest.NewRecorder()
  549. req, err := http.NewRequest("GET", "", nil)
  550. if err != nil {
  551. t.Fatal(err)
  552. }
  553. h := &serverHandler{peers: peers}
  554. h.serveMachines(writer, req)
  555. w := "http://localhost:8080, http://localhost:8081, http://localhost:8082"
  556. if g := writer.Body.String(); g != w {
  557. t.Errorf("body = %s, want %s", g, w)
  558. }
  559. if writer.Code != http.StatusOK {
  560. t.Errorf("header = %d, want %d", writer.Code, http.StatusOK)
  561. }
  562. }
  563. func TestPeersEndpoints(t *testing.T) {
  564. tests := []struct {
  565. peers Peers
  566. endpoints []string
  567. }{
  568. // single peer with a single address
  569. {
  570. peers: Peers(map[int64][]string{
  571. 1: []string{"192.0.2.1"},
  572. }),
  573. endpoints: []string{"http://192.0.2.1"},
  574. },
  575. // single peer with a single address with a port
  576. {
  577. peers: Peers(map[int64][]string{
  578. 1: []string{"192.0.2.1:8001"},
  579. }),
  580. endpoints: []string{"http://192.0.2.1:8001"},
  581. },
  582. // several peers explicitly unsorted
  583. {
  584. peers: Peers(map[int64][]string{
  585. 2: []string{"192.0.2.3", "192.0.2.4"},
  586. 3: []string{"192.0.2.5", "192.0.2.6"},
  587. 1: []string{"192.0.2.1", "192.0.2.2"},
  588. }),
  589. endpoints: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
  590. },
  591. // no peers
  592. {
  593. peers: Peers(map[int64][]string{}),
  594. endpoints: []string{},
  595. },
  596. // peer with no endpoints
  597. {
  598. peers: Peers(map[int64][]string{
  599. 3: []string{},
  600. }),
  601. endpoints: []string{},
  602. },
  603. }
  604. for i, tt := range tests {
  605. endpoints := tt.peers.Endpoints()
  606. if !reflect.DeepEqual(tt.endpoints, endpoints) {
  607. t.Errorf("#%d: peers.Endpoints() incorrect: want=%#v got=%#v", i, tt.endpoints, endpoints)
  608. }
  609. }
  610. }
  611. func TestAllowMethod(t *testing.T) {
  612. tests := []struct {
  613. m string
  614. ms []string
  615. w bool
  616. wh string
  617. }{
  618. // Accepted methods
  619. {
  620. m: "GET",
  621. ms: []string{"GET", "POST", "PUT"},
  622. w: true,
  623. },
  624. {
  625. m: "POST",
  626. ms: []string{"POST"},
  627. w: true,
  628. },
  629. // Made-up methods no good
  630. {
  631. m: "FAKE",
  632. ms: []string{"GET", "POST", "PUT"},
  633. w: false,
  634. wh: "GET,POST,PUT",
  635. },
  636. // Empty methods no good
  637. {
  638. m: "",
  639. ms: []string{"GET", "POST"},
  640. w: false,
  641. wh: "GET,POST",
  642. },
  643. // Empty accepted methods no good
  644. {
  645. m: "GET",
  646. ms: []string{""},
  647. w: false,
  648. wh: "",
  649. },
  650. // No methods accepted
  651. {
  652. m: "GET",
  653. ms: []string{},
  654. w: false,
  655. wh: "",
  656. },
  657. }
  658. for i, tt := range tests {
  659. rw := httptest.NewRecorder()
  660. g := allowMethod(rw, tt.m, tt.ms...)
  661. if g != tt.w {
  662. t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
  663. }
  664. if !tt.w {
  665. if rw.Code != http.StatusMethodNotAllowed {
  666. t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
  667. }
  668. gh := rw.Header().Get("Allow")
  669. if gh != tt.wh {
  670. t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
  671. }
  672. }
  673. }
  674. }
  675. // errServer implements the etcd.Server interface for testing.
  676. // It returns the given error from any Do/Process calls.
  677. type errServer struct {
  678. err error
  679. }
  680. func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
  681. return etcdserver.Response{}, fs.err
  682. }
  683. func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
  684. return fs.err
  685. }
  686. func (fs *errServer) Start() {}
  687. func (fs *errServer) Stop() {}
  688. // errReader implements io.Reader to facilitate a broken request.
  689. type errReader struct{}
  690. func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
  691. func mustMarshalMsg(t *testing.T, m raftpb.Message) []byte {
  692. json, err := m.Marshal()
  693. if err != nil {
  694. t.Fatalf("error marshalling raft Message: %#v", err)
  695. }
  696. return json
  697. }
  698. func TestServeRaft(t *testing.T) {
  699. testCases := []struct {
  700. method string
  701. body io.Reader
  702. serverErr error
  703. wcode int
  704. }{
  705. {
  706. // bad method
  707. "GET",
  708. bytes.NewReader(
  709. mustMarshalMsg(
  710. t,
  711. raftpb.Message{},
  712. ),
  713. ),
  714. nil,
  715. http.StatusMethodNotAllowed,
  716. },
  717. {
  718. // bad method
  719. "PUT",
  720. bytes.NewReader(
  721. mustMarshalMsg(
  722. t,
  723. raftpb.Message{},
  724. ),
  725. ),
  726. nil,
  727. http.StatusMethodNotAllowed,
  728. },
  729. {
  730. // bad method
  731. "DELETE",
  732. bytes.NewReader(
  733. mustMarshalMsg(
  734. t,
  735. raftpb.Message{},
  736. ),
  737. ),
  738. nil,
  739. http.StatusMethodNotAllowed,
  740. },
  741. {
  742. // bad request body
  743. "POST",
  744. &errReader{},
  745. nil,
  746. http.StatusBadRequest,
  747. },
  748. {
  749. // bad request protobuf
  750. "POST",
  751. strings.NewReader("malformed garbage"),
  752. nil,
  753. http.StatusBadRequest,
  754. },
  755. {
  756. // good request, etcdserver.Server error
  757. "POST",
  758. bytes.NewReader(
  759. mustMarshalMsg(
  760. t,
  761. raftpb.Message{},
  762. ),
  763. ),
  764. errors.New("some error"),
  765. http.StatusInternalServerError,
  766. },
  767. {
  768. // good request
  769. "POST",
  770. bytes.NewReader(
  771. mustMarshalMsg(
  772. t,
  773. raftpb.Message{},
  774. ),
  775. ),
  776. nil,
  777. http.StatusNoContent,
  778. },
  779. }
  780. for i, tt := range testCases {
  781. req, err := http.NewRequest(tt.method, "foo", tt.body)
  782. if err != nil {
  783. t.Fatalf("#%d: could not create request: %#v", i, err)
  784. }
  785. h := &serverHandler{
  786. timeout: time.Hour,
  787. server: &errServer{tt.serverErr},
  788. peers: nil,
  789. }
  790. rw := httptest.NewRecorder()
  791. h.serveRaft(rw, req)
  792. if rw.Code != tt.wcode {
  793. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  794. }
  795. }
  796. }
  797. // resServer implements the etcd.Server interface for testing.
  798. // It returns the given responsefrom any Do calls, and nil error
  799. type resServer struct {
  800. res etcdserver.Response
  801. }
  802. func (rs *resServer) Do(_ context.Context, _ etcdserverpb.Request) (etcdserver.Response, error) {
  803. return rs.res, nil
  804. }
  805. func (rs *resServer) Process(_ context.Context, _ raftpb.Message) error { return nil }
  806. func (rs *resServer) Start() {}
  807. func (rs *resServer) Stop() {}
  808. func mustMarshalEvent(t *testing.T, ev *store.Event) string {
  809. b := new(bytes.Buffer)
  810. if err := json.NewEncoder(b).Encode(ev); err != nil {
  811. t.Fatalf("error marshalling event %#v: %v", ev, err)
  812. }
  813. return b.String()
  814. }
  815. func TestBadServeKeys(t *testing.T) {
  816. testBadCases := []struct {
  817. req *http.Request
  818. server etcdserver.Server
  819. wcode int
  820. }{
  821. {
  822. // bad method
  823. &http.Request{
  824. Method: "CONNECT",
  825. },
  826. &resServer{},
  827. http.StatusMethodNotAllowed,
  828. },
  829. {
  830. // bad method
  831. &http.Request{
  832. Method: "TRACE",
  833. },
  834. &resServer{},
  835. http.StatusMethodNotAllowed,
  836. },
  837. {
  838. // parseRequest error
  839. &http.Request{
  840. Body: nil,
  841. Method: "PUT",
  842. },
  843. &resServer{},
  844. http.StatusBadRequest,
  845. },
  846. {
  847. // etcdserver.Server error
  848. mustNewRequest(t, "foo"),
  849. &errServer{
  850. errors.New("blah"),
  851. },
  852. http.StatusInternalServerError,
  853. },
  854. {
  855. // non-event/watcher response from etcdserver.Server
  856. mustNewRequest(t, "foo"),
  857. &resServer{
  858. etcdserver.Response{},
  859. },
  860. http.StatusInternalServerError,
  861. },
  862. }
  863. for i, tt := range testBadCases {
  864. h := &serverHandler{
  865. timeout: 0, // context times out immediately
  866. server: tt.server,
  867. peers: nil,
  868. }
  869. rw := httptest.NewRecorder()
  870. h.serveKeys(rw, tt.req)
  871. if rw.Code != tt.wcode {
  872. t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
  873. }
  874. }
  875. }
  876. func TestServeKeysEvent(t *testing.T) {
  877. req := mustNewRequest(t, "foo")
  878. server := &resServer{
  879. etcdserver.Response{
  880. Event: &store.Event{
  881. Action: store.Get,
  882. Node: &store.NodeExtern{},
  883. },
  884. },
  885. }
  886. h := &serverHandler{
  887. timeout: time.Hour,
  888. server: server,
  889. peers: nil,
  890. }
  891. rw := httptest.NewRecorder()
  892. h.serveKeys(rw, req)
  893. wcode := http.StatusOK
  894. wbody := mustMarshalEvent(
  895. t,
  896. &store.Event{
  897. Action: store.Get,
  898. Node: &store.NodeExtern{},
  899. },
  900. )
  901. if rw.Code != wcode {
  902. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  903. }
  904. g := rw.Body.String()
  905. if g != wbody {
  906. t.Errorf("got body=%#v, want %#v", g, wbody)
  907. }
  908. }
  909. func TestServeKeysWatch(t *testing.T) {
  910. req := mustNewRequest(t, "/foo/bar")
  911. ec := make(chan *store.Event)
  912. dw := &dummyWatcher{
  913. echan: ec,
  914. }
  915. server := &resServer{
  916. etcdserver.Response{
  917. Watcher: dw,
  918. },
  919. }
  920. h := &serverHandler{
  921. timeout: time.Hour,
  922. server: server,
  923. peers: nil,
  924. }
  925. go func() {
  926. ec <- &store.Event{
  927. Action: store.Get,
  928. Node: &store.NodeExtern{},
  929. }
  930. }()
  931. rw := httptest.NewRecorder()
  932. h.serveKeys(rw, req)
  933. wcode := http.StatusOK
  934. wbody := mustMarshalEvent(
  935. t,
  936. &store.Event{
  937. Action: store.Get,
  938. Node: &store.NodeExtern{},
  939. },
  940. )
  941. if rw.Code != wcode {
  942. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  943. }
  944. g := rw.Body.String()
  945. if g != wbody {
  946. t.Errorf("got body=%#v, want %#v", g, wbody)
  947. }
  948. }
  949. func TestHandleWatch(t *testing.T) {
  950. rw := httptest.NewRecorder()
  951. wa := &dummyWatcher{
  952. echan: make(chan *store.Event, 1),
  953. }
  954. wa.echan <- &store.Event{
  955. Action: store.Get,
  956. Node: &store.NodeExtern{},
  957. }
  958. handleWatch(context.Background(), rw, wa, false)
  959. wcode := http.StatusOK
  960. wct := "application/json"
  961. wbody := mustMarshalEvent(
  962. t,
  963. &store.Event{
  964. Action: store.Get,
  965. Node: &store.NodeExtern{},
  966. },
  967. )
  968. if rw.Code != wcode {
  969. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  970. }
  971. h := rw.Header()
  972. if ct := h.Get("Content-Type"); ct != wct {
  973. t.Errorf("Content-Type=%q, want %q", ct, wct)
  974. }
  975. g := rw.Body.String()
  976. if g != wbody {
  977. t.Errorf("got body=%#v, want %#v", g, wbody)
  978. }
  979. }
  980. func TestHandleWatchNoEvent(t *testing.T) {
  981. rw := httptest.NewRecorder()
  982. wa := &dummyWatcher{
  983. echan: make(chan *store.Event, 1),
  984. }
  985. close(wa.echan)
  986. handleWatch(context.Background(), rw, wa, false)
  987. wcode := http.StatusOK
  988. wct := "application/json"
  989. wbody := ""
  990. if rw.Code != wcode {
  991. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  992. }
  993. h := rw.Header()
  994. if ct := h.Get("Content-Type"); ct != wct {
  995. t.Errorf("Content-Type=%q, want %q", ct, wct)
  996. }
  997. g := rw.Body.String()
  998. if g != wbody {
  999. t.Errorf("got body=%#v, want %#v", g, wbody)
  1000. }
  1001. }
  1002. type recordingCloseNotifier struct {
  1003. *httptest.ResponseRecorder
  1004. cn chan bool
  1005. }
  1006. func (rcn *recordingCloseNotifier) CloseNotify() <-chan bool {
  1007. return rcn.cn
  1008. }
  1009. func TestHandleWatchCloseNotified(t *testing.T) {
  1010. rw := &recordingCloseNotifier{
  1011. ResponseRecorder: httptest.NewRecorder(),
  1012. cn: make(chan bool, 1),
  1013. }
  1014. rw.cn <- true
  1015. wa := &dummyWatcher{}
  1016. handleWatch(context.Background(), rw, wa, false)
  1017. wcode := http.StatusOK
  1018. wct := "application/json"
  1019. wbody := ""
  1020. if rw.Code != wcode {
  1021. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1022. }
  1023. h := rw.Header()
  1024. if ct := h.Get("Content-Type"); ct != wct {
  1025. t.Errorf("Content-Type=%q, want %q", ct, wct)
  1026. }
  1027. g := rw.Body.String()
  1028. if g != wbody {
  1029. t.Errorf("got body=%#v, want %#v", g, wbody)
  1030. }
  1031. }
  1032. func TestHandleWatchTimeout(t *testing.T) {
  1033. rw := httptest.NewRecorder()
  1034. wa := &dummyWatcher{}
  1035. // Simulate a timed-out context
  1036. ctx, cancel := context.WithCancel(context.Background())
  1037. cancel()
  1038. handleWatch(ctx, rw, wa, false)
  1039. wcode := http.StatusOK
  1040. wct := "application/json"
  1041. wbody := ""
  1042. if rw.Code != wcode {
  1043. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1044. }
  1045. h := rw.Header()
  1046. if ct := h.Get("Content-Type"); ct != wct {
  1047. t.Errorf("Content-Type=%q, want %q", ct, wct)
  1048. }
  1049. g := rw.Body.String()
  1050. if g != wbody {
  1051. t.Errorf("got body=%#v, want %#v", g, wbody)
  1052. }
  1053. }
  1054. // flushingRecorder provides a channel to allow users to block until the Recorder is Flushed()
  1055. type flushingRecorder struct {
  1056. *httptest.ResponseRecorder
  1057. ch chan struct{}
  1058. }
  1059. func (fr *flushingRecorder) Flush() {
  1060. fr.ResponseRecorder.Flush()
  1061. fr.ch <- struct{}{}
  1062. }
  1063. func TestHandleWatchStreaming(t *testing.T) {
  1064. rw := &flushingRecorder{
  1065. httptest.NewRecorder(),
  1066. make(chan struct{}, 1),
  1067. }
  1068. wa := &dummyWatcher{
  1069. echan: make(chan *store.Event),
  1070. }
  1071. // Launch the streaming handler in the background with a cancellable context
  1072. ctx, cancel := context.WithCancel(context.Background())
  1073. done := make(chan struct{})
  1074. go func() {
  1075. handleWatch(ctx, rw, wa, true)
  1076. close(done)
  1077. }()
  1078. // Expect one Flush for the headers etc.
  1079. select {
  1080. case <-rw.ch:
  1081. case <-time.After(time.Second):
  1082. t.Fatalf("timed out waiting for flush")
  1083. }
  1084. // Expect headers but no body
  1085. wcode := http.StatusOK
  1086. wct := "application/json"
  1087. wbody := ""
  1088. if rw.Code != wcode {
  1089. t.Errorf("got code=%d, want %d", rw.Code, wcode)
  1090. }
  1091. h := rw.Header()
  1092. if ct := h.Get("Content-Type"); ct != wct {
  1093. t.Errorf("Content-Type=%q, want %q", ct, wct)
  1094. }
  1095. g := rw.Body.String()
  1096. if g != wbody {
  1097. t.Errorf("got body=%#v, want %#v", g, wbody)
  1098. }
  1099. // Now send the first event
  1100. select {
  1101. case wa.echan <- &store.Event{
  1102. Action: store.Get,
  1103. Node: &store.NodeExtern{},
  1104. }:
  1105. case <-time.After(time.Second):
  1106. t.Fatal("timed out waiting for send")
  1107. }
  1108. // Wait for it to be flushed...
  1109. select {
  1110. case <-rw.ch:
  1111. case <-time.After(time.Second):
  1112. t.Fatalf("timed out waiting for flush")
  1113. }
  1114. // And check the body is as expected
  1115. wbody = mustMarshalEvent(
  1116. t,
  1117. &store.Event{
  1118. Action: store.Get,
  1119. Node: &store.NodeExtern{},
  1120. },
  1121. )
  1122. g = rw.Body.String()
  1123. if g != wbody {
  1124. t.Errorf("got body=%#v, want %#v", g, wbody)
  1125. }
  1126. // Rinse and repeat
  1127. select {
  1128. case wa.echan <- &store.Event{
  1129. Action: store.Get,
  1130. Node: &store.NodeExtern{},
  1131. }:
  1132. case <-time.After(time.Second):
  1133. t.Fatal("timed out waiting for send")
  1134. }
  1135. select {
  1136. case <-rw.ch:
  1137. case <-time.After(time.Second):
  1138. t.Fatalf("timed out waiting for flush")
  1139. }
  1140. // This time, we expect to see both events
  1141. wbody = wbody + wbody
  1142. g = rw.Body.String()
  1143. if g != wbody {
  1144. t.Errorf("got body=%#v, want %#v", g, wbody)
  1145. }
  1146. // Finally, time out the connection and ensure the serving goroutine returns
  1147. cancel()
  1148. select {
  1149. case <-done:
  1150. case <-time.After(time.Second):
  1151. t.Fatalf("timed out waiting for done")
  1152. }
  1153. }