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.

452 lines
11 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/ioutil"
  11. "net/http"
  12. "net/url"
  13. "os"
  14. "reflect"
  15. "strconv"
  16. "testing"
  17. "time"
  18. )
  19. func TestObjectService_Get(t *testing.T) {
  20. setup()
  21. defer teardown()
  22. name := "test/hello.txt"
  23. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  24. testMethod(t, r, "GET")
  25. vs := values{
  26. "response-content-type": "text/html",
  27. }
  28. testFormValues(t, r, vs)
  29. testHeader(t, r, "Range", "bytes=0-3")
  30. fmt.Fprint(w, `hello`)
  31. })
  32. opt := &ObjectGetOptions{
  33. ResponseContentType: "text/html",
  34. Range: "bytes=0-3",
  35. }
  36. resp, err := client.Object.Get(context.Background(), name, opt)
  37. if err != nil {
  38. t.Fatalf("Object.Get returned error: %v", err)
  39. }
  40. b, _ := ioutil.ReadAll(resp.Body)
  41. ref := string(b)
  42. want := "hello"
  43. if !reflect.DeepEqual(ref, want) {
  44. t.Errorf("Object.Get returned %+v, want %+v", ref, want)
  45. }
  46. }
  47. func TestObjectService_Put(t *testing.T) {
  48. setup()
  49. defer teardown()
  50. opt := &ObjectPutOptions{
  51. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  52. ContentType: "text/html",
  53. },
  54. ACLHeaderOptions: &ACLHeaderOptions{
  55. XCosACL: "private",
  56. },
  57. }
  58. name := "test/hello.txt"
  59. retry := 0
  60. final := 10
  61. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  62. testMethod(t, r, http.MethodPut)
  63. testHeader(t, r, "x-cos-acl", "private")
  64. testHeader(t, r, "Content-Type", "text/html")
  65. if retry%2 == 0 {
  66. b, _ := ioutil.ReadAll(r.Body)
  67. tb := crc64.MakeTable(crc64.ECMA)
  68. crc := crc64.Update(0, tb, b)
  69. v := string(b)
  70. want := "hello"
  71. if !reflect.DeepEqual(v, want) {
  72. t.Errorf("Object.Put request body: %#v, want %#v", v, want)
  73. }
  74. realcrc := crc64.Update(0, tb, []byte("hello"))
  75. if !reflect.DeepEqual(crc, realcrc) {
  76. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  77. }
  78. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  79. if retry != final {
  80. w.WriteHeader(http.StatusGatewayTimeout)
  81. }
  82. } else {
  83. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  84. }
  85. })
  86. for retry <= final {
  87. r := bytes.NewReader([]byte("hello"))
  88. _, err := client.Object.Put(context.Background(), name, r, opt)
  89. if retry < final && err == nil {
  90. t.Fatalf("Error must not nil when retry < final")
  91. }
  92. if retry == final && err != nil {
  93. t.Fatalf("Put Error: %v", err)
  94. }
  95. retry++
  96. }
  97. }
  98. func TestObjectService_PutFromFile(t *testing.T) {
  99. setup()
  100. defer teardown()
  101. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  102. newfile, err := os.Create(filePath)
  103. if err != nil {
  104. t.Fatalf("create tmp file failed")
  105. }
  106. defer os.Remove(filePath)
  107. // 源文件内容
  108. b := make([]byte, 1024*1024*3)
  109. _, err = rand.Read(b)
  110. newfile.Write(b)
  111. newfile.Close()
  112. tb := crc64.MakeTable(crc64.ECMA)
  113. realcrc := crc64.Update(0, tb, b)
  114. opt := &ObjectPutOptions{
  115. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  116. ContentType: "text/html",
  117. },
  118. ACLHeaderOptions: &ACLHeaderOptions{
  119. XCosACL: "private",
  120. },
  121. }
  122. name := "test/hello.txt"
  123. retry := 0
  124. final := 4
  125. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  126. testMethod(t, r, http.MethodPut)
  127. testHeader(t, r, "x-cos-acl", "private")
  128. testHeader(t, r, "Content-Type", "text/html")
  129. if retry%2 == 0 {
  130. bs, _ := ioutil.ReadAll(r.Body)
  131. crc := crc64.Update(0, tb, bs)
  132. if !reflect.DeepEqual(bs, b) {
  133. t.Errorf("Object.Put request body Error")
  134. }
  135. if !reflect.DeepEqual(crc, realcrc) {
  136. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  137. }
  138. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  139. if retry != final {
  140. w.WriteHeader(http.StatusGatewayTimeout)
  141. }
  142. } else {
  143. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  144. }
  145. })
  146. for retry <= final {
  147. _, err := client.Object.PutFromFile(context.Background(), name, filePath, opt)
  148. if retry < final && err == nil {
  149. t.Fatalf("Error must not nil when retry < final")
  150. }
  151. if retry == final && err != nil {
  152. t.Fatalf("Put Error: %v", err)
  153. }
  154. retry++
  155. }
  156. }
  157. func TestObjectService_Delete(t *testing.T) {
  158. setup()
  159. defer teardown()
  160. name := "test/hello.txt"
  161. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  162. testMethod(t, r, http.MethodDelete)
  163. w.WriteHeader(http.StatusNoContent)
  164. })
  165. _, err := client.Object.Delete(context.Background(), name)
  166. if err != nil {
  167. t.Fatalf("Object.Delete returned error: %v", err)
  168. }
  169. }
  170. func TestObjectService_Head(t *testing.T) {
  171. setup()
  172. defer teardown()
  173. name := "test/hello.txt"
  174. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  175. testMethod(t, r, "HEAD")
  176. testHeader(t, r, "If-Modified-Since", "Mon, 12 Jun 2017 05:36:19 GMT")
  177. })
  178. opt := &ObjectHeadOptions{
  179. IfModifiedSince: "Mon, 12 Jun 2017 05:36:19 GMT",
  180. }
  181. _, err := client.Object.Head(context.Background(), name, opt)
  182. if err != nil {
  183. t.Fatalf("Object.Head returned error: %v", err)
  184. }
  185. }
  186. func TestObjectService_Options(t *testing.T) {
  187. setup()
  188. defer teardown()
  189. name := "test/hello.txt"
  190. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  191. testMethod(t, r, http.MethodOptions)
  192. testHeader(t, r, "Access-Control-Request-Method", "PUT")
  193. testHeader(t, r, "Origin", "www.qq.com")
  194. })
  195. opt := &ObjectOptionsOptions{
  196. Origin: "www.qq.com",
  197. AccessControlRequestMethod: "PUT",
  198. }
  199. _, err := client.Object.Options(context.Background(), name, opt)
  200. if err != nil {
  201. t.Fatalf("Object.Options returned error: %v", err)
  202. }
  203. }
  204. // func TestObjectService_Append(t *testing.T) {
  205. // setup()
  206. // defer teardown()
  207. // opt := &ObjectPutOptions{
  208. // ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  209. // ContentType: "text/html",
  210. // },
  211. // ACLHeaderOptions: &ACLHeaderOptions{
  212. // XCosACL: "private",
  213. // },
  214. // }
  215. // name := "test/hello.txt"
  216. // position := 0
  217. // mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  218. // vs := values{
  219. // "append": "",
  220. // "position": "0",
  221. // }
  222. // testFormValues(t, r, vs)
  223. // testMethod(t, r, http.MethodPost)
  224. // testHeader(t, r, "x-cos-acl", "private")
  225. // testHeader(t, r, "Content-Type", "text/html")
  226. // b, _ := ioutil.ReadAll(r.Body)
  227. // v := string(b)
  228. // want := "hello"
  229. // if !reflect.DeepEqual(v, want) {
  230. // t.Errorf("Object.Append request body: %#v, want %#v", v, want)
  231. // }
  232. // })
  233. // r := bytes.NewReader([]byte("hello"))
  234. // _, err := client.Object.Append(context.Background(), name, position, r, opt)
  235. // if err != nil {
  236. // t.Fatalf("Object.Append returned error: %v", err)
  237. // }
  238. // }
  239. func TestObjectService_DeleteMulti(t *testing.T) {
  240. setup()
  241. defer teardown()
  242. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  243. testMethod(t, r, http.MethodPost)
  244. vs := values{
  245. "delete": "",
  246. }
  247. testFormValues(t, r, vs)
  248. fmt.Fprint(w, `<DeleteResult>
  249. <Deleted>
  250. <Key>test1</Key>
  251. </Deleted>
  252. <Deleted>
  253. <Key>test3</Key>
  254. </Deleted>
  255. <Deleted>
  256. <Key>test2</Key>
  257. </Deleted>
  258. </DeleteResult>`)
  259. })
  260. opt := &ObjectDeleteMultiOptions{
  261. Objects: []Object{
  262. {
  263. Key: "test1",
  264. },
  265. {
  266. Key: "test3",
  267. },
  268. {
  269. Key: "test2",
  270. },
  271. },
  272. }
  273. ref, _, err := client.Object.DeleteMulti(context.Background(), opt)
  274. if err != nil {
  275. t.Fatalf("Object.DeleteMulti returned error: %v", err)
  276. }
  277. want := &ObjectDeleteMultiResult{
  278. XMLName: xml.Name{Local: "DeleteResult"},
  279. DeletedObjects: []Object{
  280. {
  281. Key: "test1",
  282. },
  283. {
  284. Key: "test3",
  285. },
  286. {
  287. Key: "test2",
  288. },
  289. },
  290. }
  291. if !reflect.DeepEqual(ref, want) {
  292. t.Errorf("Object.DeleteMulti returned %+v, want %+v", ref, want)
  293. }
  294. }
  295. func TestObjectService_Copy(t *testing.T) {
  296. setup()
  297. defer teardown()
  298. mux.HandleFunc("/test.go.copy", func(w http.ResponseWriter, r *http.Request) {
  299. testMethod(t, r, http.MethodPut)
  300. fmt.Fprint(w, `<CopyObjectResult>
  301. <ETag>"098f6bcd4621d373cade4e832627b4f6"</ETag>
  302. <LastModified>2017-12-13T14:53:12</LastModified>
  303. </CopyObjectResult>`)
  304. })
  305. sourceURL := "test-1253846586.cos.ap-guangzhou.myqcloud.com/test.source"
  306. ref, _, err := client.Object.Copy(context.Background(), "test.go.copy", sourceURL, nil)
  307. if err != nil {
  308. t.Fatalf("Object.Copy returned error: %v", err)
  309. }
  310. want := &ObjectCopyResult{
  311. XMLName: xml.Name{Local: "CopyObjectResult"},
  312. ETag: `"098f6bcd4621d373cade4e832627b4f6"`,
  313. LastModified: "2017-12-13T14:53:12",
  314. }
  315. if !reflect.DeepEqual(ref, want) {
  316. t.Errorf("Object.Copy returned %+v, want %+v", ref, want)
  317. }
  318. }
  319. func TestObjectService_Upload(t *testing.T) {
  320. setup()
  321. defer teardown()
  322. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  323. newfile, err := os.Create(filePath)
  324. if err != nil {
  325. t.Fatalf("create tmp file failed")
  326. }
  327. defer os.Remove(filePath)
  328. // 源文件内容
  329. b := make([]byte, 1024*1024*10)
  330. _, err = rand.Read(b)
  331. newfile.Write(b)
  332. newfile.Close()
  333. // 已上传内容, 10个分块
  334. rb := make([][]byte, 10)
  335. uploadid := "test-cos-multiupload-uploadid"
  336. partmap := make(map[int64]int)
  337. mux.HandleFunc("/test.go.upload", func(w http.ResponseWriter, r *http.Request) {
  338. if r.Method == http.MethodPut { // 分块上传
  339. r.ParseForm()
  340. part, _ := strconv.ParseInt(r.Form.Get("partNumber"), 10, 64)
  341. if partmap[part] == 0 {
  342. // 重试检验1
  343. partmap[part]++
  344. ioutil.ReadAll(r.Body)
  345. w.WriteHeader(http.StatusGatewayTimeout)
  346. } else if partmap[part] == 1 {
  347. // 重试校验2
  348. partmap[part]++
  349. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  350. } else { // 正确上传
  351. bs, _ := ioutil.ReadAll(r.Body)
  352. rb[part-1] = bs
  353. md := hex.EncodeToString(calMD5Digest(bs))
  354. crc := crc64.Update(0, crc64.MakeTable(crc64.ECMA), bs)
  355. w.Header().Add("ETag", md)
  356. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  357. }
  358. } else {
  359. testMethod(t, r, http.MethodPost)
  360. initreq := url.Values{}
  361. initreq.Set("uploads", "")
  362. compreq := url.Values{}
  363. compreq.Set("uploadId", uploadid)
  364. r.ParseForm()
  365. if reflect.DeepEqual(r.Form, initreq) {
  366. // 初始化分块上传
  367. fmt.Fprintf(w, `<InitiateMultipartUploadResult>
  368. <Bucket></Bucket>
  369. <Key>%v</Key>
  370. <UploadId>%v</UploadId>
  371. </InitiateMultipartUploadResult>`, "test.go.upload", uploadid)
  372. } else if reflect.DeepEqual(r.Form, compreq) {
  373. // 完成分块上传
  374. tb := crc64.MakeTable(crc64.ECMA)
  375. crc := uint64(0)
  376. ccv := make([]uint64, 10)
  377. for i, v := range rb {
  378. ccv[i] = crc64.Update(0, crc64.MakeTable(crc64.ECMA), v)
  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. }