You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

595 lines
15 KiB

4 years ago
6 years ago
  1. package cos
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/rand"
  6. "encoding/hex"
  7. "encoding/xml"
  8. "fmt"
  9. "hash/crc64"
  10. "io"
  11. "io/ioutil"
  12. "net/http"
  13. "net/url"
  14. "os"
  15. "reflect"
  16. "strconv"
  17. "strings"
  18. "testing"
  19. "time"
  20. )
  21. func TestObjectService_Get(t *testing.T) {
  22. setup()
  23. defer teardown()
  24. name := "test/hello.txt"
  25. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  26. testMethod(t, r, "GET")
  27. vs := values{
  28. "response-content-type": "text/html",
  29. }
  30. testFormValues(t, r, vs)
  31. testHeader(t, r, "Range", "bytes=0-3")
  32. fmt.Fprint(w, `hello`)
  33. })
  34. opt := &ObjectGetOptions{
  35. ResponseContentType: "text/html",
  36. Range: "bytes=0-3",
  37. }
  38. resp, err := client.Object.Get(context.Background(), name, opt)
  39. if err != nil {
  40. t.Fatalf("Object.Get returned error: %v", err)
  41. }
  42. b, _ := ioutil.ReadAll(resp.Body)
  43. ref := string(b)
  44. want := "hello"
  45. if !reflect.DeepEqual(ref, want) {
  46. t.Errorf("Object.Get returned %+v, want %+v", ref, want)
  47. }
  48. }
  49. func TestObjectService_Put(t *testing.T) {
  50. setup()
  51. defer teardown()
  52. opt := &ObjectPutOptions{
  53. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  54. ContentType: "text/html",
  55. },
  56. ACLHeaderOptions: &ACLHeaderOptions{
  57. XCosACL: "private",
  58. },
  59. }
  60. name := "test/hello.txt"
  61. retry := 0
  62. final := 10
  63. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  64. testMethod(t, r, http.MethodPut)
  65. testHeader(t, r, "x-cos-acl", "private")
  66. testHeader(t, r, "Content-Type", "text/html")
  67. if retry%2 == 0 {
  68. b, _ := ioutil.ReadAll(r.Body)
  69. tb := crc64.MakeTable(crc64.ECMA)
  70. crc := crc64.Update(0, tb, b)
  71. v := string(b)
  72. want := "hello"
  73. if !reflect.DeepEqual(v, want) {
  74. t.Errorf("Object.Put request body: %#v, want %#v", v, want)
  75. }
  76. realcrc := crc64.Update(0, tb, []byte("hello"))
  77. if !reflect.DeepEqual(crc, realcrc) {
  78. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  79. }
  80. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  81. if retry != final {
  82. w.WriteHeader(http.StatusGatewayTimeout)
  83. }
  84. } else {
  85. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  86. }
  87. })
  88. for retry <= final {
  89. r := bytes.NewReader([]byte("hello"))
  90. _, err := client.Object.Put(context.Background(), name, r, opt)
  91. if retry < final && err == nil {
  92. t.Fatalf("Error must not nil when retry < final")
  93. }
  94. if retry == final && err != nil {
  95. t.Fatalf("Put Error: %v", err)
  96. }
  97. retry++
  98. }
  99. }
  100. func TestObjectService_PutFromFile(t *testing.T) {
  101. setup()
  102. defer teardown()
  103. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  104. newfile, err := os.Create(filePath)
  105. if err != nil {
  106. t.Fatalf("create tmp file failed")
  107. }
  108. defer os.Remove(filePath)
  109. // 源文件内容
  110. b := make([]byte, 1024*1024*3)
  111. _, err = rand.Read(b)
  112. newfile.Write(b)
  113. newfile.Close()
  114. tb := crc64.MakeTable(crc64.ECMA)
  115. realcrc := crc64.Update(0, tb, b)
  116. opt := &ObjectPutOptions{
  117. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  118. ContentType: "text/html",
  119. },
  120. ACLHeaderOptions: &ACLHeaderOptions{
  121. XCosACL: "private",
  122. },
  123. }
  124. name := "test/hello.txt"
  125. retry := 0
  126. final := 4
  127. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  128. testMethod(t, r, http.MethodPut)
  129. testHeader(t, r, "x-cos-acl", "private")
  130. testHeader(t, r, "Content-Type", "text/html")
  131. if retry%2 == 0 {
  132. bs, _ := ioutil.ReadAll(r.Body)
  133. crc := crc64.Update(0, tb, bs)
  134. if !reflect.DeepEqual(bs, b) {
  135. t.Errorf("Object.Put request body Error")
  136. }
  137. if !reflect.DeepEqual(crc, realcrc) {
  138. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  139. }
  140. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  141. if retry != final {
  142. w.WriteHeader(http.StatusGatewayTimeout)
  143. }
  144. } else {
  145. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  146. }
  147. })
  148. for retry <= final {
  149. _, err := client.Object.PutFromFile(context.Background(), name, filePath, opt)
  150. if retry < final && err == nil {
  151. t.Fatalf("Error must not nil when retry < final")
  152. }
  153. if retry == final && err != nil {
  154. t.Fatalf("Put Error: %v", err)
  155. }
  156. retry++
  157. }
  158. }
  159. func TestObjectService_Delete(t *testing.T) {
  160. setup()
  161. defer teardown()
  162. name := "test/hello.txt"
  163. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  164. testMethod(t, r, http.MethodDelete)
  165. w.WriteHeader(http.StatusNoContent)
  166. })
  167. _, err := client.Object.Delete(context.Background(), name)
  168. if err != nil {
  169. t.Fatalf("Object.Delete returned error: %v", err)
  170. }
  171. }
  172. func TestObjectService_Head(t *testing.T) {
  173. setup()
  174. defer teardown()
  175. name := "test/hello.txt"
  176. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  177. testMethod(t, r, "HEAD")
  178. testHeader(t, r, "If-Modified-Since", "Mon, 12 Jun 2017 05:36:19 GMT")
  179. })
  180. opt := &ObjectHeadOptions{
  181. IfModifiedSince: "Mon, 12 Jun 2017 05:36:19 GMT",
  182. }
  183. _, err := client.Object.Head(context.Background(), name, opt)
  184. if err != nil {
  185. t.Fatalf("Object.Head returned error: %v", err)
  186. }
  187. }
  188. func TestObjectService_Options(t *testing.T) {
  189. setup()
  190. defer teardown()
  191. name := "test/hello.txt"
  192. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  193. testMethod(t, r, http.MethodOptions)
  194. testHeader(t, r, "Access-Control-Request-Method", "PUT")
  195. testHeader(t, r, "Origin", "www.qq.com")
  196. })
  197. opt := &ObjectOptionsOptions{
  198. Origin: "www.qq.com",
  199. AccessControlRequestMethod: "PUT",
  200. }
  201. _, err := client.Object.Options(context.Background(), name, opt)
  202. if err != nil {
  203. t.Fatalf("Object.Options returned error: %v", err)
  204. }
  205. }
  206. // func TestObjectService_Append(t *testing.T) {
  207. // setup()
  208. // defer teardown()
  209. // opt := &ObjectPutOptions{
  210. // ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  211. // ContentType: "text/html",
  212. // },
  213. // ACLHeaderOptions: &ACLHeaderOptions{
  214. // XCosACL: "private",
  215. // },
  216. // }
  217. // name := "test/hello.txt"
  218. // position := 0
  219. // mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  220. // vs := values{
  221. // "append": "",
  222. // "position": "0",
  223. // }
  224. // testFormValues(t, r, vs)
  225. // testMethod(t, r, http.MethodPost)
  226. // testHeader(t, r, "x-cos-acl", "private")
  227. // testHeader(t, r, "Content-Type", "text/html")
  228. // b, _ := ioutil.ReadAll(r.Body)
  229. // v := string(b)
  230. // want := "hello"
  231. // if !reflect.DeepEqual(v, want) {
  232. // t.Errorf("Object.Append request body: %#v, want %#v", v, want)
  233. // }
  234. // })
  235. // r := bytes.NewReader([]byte("hello"))
  236. // _, err := client.Object.Append(context.Background(), name, position, r, opt)
  237. // if err != nil {
  238. // t.Fatalf("Object.Append returned error: %v", err)
  239. // }
  240. // }
  241. func TestObjectService_DeleteMulti(t *testing.T) {
  242. setup()
  243. defer teardown()
  244. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  245. testMethod(t, r, http.MethodPost)
  246. vs := values{
  247. "delete": "",
  248. }
  249. testFormValues(t, r, vs)
  250. fmt.Fprint(w, `<DeleteResult>
  251. <Deleted>
  252. <Key>test1</Key>
  253. </Deleted>
  254. <Deleted>
  255. <Key>test3</Key>
  256. </Deleted>
  257. <Deleted>
  258. <Key>test2</Key>
  259. </Deleted>
  260. </DeleteResult>`)
  261. })
  262. opt := &ObjectDeleteMultiOptions{
  263. Objects: []Object{
  264. {
  265. Key: "test1",
  266. },
  267. {
  268. Key: "test3",
  269. },
  270. {
  271. Key: "test2",
  272. },
  273. },
  274. }
  275. ref, _, err := client.Object.DeleteMulti(context.Background(), opt)
  276. if err != nil {
  277. t.Fatalf("Object.DeleteMulti returned error: %v", err)
  278. }
  279. want := &ObjectDeleteMultiResult{
  280. XMLName: xml.Name{Local: "DeleteResult"},
  281. DeletedObjects: []Object{
  282. {
  283. Key: "test1",
  284. },
  285. {
  286. Key: "test3",
  287. },
  288. {
  289. Key: "test2",
  290. },
  291. },
  292. }
  293. if !reflect.DeepEqual(ref, want) {
  294. t.Errorf("Object.DeleteMulti returned %+v, want %+v", ref, want)
  295. }
  296. }
  297. func TestObjectService_Copy(t *testing.T) {
  298. setup()
  299. defer teardown()
  300. mux.HandleFunc("/test.go.copy", func(w http.ResponseWriter, r *http.Request) {
  301. testMethod(t, r, http.MethodPut)
  302. fmt.Fprint(w, `<CopyObjectResult>
  303. <ETag>"098f6bcd4621d373cade4e832627b4f6"</ETag>
  304. <LastModified>2017-12-13T14:53:12</LastModified>
  305. </CopyObjectResult>`)
  306. })
  307. sourceURL := "test-1253846586.cos.ap-guangzhou.myqcloud.com/test.source"
  308. ref, _, err := client.Object.Copy(context.Background(), "test.go.copy", sourceURL, nil)
  309. if err != nil {
  310. t.Fatalf("Object.Copy returned error: %v", err)
  311. }
  312. want := &ObjectCopyResult{
  313. XMLName: xml.Name{Local: "CopyObjectResult"},
  314. ETag: `"098f6bcd4621d373cade4e832627b4f6"`,
  315. LastModified: "2017-12-13T14:53:12",
  316. }
  317. if !reflect.DeepEqual(ref, want) {
  318. t.Errorf("Object.Copy returned %+v, want %+v", ref, want)
  319. }
  320. }
  321. func TestObjectService_Upload(t *testing.T) {
  322. setup()
  323. defer teardown()
  324. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  325. newfile, err := os.Create(filePath)
  326. if err != nil {
  327. t.Fatalf("create tmp file failed")
  328. }
  329. defer os.Remove(filePath)
  330. // 源文件内容
  331. b := make([]byte, 1024*1024*33)
  332. _, err = rand.Read(b)
  333. newfile.Write(b)
  334. newfile.Close()
  335. // 已上传内容, 10个分块
  336. rb := make([][]byte, 33)
  337. uploadid := "test-cos-multiupload-uploadid"
  338. partmap := make(map[int64]int)
  339. mux.HandleFunc("/test.go.upload", func(w http.ResponseWriter, r *http.Request) {
  340. if r.Method == http.MethodPut { // 分块上传
  341. r.ParseForm()
  342. part, _ := strconv.ParseInt(r.Form.Get("partNumber"), 10, 64)
  343. if partmap[part] == 0 {
  344. // 重试检验1
  345. partmap[part]++
  346. ioutil.ReadAll(r.Body)
  347. w.WriteHeader(http.StatusGatewayTimeout)
  348. } else if partmap[part] == 1 {
  349. // 重试校验2
  350. partmap[part]++
  351. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  352. } else { // 正确上传
  353. bs, _ := ioutil.ReadAll(r.Body)
  354. rb[part-1] = bs
  355. md := hex.EncodeToString(calMD5Digest(bs))
  356. crc := crc64.Update(0, crc64.MakeTable(crc64.ECMA), bs)
  357. w.Header().Add("ETag", md)
  358. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  359. }
  360. } else {
  361. testMethod(t, r, http.MethodPost)
  362. initreq := url.Values{}
  363. initreq.Set("uploads", "")
  364. compreq := url.Values{}
  365. compreq.Set("uploadId", uploadid)
  366. r.ParseForm()
  367. if reflect.DeepEqual(r.Form, initreq) {
  368. // 初始化分块上传
  369. fmt.Fprintf(w, `<InitiateMultipartUploadResult>
  370. <Bucket></Bucket>
  371. <Key>%v</Key>
  372. <UploadId>%v</UploadId>
  373. </InitiateMultipartUploadResult>`, "test.go.upload", uploadid)
  374. } else if reflect.DeepEqual(r.Form, compreq) {
  375. // 完成分块上传
  376. tb := crc64.MakeTable(crc64.ECMA)
  377. crc := uint64(0)
  378. for _, v := range rb {
  379. crc = crc64.Update(crc, tb, v)
  380. }
  381. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  382. fmt.Fprintf(w, `<CompleteMultipartUploadResult>
  383. <Location>/test.go.upload</Location>
  384. <Bucket></Bucket>
  385. <Key>test.go.upload</Key>
  386. <ETag>&quot;%v&quot;</ETag>
  387. </CompleteMultipartUploadResult>`, hex.EncodeToString(calMD5Digest(b)))
  388. } else {
  389. t.Errorf("TestObjectService_Upload Unknown Request")
  390. }
  391. }
  392. })
  393. opt := &MultiUploadOptions{
  394. ThreadPoolSize: 3,
  395. PartSize: 1,
  396. }
  397. _, _, err = client.Object.Upload(context.Background(), "test.go.upload", filePath, opt)
  398. if err != nil {
  399. t.Fatalf("Object.Upload returned error: %v", err)
  400. }
  401. }
  402. func TestObjectService_Upload2(t *testing.T) {
  403. setup()
  404. defer teardown()
  405. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  406. newfile, err := os.Create(filePath)
  407. if err != nil {
  408. t.Fatalf("create tmp file failed")
  409. }
  410. defer os.Remove(filePath)
  411. // 源文件内容
  412. b := make([]byte, 1024*1024*3)
  413. _, err = rand.Read(b)
  414. newfile.Write(b)
  415. newfile.Close()
  416. tb := crc64.MakeTable(crc64.ECMA)
  417. realcrc := crc64.Update(0, tb, b)
  418. name := "test/hello.txt"
  419. retry := 0
  420. final := 4
  421. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  422. testMethod(t, r, http.MethodPut)
  423. testHeader(t, r, "x-cos-acl", "private")
  424. testHeader(t, r, "Content-Type", "text/html")
  425. if retry%2 == 0 {
  426. bs, _ := ioutil.ReadAll(r.Body)
  427. crc := crc64.Update(0, tb, bs)
  428. if !reflect.DeepEqual(bs, b) {
  429. t.Errorf("Object.Put request body Error")
  430. }
  431. if !reflect.DeepEqual(crc, realcrc) {
  432. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  433. }
  434. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  435. if retry != final {
  436. w.WriteHeader(http.StatusGatewayTimeout)
  437. }
  438. } else {
  439. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  440. }
  441. })
  442. mopt := &MultiUploadOptions{
  443. OptIni: &InitiateMultipartUploadOptions{
  444. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  445. ContentType: "text/html",
  446. },
  447. ACLHeaderOptions: &ACLHeaderOptions{
  448. XCosACL: "private",
  449. },
  450. },
  451. }
  452. for retry <= final {
  453. _, _, err := client.Object.Upload(context.Background(), name, filePath, mopt)
  454. if retry < final && err == nil {
  455. t.Fatalf("Error must not nil when retry < final")
  456. }
  457. if retry == final && err != nil {
  458. t.Fatalf("Put Error: %v", err)
  459. }
  460. retry++
  461. }
  462. }
  463. func TestObjectService_Download(t *testing.T) {
  464. setup()
  465. defer teardown()
  466. filePath := "rsp.file" + time.Now().Format(time.RFC3339)
  467. newfile, err := os.Create(filePath)
  468. if err != nil {
  469. t.Fatalf("create tmp file failed")
  470. }
  471. defer os.Remove(filePath)
  472. // 源文件内容
  473. totalBytes := int64(1024 * 1024 * 10)
  474. b := make([]byte, totalBytes)
  475. _, err = rand.Read(b)
  476. newfile.Write(b)
  477. newfile.Close()
  478. tb := crc64.MakeTable(crc64.ECMA)
  479. localcrc := crc64.Update(0, tb, b)
  480. retryMap := make(map[int64]int)
  481. mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
  482. if r.Method == http.MethodHead {
  483. w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
  484. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(localcrc, 10))
  485. return
  486. }
  487. strRange := r.Header.Get("Range")
  488. slice1 := strings.Split(strRange, "=")
  489. slice2 := strings.Split(slice1[1], "-")
  490. start, _ := strconv.ParseInt(slice2[0], 10, 64)
  491. end, _ := strconv.ParseInt(slice2[1], 10, 64)
  492. if retryMap[start] == 0 {
  493. retryMap[start]++
  494. w.WriteHeader(http.StatusGatewayTimeout)
  495. } else if retryMap[start] == 1 {
  496. retryMap[start]++
  497. w.WriteHeader(http.StatusGatewayTimeout)
  498. return
  499. fd, err := os.Open(filePath)
  500. if err != nil {
  501. t.Fatalf("open file failed: %v", err)
  502. }
  503. defer fd.Close()
  504. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(localcrc, 10))
  505. fd.Seek(start, os.SEEK_SET)
  506. n, err := io.Copy(w, LimitReadCloser(fd, (end-start)/2))
  507. if err != nil || int64(n) != (end-start)/2 {
  508. t.Fatalf("write file failed:%v, n:%v", err, n)
  509. }
  510. } else {
  511. fd, err := os.Open(filePath)
  512. if err != nil {
  513. t.Fatalf("open file failed: %v", err)
  514. }
  515. defer fd.Close()
  516. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(localcrc, 10))
  517. fd.Seek(start, os.SEEK_SET)
  518. n, err := io.Copy(w, LimitReadCloser(fd, end-start+1))
  519. if err != nil || int64(n) != end-start+1 {
  520. t.Fatalf("write file failed:%v, n:%v", err, n)
  521. }
  522. }
  523. })
  524. opt := &MultiDownloadOptions{
  525. ThreadPoolSize: 3,
  526. PartSize: 1,
  527. }
  528. downPath := "down.file" + time.Now().Format(time.RFC3339)
  529. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  530. if err != nil {
  531. t.Fatalf("Object.Upload returned error: %v", err)
  532. }
  533. os.Remove(downPath)
  534. }