浏览代码

初始化

wangchuanjin 5 年之前
父节点
当前提交
96ef45be5e
共有 100 个文件被更改,包括 17423 次插入0 次删除
  1. 103 0
      src/github.com/PuerkitoBio/goquery/array.go
  2. 180 0
      src/github.com/PuerkitoBio/goquery/array_test.go
  3. 112 0
      src/github.com/PuerkitoBio/goquery/bench_array_test.go
  4. 42 0
      src/github.com/PuerkitoBio/goquery/bench_example_test.go
  5. 72 0
      src/github.com/PuerkitoBio/goquery/bench_expand_test.go
  6. 212 0
      src/github.com/PuerkitoBio/goquery/bench_filter_test.go
  7. 62 0
      src/github.com/PuerkitoBio/goquery/bench_iteration_test.go
  8. 47 0
      src/github.com/PuerkitoBio/goquery/bench_property_test.go
  9. 97 0
      src/github.com/PuerkitoBio/goquery/bench_query_test.go
  10. 716 0
      src/github.com/PuerkitoBio/goquery/bench_traversal_test.go
  11. 118 0
      src/github.com/PuerkitoBio/goquery/doc.go
  12. 32 0
      src/github.com/PuerkitoBio/goquery/example_test.go
  13. 49 0
      src/github.com/PuerkitoBio/goquery/expand.go
  14. 68 0
      src/github.com/PuerkitoBio/goquery/expand_test.go
  15. 156 0
      src/github.com/PuerkitoBio/goquery/filter.go
  16. 191 0
      src/github.com/PuerkitoBio/goquery/filter_test.go
  17. 33 0
      src/github.com/PuerkitoBio/goquery/iteration.go
  18. 88 0
      src/github.com/PuerkitoBio/goquery/iteration_test.go
  19. 551 0
      src/github.com/PuerkitoBio/goquery/manipulation.go
  20. 453 0
      src/github.com/PuerkitoBio/goquery/manipulation_test.go
  21. 292 0
      src/github.com/PuerkitoBio/goquery/property.go
  22. 252 0
      src/github.com/PuerkitoBio/goquery/property_test.go
  23. 56 0
      src/github.com/PuerkitoBio/goquery/query.go
  24. 96 0
      src/github.com/PuerkitoBio/goquery/query_test.go
  25. 696 0
      src/github.com/PuerkitoBio/goquery/traversal.go
  26. 697 0
      src/github.com/PuerkitoBio/goquery/traversal_test.go
  27. 113 0
      src/github.com/PuerkitoBio/goquery/type.go
  28. 192 0
      src/github.com/PuerkitoBio/goquery/type_test.go
  29. 84 0
      src/github.com/PuerkitoBio/goquery/utilities.go
  30. 149 0
      src/github.com/SKatiyar/qr/coding/gen.go
  31. 815 0
      src/github.com/SKatiyar/qr/coding/qr.go
  32. 133 0
      src/github.com/SKatiyar/qr/coding/qr_test.go
  33. 85 0
      src/github.com/SKatiyar/qr/gf256/blog_test.go
  34. 241 0
      src/github.com/SKatiyar/qr/gf256/gf256.go
  35. 194 0
      src/github.com/SKatiyar/qr/gf256/gf256_test.go
  36. 149 0
      src/github.com/SKatiyar/qr/libqrencode/qrencode.go
  37. 400 0
      src/github.com/SKatiyar/qr/png.go
  38. 56 0
      src/github.com/SKatiyar/qr/png_test.go
  39. 116 0
      src/github.com/SKatiyar/qr/qr.go
  40. 503 0
      src/github.com/SKatiyar/qr/web/pic.go
  41. 1118 0
      src/github.com/SKatiyar/qr/web/play.go
  42. 152 0
      src/github.com/SKatiyar/qr/web/resize/resize.go
  43. 29 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/.appveyor.yml
  44. 24 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/.github/ISSUE_TEMPLATE/Bug_Report.md
  45. 23 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/.github/ISSUE_TEMPLATE/Bug_Report_zh.md
  46. 27 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/.travis.yml
  47. 15 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/CONTRIBUTING.md
  48. 3969 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/ChangeLog.txt
  49. 95 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/Gopkg.lock
  50. 50 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/Gopkg.toml
  51. 201 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE
  52. 9 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/Makefile
  53. 142 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/README-CN.md
  54. 145 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/README.md
  55. 2 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/doc.go
  56. 11 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/0-Requirements-CN.md
  57. 11 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/0-Requirements-EN.md
  58. 24 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/1-Installation-CN.md
  59. 25 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/1-Installation-EN.md
  60. 24 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/10-Package-Management-CN.md
  61. 25 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/10-Package-Management-EN.md
  62. 116 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/2-Client-CN.md
  63. 120 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/2-Client-EN.md
  64. 32 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/3-Verify-CN.md
  65. 32 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/3-Verify-EN.md
  66. 31 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/4-Proxy-CN.md
  67. 31 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/4-Proxy-EN.md
  68. 35 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/5-Timeout-CN.md
  69. 35 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/5-Timeout-EN.md
  70. 8 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/6-Debug-CN.md
  71. 8 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/6-Debug-EN.md
  72. 56 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/7-Logger-CN.md
  73. 57 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/7-Logger-EN.md
  74. 29 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/8-Concurrent-CN.md
  75. 29 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/8-Concurrent-EN.md
  76. 30 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/9-Asynchronous-CN.md
  77. 30 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/docs/9-Asynchronous-EN.md
  78. 15 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/go.mod
  79. 33 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/go.sum
  80. 272 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/api_test.go
  81. 206 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/base.go
  82. 347 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/core_test.go
  83. 100 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/cr_test.go
  84. 156 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/credential_test.go
  85. 二进制
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/encyptfile
  86. 93 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/integration/error_test.go
  87. 249 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/api_timeout.go
  88. 22 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/api_timeout_test.go
  89. 18 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go
  90. 1 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential_test.go
  91. 34 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go
  92. 19 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential_test.go
  93. 12 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/bearer_token_credential.go
  94. 12 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/bearer_token_credential_test.go
  95. 29 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go
  96. 16 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role_test.go
  97. 30 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/env.go
  98. 30 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/env_test.go
  99. 92 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/instance_credentials.go
  100. 136 0
      src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/instance_credentials_test.go

+ 103 - 0
src/github.com/PuerkitoBio/goquery/array.go

@@ -0,0 +1,103 @@
+package goquery
+
+import (
+	"golang.org/x/net/html"
+)
+
+// First reduces the set of matched elements to the first in the set.
+// It returns a new Selection object, and an empty Selection object if the
+// the selection is empty.
+func (s *Selection) First() *Selection {
+	return s.Eq(0)
+}
+
+// Last reduces the set of matched elements to the last in the set.
+// It returns a new Selection object, and an empty Selection object if
+// the selection is empty.
+func (s *Selection) Last() *Selection {
+	return s.Eq(-1)
+}
+
+// Eq reduces the set of matched elements to the one at the specified index.
+// If a negative index is given, it counts backwards starting at the end of the
+// set. It returns a new Selection object, and an empty Selection object if the
+// index is invalid.
+func (s *Selection) Eq(index int) *Selection {
+	if index < 0 {
+		index += len(s.Nodes)
+	}
+
+	if index >= len(s.Nodes) || index < 0 {
+		return newEmptySelection(s.document)
+	}
+
+	return s.Slice(index, index+1)
+}
+
+// Slice reduces the set of matched elements to a subset specified by a range
+// of indices.
+func (s *Selection) Slice(start, end int) *Selection {
+	if start < 0 {
+		start += len(s.Nodes)
+	}
+	if end < 0 {
+		end += len(s.Nodes)
+	}
+	return pushStack(s, s.Nodes[start:end])
+}
+
+// Get retrieves the underlying node at the specified index.
+// Get without parameter is not implemented, since the node array is available
+// on the Selection object.
+func (s *Selection) Get(index int) *html.Node {
+	if index < 0 {
+		index += len(s.Nodes) // Negative index gets from the end
+	}
+	return s.Nodes[index]
+}
+
+// Index returns the position of the first element within the Selection object
+// relative to its sibling elements.
+func (s *Selection) Index() int {
+	if len(s.Nodes) > 0 {
+		return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length()
+	}
+	return -1
+}
+
+// IndexSelector returns the position of the first element within the
+// Selection object relative to the elements matched by the selector, or -1 if
+// not found.
+func (s *Selection) IndexSelector(selector string) int {
+	if len(s.Nodes) > 0 {
+		sel := s.document.Find(selector)
+		return indexInSlice(sel.Nodes, s.Nodes[0])
+	}
+	return -1
+}
+
+// IndexMatcher returns the position of the first element within the
+// Selection object relative to the elements matched by the matcher, or -1 if
+// not found.
+func (s *Selection) IndexMatcher(m Matcher) int {
+	if len(s.Nodes) > 0 {
+		sel := s.document.FindMatcher(m)
+		return indexInSlice(sel.Nodes, s.Nodes[0])
+	}
+	return -1
+}
+
+// IndexOfNode returns the position of the specified node within the Selection
+// object, or -1 if not found.
+func (s *Selection) IndexOfNode(node *html.Node) int {
+	return indexInSlice(s.Nodes, node)
+}
+
+// IndexOfSelection returns the position of the first node in the specified
+// Selection object within this Selection object, or -1 if not found.
+func (s *Selection) IndexOfSelection(sel *Selection) int {
+	if sel != nil && len(sel.Nodes) > 0 {
+		return indexInSlice(s.Nodes, sel.Nodes[0])
+	}
+	return -1
+}

+ 180 - 0
src/github.com/PuerkitoBio/goquery/array_test.go

@@ -0,0 +1,180 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func TestFirst(t *testing.T) {
+	sel := Doc().Find(".pvk-content").First()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestFirstEmpty(t *testing.T) {
+	sel := Doc().Find(".pvk-zzcontentzz").First()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestFirstRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.First().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestLast(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Last()
+	assertLength(t, sel.Nodes, 1)
+
+	// Should contain Footer
+	foot := Doc().Find(".footer")
+	if !sel.Contains(foot.Nodes[0]) {
+		t.Error("Last .pvk-content should contain .footer.")
+	}
+}
+
+func TestLastEmpty(t *testing.T) {
+	sel := Doc().Find(".pvk-zzcontentzz").Last()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestLastRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Last().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestEq(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Eq(1)
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestEqNegative(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Eq(-1)
+	assertLength(t, sel.Nodes, 1)
+
+	// Should contain Footer
+	foot := Doc().Find(".footer")
+	if !sel.Contains(foot.Nodes[0]) {
+		t.Error("Index -1 of .pvk-content should contain .footer.")
+	}
+}
+
+func TestEqEmpty(t *testing.T) {
+	sel := Doc().Find("something_random_that_does_not_exists").Eq(0)
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestEqInvalidPositive(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Eq(3)
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestEqInvalidNegative(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Eq(-4)
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestEqRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Eq(1).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestSlice(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Slice(0, 2)
+
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestSliceOutOfBounds(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find(".pvk-content").Slice(2, 12)
+}
+
+func TestNegativeSliceStart(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Slice(-2, 3)
+	assertLength(t, sel.Nodes, 1)
+	assertSelectionIs(t, sel.Eq(0), "#cf3")
+}
+
+func TestNegativeSliceEnd(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Slice(1, -1)
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel.Eq(0), "#cf2")
+	assertSelectionIs(t, sel.Eq(1), "#cf3")
+}
+
+func TestNegativeSliceBoth(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Slice(-3, -1)
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel.Eq(0), "#cf2")
+	assertSelectionIs(t, sel.Eq(1), "#cf3")
+}
+
+func TestNegativeSliceOutOfBounds(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find(".container-fluid").Slice(-12, -7)
+}
+
+func TestSliceRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Slice(0, 2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestGet(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	node := sel.Get(1)
+	if sel.Nodes[1] != node {
+		t.Errorf("Expected node %v to be %v.", node, sel.Nodes[1])
+	}
+}
+
+func TestGetNegative(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	node := sel.Get(-3)
+	if sel.Nodes[0] != node {
+		t.Errorf("Expected node %v to be %v.", node, sel.Nodes[0])
+	}
+}
+
+func TestGetInvalid(t *testing.T) {
+	defer assertPanic(t)
+	sel := Doc().Find(".pvk-content")
+	sel.Get(129)
+}
+
+func TestIndex(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	if i := sel.Index(); i != 1 {
+		t.Errorf("Expected index of 1, got %v.", i)
+	}
+}
+
+func TestIndexSelector(t *testing.T) {
+	sel := Doc().Find(".hero-unit")
+	if i := sel.IndexSelector("div"); i != 4 {
+		t.Errorf("Expected index of 4, got %v.", i)
+	}
+}
+
+func TestIndexOfNode(t *testing.T) {
+	sel := Doc().Find("div.pvk-gutter")
+	if i := sel.IndexOfNode(sel.Nodes[1]); i != 1 {
+		t.Errorf("Expected index of 1, got %v.", i)
+	}
+}
+
+func TestIndexOfNilNode(t *testing.T) {
+	sel := Doc().Find("div.pvk-gutter")
+	if i := sel.IndexOfNode(nil); i != -1 {
+		t.Errorf("Expected index of -1, got %v.", i)
+	}
+}
+
+func TestIndexOfSelection(t *testing.T) {
+	sel := Doc().Find("div")
+	sel2 := Doc().Find(".hero-unit")
+	if i := sel.IndexOfSelection(sel2); i != 4 {
+		t.Errorf("Expected index of 4, got %v.", i)
+	}
+}

+ 112 - 0
src/github.com/PuerkitoBio/goquery/bench_array_test.go

@@ -0,0 +1,112 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkFirst(b *testing.B) {
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.First()
+	}
+}
+
+func BenchmarkLast(b *testing.B) {
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Last()
+	}
+}
+
+func BenchmarkEq(b *testing.B) {
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	j := 0
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Eq(j)
+		if j++; j >= sel.Length() {
+			j = 0
+		}
+	}
+}
+
+func BenchmarkSlice(b *testing.B) {
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	j := 0
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Slice(j, j+4)
+		if j++; j >= (sel.Length() - 4) {
+			j = 0
+		}
+	}
+}
+
+func BenchmarkGet(b *testing.B) {
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	j := 0
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Get(j)
+		if j++; j >= sel.Length() {
+			j = 0
+		}
+	}
+}
+
+func BenchmarkIndex(b *testing.B) {
+	var j int
+
+	b.StopTimer()
+	sel := DocB().Find("#Main")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		j = sel.Index()
+	}
+	b.Logf("Index=%d", j)
+}
+
+func BenchmarkIndexSelector(b *testing.B) {
+	var j int
+
+	b.StopTimer()
+	sel := DocB().Find("#manual-nav dl dd:nth-child(1)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		j = sel.IndexSelector("dd")
+	}
+	b.Logf("IndexSelector=%d", j)
+}
+
+func BenchmarkIndexOfNode(b *testing.B) {
+	var j int
+
+	b.StopTimer()
+	sel := DocB().Find("span a")
+	sel2 := DocB().Find("span a:nth-child(3)")
+	n := sel2.Get(0)
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		j = sel.IndexOfNode(n)
+	}
+	b.Logf("IndexOfNode=%d", j)
+}
+
+func BenchmarkIndexOfSelection(b *testing.B) {
+	var j int
+	b.StopTimer()
+	sel := DocB().Find("span a")
+	sel2 := DocB().Find("span a:nth-child(3)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		j = sel.IndexOfSelection(sel2)
+	}
+	b.Logf("IndexOfSelection=%d", j)
+}

+ 42 - 0
src/github.com/PuerkitoBio/goquery/bench_example_test.go

@@ -0,0 +1,42 @@
+package goquery
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+	"testing"
+)
+
+func BenchmarkMetalReviewExample(b *testing.B) {
+	var n int
+	var buf bytes.Buffer
+
+	b.StopTimer()
+	doc := loadDoc("metalreview.html")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		doc.Find(".slider-row:nth-child(1) .slider-item").Each(func(i int, s *Selection) {
+			var band, title string
+			var score float64
+			var e error
+
+			n++
+			// For each item found, get the band, title and score, and print it
+			band = s.Find("strong").Text()
+			title = s.Find("em").Text()
+			if score, e = strconv.ParseFloat(s.Find(".score").Text(), 64); e != nil {
+				// Not a valid float, ignore score
+				if n <= 4 {
+					buf.WriteString(fmt.Sprintf("Review %d: %s - %s.\n", i, band, title))
+				}
+			} else {
+				// Print all, including score
+				if n <= 4 {
+					buf.WriteString(fmt.Sprintf("Review %d: %s - %s (%2.1f).\n", i, band, title, score))
+				}
+			}
+		})
+	}
+	b.Log(buf.String())
+	b.Logf("MetalReviewExample=%d", n)
+}

+ 72 - 0
src/github.com/PuerkitoBio/goquery/bench_expand_test.go

@@ -0,0 +1,72 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkAdd(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Add("h2[title]").Length()
+		} else {
+			sel.Add("h2[title]")
+		}
+	}
+	b.Logf("Add=%d", n)
+}
+
+func BenchmarkAddSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	sel2 := DocB().Find("h2[title]")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.AddSelection(sel2).Length()
+		} else {
+			sel.AddSelection(sel2)
+		}
+	}
+	b.Logf("AddSelection=%d", n)
+}
+
+func BenchmarkAddNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocB().Find("dd")
+	sel2 := DocB().Find("h2[title]")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.AddNodes(nodes...).Length()
+		} else {
+			sel.AddNodes(nodes...)
+		}
+	}
+	b.Logf("AddNodes=%d", n)
+}
+
+func BenchmarkAndSelf(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocB().Find("dd").Parent()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.AndSelf().Length()
+		} else {
+			sel.AndSelf()
+		}
+	}
+	b.Logf("AndSelf=%d", n)
+}

+ 212 - 0
src/github.com/PuerkitoBio/goquery/bench_filter_test.go

@@ -0,0 +1,212 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkFilter(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Filter(".toclevel-1").Length()
+		} else {
+			sel.Filter(".toclevel-1")
+		}
+	}
+	b.Logf("Filter=%d", n)
+}
+
+func BenchmarkNot(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Not(".toclevel-2").Length()
+		} else {
+			sel.Filter(".toclevel-2")
+		}
+	}
+	b.Logf("Not=%d", n)
+}
+
+func BenchmarkFilterFunction(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	f := func(i int, s *Selection) bool {
+		return len(s.Get(0).Attr) > 0
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.FilterFunction(f).Length()
+		} else {
+			sel.FilterFunction(f)
+		}
+	}
+	b.Logf("FilterFunction=%d", n)
+}
+
+func BenchmarkNotFunction(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	f := func(i int, s *Selection) bool {
+		return len(s.Get(0).Attr) > 0
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NotFunction(f).Length()
+		} else {
+			sel.NotFunction(f)
+		}
+	}
+	b.Logf("NotFunction=%d", n)
+}
+
+func BenchmarkFilterNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-2")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.FilterNodes(nodes...).Length()
+		} else {
+			sel.FilterNodes(nodes...)
+		}
+	}
+	b.Logf("FilterNodes=%d", n)
+}
+
+func BenchmarkNotNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-1")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NotNodes(nodes...).Length()
+		} else {
+			sel.NotNodes(nodes...)
+		}
+	}
+	b.Logf("NotNodes=%d", n)
+}
+
+func BenchmarkFilterSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.FilterSelection(sel2).Length()
+		} else {
+			sel.FilterSelection(sel2)
+		}
+	}
+	b.Logf("FilterSelection=%d", n)
+}
+
+func BenchmarkNotSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-1")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NotSelection(sel2).Length()
+		} else {
+			sel.NotSelection(sel2)
+		}
+	}
+	b.Logf("NotSelection=%d", n)
+}
+
+func BenchmarkHas(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Has(".editsection").Length()
+		} else {
+			sel.Has(".editsection")
+		}
+	}
+	b.Logf("Has=%d", n)
+}
+
+func BenchmarkHasNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".tocnumber")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.HasNodes(nodes...).Length()
+		} else {
+			sel.HasNodes(nodes...)
+		}
+	}
+	b.Logf("HasNodes=%d", n)
+}
+
+func BenchmarkHasSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".tocnumber")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.HasSelection(sel2).Length()
+		} else {
+			sel.HasSelection(sel2)
+		}
+	}
+	b.Logf("HasSelection=%d", n)
+}
+
+func BenchmarkEnd(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li").Has(".tocnumber")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.End().Length()
+		} else {
+			sel.End()
+		}
+	}
+	b.Logf("End=%d", n)
+}

+ 62 - 0
src/github.com/PuerkitoBio/goquery/bench_iteration_test.go

@@ -0,0 +1,62 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkEach(b *testing.B) {
+	var tmp, n int
+
+	b.StopTimer()
+	sel := DocW().Find("td")
+	f := func(i int, s *Selection) {
+		tmp++
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Each(f)
+		if n == 0 {
+			n = tmp
+		}
+	}
+	b.Logf("Each=%d", n)
+}
+
+func BenchmarkMap(b *testing.B) {
+	var tmp, n int
+
+	b.StopTimer()
+	sel := DocW().Find("td")
+	f := func(i int, s *Selection) string {
+		tmp++
+		return string(tmp)
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Map(f)
+		if n == 0 {
+			n = tmp
+		}
+	}
+	b.Logf("Map=%d", n)
+}
+
+func BenchmarkEachWithBreak(b *testing.B) {
+	var tmp, n int
+
+	b.StopTimer()
+	sel := DocW().Find("td")
+	f := func(i int, s *Selection) bool {
+		tmp++
+		return tmp < 10
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		tmp = 0
+		sel.EachWithBreak(f)
+		if n == 0 {
+			n = tmp
+		}
+	}
+	b.Logf("Each=%d", n)
+}

+ 47 - 0
src/github.com/PuerkitoBio/goquery/bench_property_test.go

@@ -0,0 +1,47 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkAttr(b *testing.B) {
+	var s string
+
+	b.StopTimer()
+	sel := DocW().Find("h1")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		s, _ = sel.Attr("id")
+	}
+	b.Logf("Attr=%s", s)
+}
+
+func BenchmarkText(b *testing.B) {
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Text()
+	}
+}
+
+func BenchmarkLength(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		n = sel.Length()
+	}
+	b.Logf("Length=%d", n)
+}
+
+func BenchmarkHtml(b *testing.B) {
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		sel.Html()
+	}
+}

+ 97 - 0
src/github.com/PuerkitoBio/goquery/bench_query_test.go

@@ -0,0 +1,97 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkIs(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.Is(".toclevel-2")
+	}
+	b.Logf("Is=%v", y)
+}
+
+func BenchmarkIsPositional(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.Is("li:nth-child(2)")
+	}
+	b.Logf("IsPositional=%v", y)
+}
+
+func BenchmarkIsFunction(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1")
+	f := func(i int, s *Selection) bool {
+		return i == 8
+	}
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.IsFunction(f)
+	}
+	b.Logf("IsFunction=%v", y)
+}
+
+func BenchmarkIsSelection(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.IsSelection(sel2)
+	}
+	b.Logf("IsSelection=%v", y)
+}
+
+func BenchmarkIsNodes(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	sel2 := DocW().Find(".toclevel-2")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.IsNodes(nodes...)
+	}
+	b.Logf("IsNodes=%v", y)
+}
+
+func BenchmarkHasClass(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("span")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.HasClass("official")
+	}
+	b.Logf("HasClass=%v", y)
+}
+
+func BenchmarkContains(b *testing.B) {
+	var y bool
+
+	b.StopTimer()
+	sel := DocW().Find("span.url")
+	sel2 := DocW().Find("a[rel=\"nofollow\"]")
+	node := sel2.Nodes[0]
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		y = sel.Contains(node)
+	}
+	b.Logf("Contains=%v", y)
+}

+ 716 - 0
src/github.com/PuerkitoBio/goquery/bench_traversal_test.go

@@ -0,0 +1,716 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func BenchmarkFind(b *testing.B) {
+	var n int
+
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = DocB().Find("dd").Length()
+
+		} else {
+			DocB().Find("dd")
+		}
+	}
+	b.Logf("Find=%d", n)
+}
+
+func BenchmarkFindWithinSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("ul")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Find("a[class]").Length()
+		} else {
+			sel.Find("a[class]")
+		}
+	}
+	b.Logf("FindWithinSelection=%d", n)
+}
+
+func BenchmarkFindSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("ul")
+	sel2 := DocW().Find("span")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.FindSelection(sel2).Length()
+		} else {
+			sel.FindSelection(sel2)
+		}
+	}
+	b.Logf("FindSelection=%d", n)
+}
+
+func BenchmarkFindNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("ul")
+	sel2 := DocW().Find("span")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.FindNodes(nodes...).Length()
+		} else {
+			sel.FindNodes(nodes...)
+		}
+	}
+	b.Logf("FindNodes=%d", n)
+}
+
+func BenchmarkContents(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Contents().Length()
+		} else {
+			sel.Contents()
+		}
+	}
+	b.Logf("Contents=%d", n)
+}
+
+func BenchmarkContentsFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ContentsFiltered("a[href=\"#Examples\"]").Length()
+		} else {
+			sel.ContentsFiltered("a[href=\"#Examples\"]")
+		}
+	}
+	b.Logf("ContentsFiltered=%d", n)
+}
+
+func BenchmarkChildren(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Children().Length()
+		} else {
+			sel.Children()
+		}
+	}
+	b.Logf("Children=%d", n)
+}
+
+func BenchmarkChildrenFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h3")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ChildrenFiltered(".editsection").Length()
+		} else {
+			sel.ChildrenFiltered(".editsection")
+		}
+	}
+	b.Logf("ChildrenFiltered=%d", n)
+}
+
+func BenchmarkParent(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Parent().Length()
+		} else {
+			sel.Parent()
+		}
+	}
+	b.Logf("Parent=%d", n)
+}
+
+func BenchmarkParentFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentFiltered("ul[id]").Length()
+		} else {
+			sel.ParentFiltered("ul[id]")
+		}
+	}
+	b.Logf("ParentFiltered=%d", n)
+}
+
+func BenchmarkParents(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("th a")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Parents().Length()
+		} else {
+			sel.Parents()
+		}
+	}
+	b.Logf("Parents=%d", n)
+}
+
+func BenchmarkParentsFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("th a")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsFiltered("tr").Length()
+		} else {
+			sel.ParentsFiltered("tr")
+		}
+	}
+	b.Logf("ParentsFiltered=%d", n)
+}
+
+func BenchmarkParentsUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("th a")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsUntil("table").Length()
+		} else {
+			sel.ParentsUntil("table")
+		}
+	}
+	b.Logf("ParentsUntil=%d", n)
+}
+
+func BenchmarkParentsUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("th a")
+	sel2 := DocW().Find("#content")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsUntilSelection(sel2).Length()
+		} else {
+			sel.ParentsUntilSelection(sel2)
+		}
+	}
+	b.Logf("ParentsUntilSelection=%d", n)
+}
+
+func BenchmarkParentsUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("th a")
+	sel2 := DocW().Find("#content")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsUntilNodes(nodes...).Length()
+		} else {
+			sel.ParentsUntilNodes(nodes...)
+		}
+	}
+	b.Logf("ParentsUntilNodes=%d", n)
+}
+
+func BenchmarkParentsFilteredUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1 a")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsFilteredUntil(":nth-child(1)", "ul").Length()
+		} else {
+			sel.ParentsFilteredUntil(":nth-child(1)", "ul")
+		}
+	}
+	b.Logf("ParentsFilteredUntil=%d", n)
+}
+
+func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1 a")
+	sel2 := DocW().Find("ul")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2).Length()
+		} else {
+			sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
+		}
+	}
+	b.Logf("ParentsFilteredUntilSelection=%d", n)
+}
+
+func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find(".toclevel-1 a")
+	sel2 := DocW().Find("ul")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...).Length()
+		} else {
+			sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
+		}
+	}
+	b.Logf("ParentsFilteredUntilNodes=%d", n)
+}
+
+func BenchmarkSiblings(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("ul li:nth-child(1)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Siblings().Length()
+		} else {
+			sel.Siblings()
+		}
+	}
+	b.Logf("Siblings=%d", n)
+}
+
+func BenchmarkSiblingsFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("ul li:nth-child(1)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.SiblingsFiltered("[class]").Length()
+		} else {
+			sel.SiblingsFiltered("[class]")
+		}
+	}
+	b.Logf("SiblingsFiltered=%d", n)
+}
+
+func BenchmarkNext(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(1)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Next().Length()
+		} else {
+			sel.Next()
+		}
+	}
+	b.Logf("Next=%d", n)
+}
+
+func BenchmarkNextFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(1)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextFiltered("[class]").Length()
+		} else {
+			sel.NextFiltered("[class]")
+		}
+	}
+	b.Logf("NextFiltered=%d", n)
+}
+
+func BenchmarkNextAll(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(3)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextAll().Length()
+		} else {
+			sel.NextAll()
+		}
+	}
+	b.Logf("NextAll=%d", n)
+}
+
+func BenchmarkNextAllFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(3)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextAllFiltered("[class]").Length()
+		} else {
+			sel.NextAllFiltered("[class]")
+		}
+	}
+	b.Logf("NextAllFiltered=%d", n)
+}
+
+func BenchmarkPrev(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:last-child")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Prev().Length()
+		} else {
+			sel.Prev()
+		}
+	}
+	b.Logf("Prev=%d", n)
+}
+
+func BenchmarkPrevFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:last-child")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevFiltered("[class]").Length()
+		} else {
+			sel.PrevFiltered("[class]")
+		}
+	}
+	// There is one more Prev li with a class, compared to Next li with a class
+	// (confirmed by looking at the HTML, this is ok)
+	b.Logf("PrevFiltered=%d", n)
+}
+
+func BenchmarkPrevAll(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(4)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevAll().Length()
+		} else {
+			sel.PrevAll()
+		}
+	}
+	b.Logf("PrevAll=%d", n)
+}
+
+func BenchmarkPrevAllFiltered(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:nth-child(4)")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevAllFiltered("[class]").Length()
+		} else {
+			sel.PrevAllFiltered("[class]")
+		}
+	}
+	b.Logf("PrevAllFiltered=%d", n)
+}
+
+func BenchmarkNextUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:first-child")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextUntil(":nth-child(4)").Length()
+		} else {
+			sel.NextUntil(":nth-child(4)")
+		}
+	}
+	b.Logf("NextUntil=%d", n)
+}
+
+func BenchmarkNextUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("ul")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextUntilSelection(sel2).Length()
+		} else {
+			sel.NextUntilSelection(sel2)
+		}
+	}
+	b.Logf("NextUntilSelection=%d", n)
+}
+
+func BenchmarkNextUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("p")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextUntilNodes(nodes...).Length()
+		} else {
+			sel.NextUntilNodes(nodes...)
+		}
+	}
+	b.Logf("NextUntilNodes=%d", n)
+}
+
+func BenchmarkPrevUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("li:last-child")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevUntil(":nth-child(4)").Length()
+		} else {
+			sel.PrevUntil(":nth-child(4)")
+		}
+	}
+	b.Logf("PrevUntil=%d", n)
+}
+
+func BenchmarkPrevUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("ul")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevUntilSelection(sel2).Length()
+		} else {
+			sel.PrevUntilSelection(sel2)
+		}
+	}
+	b.Logf("PrevUntilSelection=%d", n)
+}
+
+func BenchmarkPrevUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("p")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevUntilNodes(nodes...).Length()
+		} else {
+			sel.PrevUntilNodes(nodes...)
+		}
+	}
+	b.Logf("PrevUntilNodes=%d", n)
+}
+
+func BenchmarkNextFilteredUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextFilteredUntil("p", "div").Length()
+		} else {
+			sel.NextFilteredUntil("p", "div")
+		}
+	}
+	b.Logf("NextFilteredUntil=%d", n)
+}
+
+func BenchmarkNextFilteredUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("div")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextFilteredUntilSelection("p", sel2).Length()
+		} else {
+			sel.NextFilteredUntilSelection("p", sel2)
+		}
+	}
+	b.Logf("NextFilteredUntilSelection=%d", n)
+}
+
+func BenchmarkNextFilteredUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("div")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.NextFilteredUntilNodes("p", nodes...).Length()
+		} else {
+			sel.NextFilteredUntilNodes("p", nodes...)
+		}
+	}
+	b.Logf("NextFilteredUntilNodes=%d", n)
+}
+
+func BenchmarkPrevFilteredUntil(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevFilteredUntil("p", "div").Length()
+		} else {
+			sel.PrevFilteredUntil("p", "div")
+		}
+	}
+	b.Logf("PrevFilteredUntil=%d", n)
+}
+
+func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("div")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevFilteredUntilSelection("p", sel2).Length()
+		} else {
+			sel.PrevFilteredUntilSelection("p", sel2)
+		}
+	}
+	b.Logf("PrevFilteredUntilSelection=%d", n)
+}
+
+func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := DocW().Find("h2")
+	sel2 := DocW().Find("div")
+	nodes := sel2.Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.PrevFilteredUntilNodes("p", nodes...).Length()
+		} else {
+			sel.PrevFilteredUntilNodes("p", nodes...)
+		}
+	}
+	b.Logf("PrevFilteredUntilNodes=%d", n)
+}
+
+func BenchmarkClosest(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := Doc().Find(".container-fluid")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.Closest(".pvk-content").Length()
+		} else {
+			sel.Closest(".pvk-content")
+		}
+	}
+	b.Logf("Closest=%d", n)
+}
+
+func BenchmarkClosestSelection(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".pvk-content")
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ClosestSelection(sel2).Length()
+		} else {
+			sel.ClosestSelection(sel2)
+		}
+	}
+	b.Logf("ClosestSelection=%d", n)
+}
+
+func BenchmarkClosestNodes(b *testing.B) {
+	var n int
+
+	b.StopTimer()
+	sel := Doc().Find(".container-fluid")
+	nodes := Doc().Find(".pvk-content").Nodes
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.ClosestNodes(nodes...).Length()
+		} else {
+			sel.ClosestNodes(nodes...)
+		}
+	}
+	b.Logf("ClosestNodes=%d", n)
+}

+ 118 - 0
src/github.com/PuerkitoBio/goquery/doc.go

@@ -0,0 +1,118 @@
+// Copyright (c) 2012-2014, Martin Angers & Contributors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation and/or
+// other materials provided with the distribution.
+// * Neither the name of the author nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+// WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*
+Package goquery implements features similar to jQuery, including the chainable
+syntax, to manipulate and query an HTML document.
+
+It brings a syntax and a set of features similar to jQuery to the Go language.
+It is based on Go's net/html package and the CSS Selector library cascadia.
+Since the net/html parser returns nodes, and not a full-featured DOM
+tree, jQuery's stateful manipulation functions (like height(), css(), detach())
+have been left off.
+
+Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is
+the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
+See the repository's wiki for various options on how to do this.
+
+Syntax-wise, it is as close as possible to jQuery, with the same function names when
+possible, and that warm and fuzzy chainable interface. jQuery being the
+ultra-popular library that it is, writing a similar HTML-manipulating
+library was better to follow its API than to start anew (in the same spirit as
+Go's fmt package), even though some of its methods are less than intuitive (looking
+at you, index()...).
+
+It is hosted on GitHub, along with additional documentation in the README.md
+file: https://github.com/puerkitobio/goquery
+
+Please note that because of the net/html dependency, goquery requires Go1.1+.
+
+The various methods are split into files based on the category of behavior.
+The three dots (...) indicate that various "overloads" are available.
+
+* array.go : array-like positional manipulation of the selection.
+    - Eq()
+    - First()
+    - Get()
+    - Index...()
+    - Last()
+    - Slice()
+
+* expand.go : methods that expand or augment the selection's set.
+    - Add...()
+    - AndSelf()
+    - Union(), which is an alias for AddSelection()
+
+* filter.go : filtering methods, that reduce the selection's set.
+    - End()
+    - Filter...()
+    - Has...()
+    - Intersection(), which is an alias of FilterSelection()
+    - Not...()
+
+* iteration.go : methods to loop over the selection's nodes.
+    - Each()
+    - EachWithBreak()
+    - Map()
+
+* manipulation.go : methods for modifying the document
+    - After...()
+    - Append...()
+    - Before...()
+    - Clone()
+    - Empty()
+    - Prepend...()
+    - Remove...()
+    - ReplaceWith...()
+    - Unwrap()
+    - Wrap...()
+    - WrapAll...()
+    - WrapInner...()
+
+* property.go : methods that inspect and get the node's properties values.
+    - Attr*(), RemoveAttr(), SetAttr()
+    - AddClass(), HasClass(), RemoveClass(), ToggleClass()
+    - Html()
+    - Length()
+    - Size(), which is an alias for Length()
+    - Text()
+
+* query.go : methods that query, or reflect, a node's identity.
+    - Contains()
+    - Is...()
+
+* traversal.go : methods to traverse the HTML document tree.
+    - Children...()
+    - Contents()
+    - Find...()
+    - Next...()
+    - Parent[s]...()
+    - Prev...()
+    - Siblings...()
+
+* type.go : definition of the types exposed by goquery.
+    - Document
+    - Selection
+    - Matcher
+*/
+package goquery

+ 32 - 0
src/github.com/PuerkitoBio/goquery/example_test.go

@@ -0,0 +1,32 @@
+package goquery
+
+import (
+	"fmt"
+	"log"
+
+	// In real use, this import would be required (not in this example, since it
+	// is part of the goquery package)
+	//"github.com/PuerkitoBio/goquery"
+)
+
+// This example scrapes the reviews shown on the home page of metalsucks.net.
+func ExampleScrape_MetalSucks() {
+	// Load the HTML document (in real use, the type would be *goquery.Document)
+	doc, err := NewDocument("http://metalsucks.net")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Find the review items (the type of the Selection would be *goquery.Selection)
+	doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *Selection) {
+		// For each item found, get the band and title
+		band := s.Find("h3").Text()
+		title := s.Find("i").Text()
+		fmt.Printf("Review %d: %s - %s\n", i, band, title)
+	})
+	// To see the output of the Example while running the test suite (go test), simply
+	// remove the leading "x" before Output on the next line. This will cause the
+	// example to fail (all the "real" tests should pass).
+
+	// xOutput: voluntarily fail the Example output.
+}

+ 49 - 0
src/github.com/PuerkitoBio/goquery/expand.go

@@ -0,0 +1,49 @@
+package goquery
+
+import (
+	"github.com/andybalholm/cascadia"
+	"golang.org/x/net/html"
+)
+
+// Add adds the selector string's matching nodes to those in the current
+// selection and returns a new Selection object.
+// The selector string is run in the context of the document of the current
+// Selection object.
+func (s *Selection) Add(selector string) *Selection {
+	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, cascadia.MustCompile(selector))...)
+}
+
+// AddMatcher adds the matcher's matching nodes to those in the current
+// selection and returns a new Selection object.
+// The matcher is run in the context of the document of the current
+// Selection object.
+func (s *Selection) AddMatcher(m Matcher) *Selection {
+	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...)
+}
+
+// AddSelection adds the specified Selection object's nodes to those in the
+// current selection and returns a new Selection object.
+func (s *Selection) AddSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.AddNodes()
+	}
+	return s.AddNodes(sel.Nodes...)
+}
+
+// Union is an alias for AddSelection.
+func (s *Selection) Union(sel *Selection) *Selection {
+	return s.AddSelection(sel)
+}
+
+// AddNodes adds the specified nodes to those in the
+// current selection and returns a new Selection object.
+func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes))
+}
+
+// AndSelf adds the previous set of elements on the stack to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the previous one.
+func (s *Selection) AndSelf() *Selection {
+	return s.AddSelection(s.prevSel)
+}

+ 68 - 0
src/github.com/PuerkitoBio/goquery/expand_test.go

@@ -0,0 +1,68 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func TestAdd(t *testing.T) {
+	sel := Doc().Find("div.row-fluid").Add("a")
+	assertLength(t, sel.Nodes, 19)
+}
+
+func TestAddRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Add("a").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestAddSelection(t *testing.T) {
+	sel := Doc().Find("div.row-fluid")
+	sel2 := Doc().Find("a")
+	sel = sel.AddSelection(sel2)
+	assertLength(t, sel.Nodes, 19)
+}
+
+func TestAddSelectionNil(t *testing.T) {
+	sel := Doc().Find("div.row-fluid")
+	assertLength(t, sel.Nodes, 9)
+
+	sel = sel.AddSelection(nil)
+	assertLength(t, sel.Nodes, 9)
+}
+
+func TestAddSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Find("a")
+	sel2 = sel.AddSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestAddNodes(t *testing.T) {
+	sel := Doc().Find("div.pvk-gutter")
+	sel2 := Doc().Find(".pvk-content")
+	sel = sel.AddNodes(sel2.Nodes...)
+	assertLength(t, sel.Nodes, 9)
+}
+
+func TestAddNodesNone(t *testing.T) {
+	sel := Doc().Find("div.pvk-gutter").AddNodes()
+	assertLength(t, sel.Nodes, 6)
+}
+
+func TestAddNodesRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Find("a")
+	sel2 = sel.AddNodes(sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestAndSelf(t *testing.T) {
+	sel := Doc().Find(".span12").Last().AndSelf()
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestAndSelfRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Find("a").AndSelf().End().End()
+	assertEqual(t, sel, sel2)
+}

+ 156 - 0
src/github.com/PuerkitoBio/goquery/filter.go

@@ -0,0 +1,156 @@
+package goquery
+
+import (
+	"github.com/andybalholm/cascadia"
+	"golang.org/x/net/html"
+)
+
+// Filter reduces the set of matched elements to those that match the selector string.
+// It returns a new Selection object for this subset of matching elements.
+func (s *Selection) Filter(selector string) *Selection {
+	return s.FilterMatcher(cascadia.MustCompile(selector))
+}
+
+// FilterMatcher reduces the set of matched elements to those that match
+// the given matcher.
+// It returns a new Selection object for this subset of matching elements.
+func (s *Selection) FilterMatcher(m Matcher) *Selection {
+	return pushStack(s, winnow(s, m, true))
+}
+
+// Not removes elements from the Selection that match the selector string.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) Not(selector string) *Selection {
+	return s.NotMatcher(cascadia.MustCompile(selector))
+}
+
+// NotMatcher removes elements from the Selection that match the given matcher.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotMatcher(m Matcher) *Selection {
+	return pushStack(s, winnow(s, m, false))
+}
+
+// FilterFunction reduces the set of matched elements to those that pass the function's test.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection {
+	return pushStack(s, winnowFunction(s, f, true))
+}
+
+// NotFunction removes elements from the Selection that pass the function's test.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection {
+	return pushStack(s, winnowFunction(s, f, false))
+}
+
+// FilterNodes reduces the set of matched elements to those that match the specified nodes.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, winnowNodes(s, nodes, true))
+}
+
+// NotNodes removes elements from the Selection that match the specified nodes.
+// It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, winnowNodes(s, nodes, false))
+}
+
+// FilterSelection reduces the set of matched elements to those that match a
+// node in the specified Selection object.
+// It returns a new Selection object for this subset of elements.
+func (s *Selection) FilterSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, winnowNodes(s, nil, true))
+	}
+	return pushStack(s, winnowNodes(s, sel.Nodes, true))
+}
+
+// NotSelection removes elements from the Selection that match a node in the specified
+// Selection object. It returns a new Selection object with the matching elements removed.
+func (s *Selection) NotSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, winnowNodes(s, nil, false))
+	}
+	return pushStack(s, winnowNodes(s, sel.Nodes, false))
+}
+
+// Intersection is an alias for FilterSelection.
+func (s *Selection) Intersection(sel *Selection) *Selection {
+	return s.FilterSelection(sel)
+}
+
+// Has reduces the set of matched elements to those that have a descendant
+// that matches the selector.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) Has(selector string) *Selection {
+	return s.HasSelection(s.document.Find(selector))
+}
+
+// HasMatcher reduces the set of matched elements to those that have a descendant
+// that matches the matcher.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasMatcher(m Matcher) *Selection {
+	return s.HasSelection(s.document.FindMatcher(m))
+}
+
+// HasNodes reduces the set of matched elements to those that have a
+// descendant that matches one of the nodes.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasNodes(nodes ...*html.Node) *Selection {
+	return s.FilterFunction(func(_ int, sel *Selection) bool {
+		// Add all nodes that contain one of the specified nodes
+		for _, n := range nodes {
+			if sel.Contains(n) {
+				return true
+			}
+		}
+		return false
+	})
+}
+
+// HasSelection reduces the set of matched elements to those that have a
+// descendant that matches one of the nodes of the specified Selection object.
+// It returns a new Selection object with the matching elements.
+func (s *Selection) HasSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.HasNodes()
+	}
+	return s.HasNodes(sel.Nodes...)
+}
+
+// End ends the most recent filtering operation in the current chain and
+// returns the set of matched elements to its previous state.
+func (s *Selection) End() *Selection {
+	if s.prevSel != nil {
+		return s.prevSel
+	}
+	return newEmptySelection(s.document)
+}
+
+// Filter based on the matcher, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
+	// Optimize if keep is requested
+	if keep {
+		return m.Filter(sel.Nodes)
+	}
+	// Use grep
+	return grep(sel, func(i int, s *Selection) bool {
+		return !m.Match(s.Get(0))
+	})
+}
+
+// Filter based on an array of nodes, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
+	return grep(sel, func(i int, s *Selection) bool {
+		return isInSlice(nodes, s.Get(0)) == keep
+	})
+}
+
+// Filter based on a function test, and the indicator to keep (Filter) or
+// to get rid of (Not) the matching elements.
+func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node {
+	return grep(sel, func(i int, s *Selection) bool {
+		return f(i, s) == keep
+	})
+}

+ 191 - 0
src/github.com/PuerkitoBio/goquery/filter_test.go

@@ -0,0 +1,191 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func TestFilter(t *testing.T) {
+	sel := Doc().Find(".span12").Filter(".alert")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestFilterNone(t *testing.T) {
+	sel := Doc().Find(".span12").Filter(".zzalert")
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestFilterRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Filter(".alert").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestFilterFunction(t *testing.T) {
+	sel := Doc().Find(".pvk-content").FilterFunction(func(i int, s *Selection) bool {
+		return i > 0
+	})
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestFilterFunctionRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.FilterFunction(func(i int, s *Selection) bool {
+		return i > 0
+	}).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestFilterNode(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.FilterNodes(sel.Nodes[2])
+	assertLength(t, sel2.Nodes, 1)
+}
+
+func TestFilterNodeRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.FilterNodes(sel.Nodes[2]).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestFilterSelection(t *testing.T) {
+	sel := Doc().Find(".link")
+	sel2 := Doc().Find("a[ng-click]")
+	sel3 := sel.FilterSelection(sel2)
+	assertLength(t, sel3.Nodes, 1)
+}
+
+func TestFilterSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".link")
+	sel2 := Doc().Find("a[ng-click]")
+	sel2 = sel.FilterSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestFilterSelectionNil(t *testing.T) {
+	var sel2 *Selection
+
+	sel := Doc().Find(".link")
+	sel3 := sel.FilterSelection(sel2)
+	assertLength(t, sel3.Nodes, 0)
+}
+
+func TestNot(t *testing.T) {
+	sel := Doc().Find(".span12").Not(".alert")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestNotRollback(t *testing.T) {
+	sel := Doc().Find(".span12")
+	sel2 := sel.Not(".alert").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNotNone(t *testing.T) {
+	sel := Doc().Find(".span12").Not(".zzalert")
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestNotFunction(t *testing.T) {
+	sel := Doc().Find(".pvk-content").NotFunction(func(i int, s *Selection) bool {
+		return i > 0
+	})
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestNotFunctionRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.NotFunction(func(i int, s *Selection) bool {
+		return i > 0
+	}).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNotNode(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.NotNodes(sel.Nodes[2])
+	assertLength(t, sel2.Nodes, 2)
+}
+
+func TestNotNodeRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.NotNodes(sel.Nodes[2]).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNotSelection(t *testing.T) {
+	sel := Doc().Find(".link")
+	sel2 := Doc().Find("a[ng-click]")
+	sel3 := sel.NotSelection(sel2)
+	assertLength(t, sel3.Nodes, 6)
+}
+
+func TestNotSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".link")
+	sel2 := Doc().Find("a[ng-click]")
+	sel2 = sel.NotSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestIntersection(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter")
+	sel2 := Doc().Find("div").Intersection(sel)
+	assertLength(t, sel2.Nodes, 6)
+}
+
+func TestIntersectionRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter")
+	sel2 := Doc().Find("div")
+	sel2 = sel.Intersection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestHas(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Has(".center-content")
+	assertLength(t, sel.Nodes, 2)
+	// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
+}
+
+func TestHasRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.Has(".center-content").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestHasNodes(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".center-content")
+	sel = sel.HasNodes(sel2.Nodes...)
+	assertLength(t, sel.Nodes, 2)
+	// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
+}
+
+func TestHasNodesRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".center-content")
+	sel2 = sel.HasNodes(sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestHasSelection(t *testing.T) {
+	sel := Doc().Find("p")
+	sel2 := Doc().Find("small")
+	sel = sel.HasSelection(sel2)
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestHasSelectionRollback(t *testing.T) {
+	sel := Doc().Find("p")
+	sel2 := Doc().Find("small")
+	sel2 = sel.HasSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestEnd(t *testing.T) {
+	sel := Doc().Find("p").Has("small").End()
+	assertLength(t, sel.Nodes, 4)
+}
+
+func TestEndToTop(t *testing.T) {
+	sel := Doc().Find("p").Has("small").End().End().End()
+	assertLength(t, sel.Nodes, 0)
+}

+ 33 - 0
src/github.com/PuerkitoBio/goquery/iteration.go

@@ -0,0 +1,33 @@
+package goquery
+
+// Each iterates over a Selection object, executing a function for each
+// matched element. It returns the current Selection object.
+func (s *Selection) Each(f func(int, *Selection)) *Selection {
+	for i, n := range s.Nodes {
+		f(i, newSingleSelection(n, s.document))
+	}
+	return s
+}
+
+// EachWithBreak iterates over a Selection object, executing a function for each
+// matched element. It is identical to Each except that it is possible to break
+// out of the loop by returning false in the callback function. It returns the
+// current Selection object.
+func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
+	for i, n := range s.Nodes {
+		if !f(i, newSingleSelection(n, s.document)) {
+			return s
+		}
+	}
+	return s
+}
+
+// Map passes each element in the current matched set through a function,
+// producing a slice of string holding the returned values.
+func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
+	for i, n := range s.Nodes {
+		result = append(result, f(i, newSingleSelection(n, s.document)))
+	}
+
+	return result
+}

+ 88 - 0
src/github.com/PuerkitoBio/goquery/iteration_test.go

@@ -0,0 +1,88 @@
+package goquery
+
+import (
+	"testing"
+
+	"golang.org/x/net/html"
+)
+
+func TestEach(t *testing.T) {
+	var cnt int
+
+	sel := Doc().Find(".hero-unit .row-fluid").Each(func(i int, n *Selection) {
+		cnt++
+		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
+	}).Find("a")
+
+	if cnt != 4 {
+		t.Errorf("Expected Each() to call function 4 times, got %v times.", cnt)
+	}
+	assertLength(t, sel.Nodes, 6)
+}
+
+func TestEachWithBreak(t *testing.T) {
+	var cnt int
+
+	sel := Doc().Find(".hero-unit .row-fluid").EachWithBreak(func(i int, n *Selection) bool {
+		cnt++
+		t.Logf("At index %v, node %v", i, n.Nodes[0].Data)
+		return false
+	}).Find("a")
+
+	if cnt != 1 {
+		t.Errorf("Expected Each() to call function 1 time, got %v times.", cnt)
+	}
+	assertLength(t, sel.Nodes, 6)
+}
+
+func TestEachEmptySelection(t *testing.T) {
+	var cnt int
+
+	sel := Doc().Find("zzzz")
+	sel.Each(func(i int, n *Selection) {
+		cnt++
+	})
+	if cnt > 0 {
+		t.Error("Expected Each() to not be called on empty Selection.")
+	}
+	sel2 := sel.Find("div")
+	assertLength(t, sel2.Nodes, 0)
+}
+
+func TestMap(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	vals := sel.Map(func(i int, s *Selection) string {
+		n := s.Get(0)
+		if n.Type == html.ElementNode {
+			return n.Data
+		}
+		return ""
+	})
+	for _, v := range vals {
+		if v != "div" {
+			t.Error("Expected Map array result to be all 'div's.")
+		}
+	}
+	if len(vals) != 3 {
+		t.Errorf("Expected Map array result to have a length of 3, found %v.", len(vals))
+	}
+}
+
+func TestForRange(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	initLen := sel.Length()
+	for i := range sel.Nodes {
+		single := sel.Eq(i)
+		//h, err := single.Html()
+		//if err != nil {
+		//	t.Fatal(err)
+		//}
+		//fmt.Println(i, h)
+		if single.Length() != 1 {
+			t.Errorf("%d: expected length of 1, got %d", i, single.Length())
+		}
+	}
+	if sel.Length() != initLen {
+		t.Errorf("expected initial selection to still have length %d, got %d", initLen, sel.Length())
+	}
+}

+ 551 - 0
src/github.com/PuerkitoBio/goquery/manipulation.go

@@ -0,0 +1,551 @@
+package goquery
+
+import (
+	"strings"
+
+	"github.com/andybalholm/cascadia"
+	"golang.org/x/net/html"
+)
+
+// After applies the selector from the root document and inserts the matched elements
+// after the elements in the set of matched elements.
+//
+// If one of the matched elements in the selection is not currently in the
+// document, it's impossible to insert nodes after it, so it will be ignored.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) After(selector string) *Selection {
+	return s.AfterMatcher(cascadia.MustCompile(selector))
+}
+
+// AfterMatcher applies the matcher from the root document and inserts the matched elements
+// after the elements in the set of matched elements.
+//
+// If one of the matched elements in the selection is not currently in the
+// document, it's impossible to insert nodes after it, so it will be ignored.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterMatcher(m Matcher) *Selection {
+	return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// AfterSelection inserts the elements in the selection after each element in the set of matched
+// elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterSelection(sel *Selection) *Selection {
+	return s.AfterNodes(sel.Nodes...)
+}
+
+// AfterHtml parses the html and inserts it after the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterHtml(html string) *Selection {
+	return s.AfterNodes(parseHtml(html)...)
+}
+
+// AfterNodes inserts the nodes after each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
+		if sn.Parent != nil {
+			sn.Parent.InsertBefore(n, sn.NextSibling)
+		}
+	})
+}
+
+// Append appends the elements specified by the selector to the end of each element
+// in the set of matched elements, following those rules:
+//
+// 1) The selector is applied to the root document.
+//
+// 2) Elements that are part of the document will be moved to the new location.
+//
+// 3) If there are multiple locations to append to, cloned nodes will be
+// appended to all target locations except the last one, which will be moved
+// as noted in (2).
+func (s *Selection) Append(selector string) *Selection {
+	return s.AppendMatcher(cascadia.MustCompile(selector))
+}
+
+// AppendMatcher appends the elements specified by the matcher to the end of each element
+// in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendMatcher(m Matcher) *Selection {
+	return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// AppendSelection appends the elements in the selection to the end of each element
+// in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendSelection(sel *Selection) *Selection {
+	return s.AppendNodes(sel.Nodes...)
+}
+
+// AppendHtml parses the html and appends it to the set of matched elements.
+func (s *Selection) AppendHtml(html string) *Selection {
+	return s.AppendNodes(parseHtml(html)...)
+}
+
+// AppendNodes appends the specified nodes to each node in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
+		sn.AppendChild(n)
+	})
+}
+
+// Before inserts the matched elements before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) Before(selector string) *Selection {
+	return s.BeforeMatcher(cascadia.MustCompile(selector))
+}
+
+// BeforeMatcher inserts the matched elements before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeMatcher(m Matcher) *Selection {
+	return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// BeforeSelection inserts the elements in the selection before each element in the set of matched
+// elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeSelection(sel *Selection) *Selection {
+	return s.BeforeNodes(sel.Nodes...)
+}
+
+// BeforeHtml parses the html and inserts it before the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeHtml(html string) *Selection {
+	return s.BeforeNodes(parseHtml(html)...)
+}
+
+// BeforeNodes inserts the nodes before each element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
+		if sn.Parent != nil {
+			sn.Parent.InsertBefore(n, sn)
+		}
+	})
+}
+
+// Clone creates a deep copy of the set of matched nodes. The new nodes will not be
+// attached to the document.
+func (s *Selection) Clone() *Selection {
+	ns := newEmptySelection(s.document)
+	ns.Nodes = cloneNodes(s.Nodes)
+	return ns
+}
+
+// Empty removes all children nodes from the set of matched elements.
+// It returns the children nodes in a new Selection.
+func (s *Selection) Empty() *Selection {
+	var nodes []*html.Node
+
+	for _, n := range s.Nodes {
+		for c := n.FirstChild; c != nil; c = n.FirstChild {
+			n.RemoveChild(c)
+			nodes = append(nodes, c)
+		}
+	}
+
+	return pushStack(s, nodes)
+}
+
+// Prepend prepends the elements specified by the selector to each element in
+// the set of matched elements, following the same rules as Append.
+func (s *Selection) Prepend(selector string) *Selection {
+	return s.PrependMatcher(cascadia.MustCompile(selector))
+}
+
+// PrependMatcher prepends the elements specified by the matcher to each
+// element in the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependMatcher(m Matcher) *Selection {
+	return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// PrependSelection prepends the elements in the selection to each element in
+// the set of matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependSelection(sel *Selection) *Selection {
+	return s.PrependNodes(sel.Nodes...)
+}
+
+// PrependHtml parses the html and prepends it to the set of matched elements.
+func (s *Selection) PrependHtml(html string) *Selection {
+	return s.PrependNodes(parseHtml(html)...)
+}
+
+// PrependNodes prepends the specified nodes to each node in the set of
+// matched elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
+	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
+		// sn.FirstChild may be nil, in which case this functions like
+		// sn.AppendChild()
+		sn.InsertBefore(n, sn.FirstChild)
+	})
+}
+
+// Remove removes the set of matched elements from the document.
+// It returns the same selection, now consisting of nodes not in the document.
+func (s *Selection) Remove() *Selection {
+	for _, n := range s.Nodes {
+		if n.Parent != nil {
+			n.Parent.RemoveChild(n)
+		}
+	}
+
+	return s
+}
+
+// RemoveFiltered removes the set of matched elements by selector.
+// It returns the Selection of removed nodes.
+func (s *Selection) RemoveFiltered(selector string) *Selection {
+	return s.RemoveMatcher(cascadia.MustCompile(selector))
+}
+
+// RemoveMatcher removes the set of matched elements.
+// It returns the Selection of removed nodes.
+func (s *Selection) RemoveMatcher(m Matcher) *Selection {
+	return s.FilterMatcher(m).Remove()
+}
+
+// ReplaceWith replaces each element in the set of matched elements with the
+// nodes matched by the given selector.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWith(selector string) *Selection {
+	return s.ReplaceWithMatcher(cascadia.MustCompile(selector))
+}
+
+// ReplaceWithMatcher replaces each element in the set of matched elements with
+// the nodes matched by the given Matcher.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
+	return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// ReplaceWithSelection replaces each element in the set of matched elements with
+// the nodes from the given Selection.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
+	return s.ReplaceWithNodes(sel.Nodes...)
+}
+
+// ReplaceWithHtml replaces each element in the set of matched elements with
+// the parsed HTML.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithHtml(html string) *Selection {
+	return s.ReplaceWithNodes(parseHtml(html)...)
+}
+
+// ReplaceWithNodes replaces each element in the set of matched elements with
+// the given nodes.
+// It returns the removed elements.
+//
+// This follows the same rules as Selection.Append.
+func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
+	s.AfterNodes(ns...)
+	return s.Remove()
+}
+
+// Unwrap removes the parents of the set of matched elements, leaving the matched
+// elements (and their siblings, if any) in their place.
+// It returns the original selection.
+func (s *Selection) Unwrap() *Selection {
+	s.Parent().Each(func(i int, ss *Selection) {
+		// For some reason, jquery allows unwrap to remove the <head> element, so
+		// allowing it here too. Same for <html>. Why it allows those elements to
+		// be unwrapped while not allowing body is a mystery to me.
+		if ss.Nodes[0].Data != "body" {
+			ss.ReplaceWithSelection(ss.Contents())
+		}
+	})
+
+	return s
+}
+
+// Wrap wraps each element in the set of matched elements inside the first
+// element matched by the given selector. The matched child is cloned before
+// being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) Wrap(selector string) *Selection {
+	return s.WrapMatcher(cascadia.MustCompile(selector))
+}
+
+// WrapMatcher wraps each element in the set of matched elements inside the
+// first element matched by the given matcher. The matched child is cloned
+// before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapMatcher(m Matcher) *Selection {
+	return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapSelection wraps each element in the set of matched elements inside the
+// first element in the given Selection. The element is cloned before being
+// inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapSelection(sel *Selection) *Selection {
+	return s.wrapNodes(sel.Nodes...)
+}
+
+// WrapHtml wraps each element in the set of matched elements inside the inner-
+// most child of the given HTML.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapHtml(html string) *Selection {
+	return s.wrapNodes(parseHtml(html)...)
+}
+
+// WrapNode wraps each element in the set of matched elements inside the inner-
+// most child of the given node. The given node is copied before being inserted
+// into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapNode(n *html.Node) *Selection {
+	return s.wrapNodes(n)
+}
+
+func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
+	s.Each(func(i int, ss *Selection) {
+		ss.wrapAllNodes(ns...)
+	})
+
+	return s
+}
+
+// WrapAll wraps a single HTML structure, matched by the given selector, around
+// all elements in the set of matched elements. The matched child is cloned
+// before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAll(selector string) *Selection {
+	return s.WrapAllMatcher(cascadia.MustCompile(selector))
+}
+
+// WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
+// around all elements in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
+	return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapAllSelection wraps a single HTML structure, the first node of the given
+// Selection, around all elements in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
+	return s.wrapAllNodes(sel.Nodes...)
+}
+
+// WrapAllHtml wraps the given HTML structure around all elements in the set of
+// matched elements. The matched child is cloned before being inserted into the
+// document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllHtml(html string) *Selection {
+	return s.wrapAllNodes(parseHtml(html)...)
+}
+
+func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
+	if len(ns) > 0 {
+		return s.WrapAllNode(ns[0])
+	}
+	return s
+}
+
+// WrapAllNode wraps the given node around the first element in the Selection,
+// making all other nodes in the Selection children of the given node. The node
+// is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapAllNode(n *html.Node) *Selection {
+	if s.Size() == 0 {
+		return s
+	}
+
+	wrap := cloneNode(n)
+
+	first := s.Nodes[0]
+	if first.Parent != nil {
+		first.Parent.InsertBefore(wrap, first)
+		first.Parent.RemoveChild(first)
+	}
+
+	for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
+		wrap = c
+	}
+
+	newSingleSelection(wrap, s.document).AppendSelection(s)
+
+	return s
+}
+
+// WrapInner wraps an HTML structure, matched by the given selector, around the
+// content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInner(selector string) *Selection {
+	return s.WrapInnerMatcher(cascadia.MustCompile(selector))
+}
+
+// WrapInnerMatcher wraps an HTML structure, matched by the given selector,
+// around the content of element in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
+	return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
+}
+
+// WrapInnerSelection wraps an HTML structure, matched by the given selector,
+// around the content of element in the set of matched elements. The matched
+// child is cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
+	return s.wrapInnerNodes(sel.Nodes...)
+}
+
+// WrapInnerHtml wraps an HTML structure, matched by the given selector, around
+// the content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerHtml(html string) *Selection {
+	return s.wrapInnerNodes(parseHtml(html)...)
+}
+
+// WrapInnerNode wraps an HTML structure, matched by the given selector, around
+// the content of element in the set of matched elements. The matched child is
+// cloned before being inserted into the document.
+//
+// It returns the original set of elements.
+func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
+	return s.wrapInnerNodes(n)
+}
+
+func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
+	if len(ns) == 0 {
+		return s
+	}
+
+	s.Each(func(i int, s *Selection) {
+		contents := s.Contents()
+
+		if contents.Size() > 0 {
+			contents.wrapAllNodes(ns...)
+		} else {
+			s.AppendNodes(cloneNode(ns[0]))
+		}
+	})
+
+	return s
+}
+
+func parseHtml(h string) []*html.Node {
+	// Errors are only returned when the io.Reader returns any error besides
+	// EOF, but strings.Reader never will
+	nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
+	if err != nil {
+		panic("goquery: failed to parse HTML: " + err.Error())
+	}
+	return nodes
+}
+
+// Get the first child that is an ElementNode
+func getFirstChildEl(n *html.Node) *html.Node {
+	c := n.FirstChild
+	for c != nil && c.Type != html.ElementNode {
+		c = c.NextSibling
+	}
+	return c
+}
+
+// Deep copy a slice of nodes.
+func cloneNodes(ns []*html.Node) []*html.Node {
+	cns := make([]*html.Node, 0, len(ns))
+
+	for _, n := range ns {
+		cns = append(cns, cloneNode(n))
+	}
+
+	return cns
+}
+
+// Deep copy a node. The new node has clones of all the original node's
+// children but none of its parents or siblings.
+func cloneNode(n *html.Node) *html.Node {
+	nn := &html.Node{
+		Type:     n.Type,
+		DataAtom: n.DataAtom,
+		Data:     n.Data,
+		Attr:     make([]html.Attribute, len(n.Attr)),
+	}
+
+	copy(nn.Attr, n.Attr)
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		nn.AppendChild(cloneNode(c))
+	}
+
+	return nn
+}
+
+func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
+	f func(sn *html.Node, n *html.Node)) *Selection {
+
+	lasti := s.Size() - 1
+
+	// net.Html doesn't provide document fragments for insertion, so to get
+	// things in the correct order with After() and Prepend(), the callback
+	// needs to be called on the reverse of the nodes.
+	if reverse {
+		for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
+			ns[i], ns[j] = ns[j], ns[i]
+		}
+	}
+
+	for i, sn := range s.Nodes {
+		for _, n := range ns {
+			if i != lasti {
+				f(sn, cloneNode(n))
+			} else {
+				if n.Parent != nil {
+					n.Parent.RemoveChild(n)
+				}
+				f(sn, n)
+			}
+		}
+	}
+
+	return s
+}

+ 453 - 0
src/github.com/PuerkitoBio/goquery/manipulation_test.go

@@ -0,0 +1,453 @@
+package goquery
+
+import (
+	"testing"
+)
+
+const (
+	wrapHtml = "<div id=\"ins\">test string<div><p><em><b></b></em></p></div></div>"
+)
+
+func TestAfter(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").After("#nf6")
+
+	assertLength(t, doc.Find("#main #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#main + #nf6").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAfterMany(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".one").After("#nf6")
+
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 1)
+	assertLength(t, doc.Find("#main #nf6").Nodes, 1)
+	assertLength(t, doc.Find(".one + #nf6").Nodes, 2)
+	printSel(t, doc.Selection)
+}
+
+func TestAfterWithRemoved(t *testing.T) {
+	doc := Doc2Clone()
+	s := doc.Find("#main").Remove()
+	s.After("#nf6")
+
+	assertLength(t, s.Find("#nf6").Nodes, 0)
+	assertLength(t, doc.Find("#nf6").Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestAfterSelection(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").AfterSelection(doc.Find("#nf1, #nf2"))
+
+	assertLength(t, doc.Find("#main #nf1, #main #nf2").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf1, #foot #nf2").Nodes, 0)
+	assertLength(t, doc.Find("#main + #nf1, #nf1 + #nf2").Nodes, 2)
+	printSel(t, doc.Selection)
+}
+
+func TestAfterHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").AfterHtml("<strong>new node</strong>")
+
+	assertLength(t, doc.Find("#main + strong").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAppend(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").Append("#nf6")
+
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf6").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAppendBody(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("body").Append("#nf6")
+
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf6").Nodes, 0)
+	assertLength(t, doc.Find("body > #nf6").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAppendSelection(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").AppendSelection(doc.Find("#nf1, #nf2"))
+
+	assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf2").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf1").Nodes, 1)
+	assertLength(t, doc.Find("#main #nf2").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAppendSelectionExisting(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").AppendSelection(doc.Find("#n1, #n2"))
+
+	assertClass(t, doc.Find("#main :nth-child(1)"), "three")
+	assertClass(t, doc.Find("#main :nth-child(5)"), "one")
+	assertClass(t, doc.Find("#main :nth-child(6)"), "two")
+	printSel(t, doc.Selection)
+}
+
+func TestAppendClone(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#n1").AppendSelection(doc.Find("#nf1").Clone())
+
+	assertLength(t, doc.Find("#foot #nf1").Nodes, 1)
+	assertLength(t, doc.Find("#main #nf1").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestAppendHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("div").AppendHtml("<strong>new node</strong>")
+
+	assertLength(t, doc.Find("strong").Nodes, 14)
+	printSel(t, doc.Selection)
+}
+
+func TestBefore(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").Before("#nf6")
+
+	assertLength(t, doc.Find("#main #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("body > #nf6:first-child").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestBeforeWithRemoved(t *testing.T) {
+	doc := Doc2Clone()
+	s := doc.Find("#main").Remove()
+	s.Before("#nf6")
+
+	assertLength(t, s.Find("#nf6").Nodes, 0)
+	assertLength(t, doc.Find("#nf6").Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestBeforeSelection(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").BeforeSelection(doc.Find("#nf1, #nf2"))
+
+	assertLength(t, doc.Find("#main #nf1, #main #nf2").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf1, #foot #nf2").Nodes, 0)
+	assertLength(t, doc.Find("body > #nf1:first-child, #nf1 + #nf2").Nodes, 2)
+	printSel(t, doc.Selection)
+}
+
+func TestBeforeHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").BeforeHtml("<strong>new node</strong>")
+
+	assertLength(t, doc.Find("body > strong:first-child").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestEmpty(t *testing.T) {
+	doc := Doc2Clone()
+	s := doc.Find("#main").Empty()
+
+	assertLength(t, doc.Find("#main").Children().Nodes, 0)
+	assertLength(t, s.Filter("div").Nodes, 6)
+	printSel(t, doc.Selection)
+}
+
+func TestPrepend(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").Prepend("#nf6")
+
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf6:first-child").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestPrependBody(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("body").Prepend("#nf6")
+
+	assertLength(t, doc.Find("#foot #nf6").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf6").Nodes, 0)
+	assertLength(t, doc.Find("body > #nf6:first-child").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestPrependSelection(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").PrependSelection(doc.Find("#nf1, #nf2"))
+
+	assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
+	assertLength(t, doc.Find("#foot #nf2").Nodes, 0)
+	assertLength(t, doc.Find("#main #nf1:first-child").Nodes, 1)
+	assertLength(t, doc.Find("#main #nf2:nth-child(2)").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestPrependSelectionExisting(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main").PrependSelection(doc.Find("#n5, #n6"))
+
+	assertClass(t, doc.Find("#main :nth-child(1)"), "five")
+	assertClass(t, doc.Find("#main :nth-child(2)"), "six")
+	assertClass(t, doc.Find("#main :nth-child(5)"), "three")
+	assertClass(t, doc.Find("#main :nth-child(6)"), "four")
+	printSel(t, doc.Selection)
+}
+
+func TestPrependClone(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#n1").PrependSelection(doc.Find("#nf1").Clone())
+
+	assertLength(t, doc.Find("#foot #nf1:first-child").Nodes, 1)
+	assertLength(t, doc.Find("#main #nf1:first-child").Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestPrependHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("div").PrependHtml("<strong>new node</strong>")
+
+	assertLength(t, doc.Find("strong:first-child").Nodes, 14)
+	printSel(t, doc.Selection)
+}
+
+func TestRemove(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#nf1").Remove()
+
+	assertLength(t, doc.Find("#foot #nf1").Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestRemoveAll(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("*").Remove()
+
+	assertLength(t, doc.Find("*").Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestRemoveRoot(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("html").Remove()
+
+	assertLength(t, doc.Find("html").Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestRemoveFiltered(t *testing.T) {
+	doc := Doc2Clone()
+	nf6 := doc.Find("#nf6")
+	s := doc.Find("div").RemoveFiltered("#nf6")
+
+	assertLength(t, doc.Find("#nf6").Nodes, 0)
+	assertLength(t, s.Nodes, 1)
+	if nf6.Nodes[0] != s.Nodes[0] {
+		t.Error("Removed node does not match original")
+	}
+	printSel(t, doc.Selection)
+}
+
+func TestReplaceWith(t *testing.T) {
+	doc := Doc2Clone()
+
+	doc.Find("#nf6").ReplaceWith("#main")
+	assertLength(t, doc.Find("#foot #main:last-child").Nodes, 1)
+	printSel(t, doc.Selection)
+
+	doc.Find("#foot").ReplaceWith("#main")
+	assertLength(t, doc.Find("#foot").Nodes, 0)
+	assertLength(t, doc.Find("#main").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestReplaceWithHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#main, #foot").ReplaceWithHtml("<div id=\"replace\"></div>")
+
+	assertLength(t, doc.Find("#replace").Nodes, 2)
+
+	printSel(t, doc.Selection)
+}
+
+func TestReplaceWithSelection(t *testing.T) {
+	doc := Doc2Clone()
+	sel := doc.Find("#nf6").ReplaceWithSelection(doc.Find("#nf5"))
+
+	assertSelectionIs(t, sel, "#nf6")
+	assertLength(t, doc.Find("#nf6").Nodes, 0)
+	assertLength(t, doc.Find("#nf5").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestUnwrap(t *testing.T) {
+	doc := Doc2Clone()
+
+	doc.Find("#nf5").Unwrap()
+	assertLength(t, doc.Find("#foot").Nodes, 0)
+	assertLength(t, doc.Find("body > #nf1").Nodes, 1)
+	assertLength(t, doc.Find("body > #nf5").Nodes, 1)
+
+	printSel(t, doc.Selection)
+
+	doc = Doc2Clone()
+
+	doc.Find("#nf5, #n1").Unwrap()
+	assertLength(t, doc.Find("#foot").Nodes, 0)
+	assertLength(t, doc.Find("#main").Nodes, 0)
+	assertLength(t, doc.Find("body > #n1").Nodes, 1)
+	assertLength(t, doc.Find("body > #nf5").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestUnwrapBody(t *testing.T) {
+	doc := Doc2Clone()
+
+	doc.Find("#main").Unwrap()
+	assertLength(t, doc.Find("body").Nodes, 1)
+	assertLength(t, doc.Find("body > #main").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestUnwrapHead(t *testing.T) {
+	doc := Doc2Clone()
+
+	doc.Find("title").Unwrap()
+	assertLength(t, doc.Find("head").Nodes, 0)
+	assertLength(t, doc.Find("head > title").Nodes, 0)
+	assertLength(t, doc.Find("title").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestUnwrapHtml(t *testing.T) {
+	doc := Doc2Clone()
+
+	doc.Find("head").Unwrap()
+	assertLength(t, doc.Find("html").Nodes, 0)
+	assertLength(t, doc.Find("html head").Nodes, 0)
+	assertLength(t, doc.Find("head").Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrap(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#nf1").Wrap("#nf2")
+	nf1 := doc.Find("#foot #nf2 #nf1")
+	assertLength(t, nf1.Nodes, 1)
+
+	nf2 := doc.Find("#nf2")
+	assertLength(t, nf2.Nodes, 2)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapEmpty(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#nf1").Wrap("#doesnt-exist")
+
+	origHtml, _ := Doc2().Html()
+	newHtml, _ := doc.Html()
+
+	if origHtml != newHtml {
+		t.Error("Expected the two documents to be identical.")
+	}
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".odd").WrapHtml(wrapHtml)
+	nf2 := doc.Find("#ins #nf2")
+	assertLength(t, nf2.Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestWrapSelection(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#nf1").WrapSelection(doc.Find("#nf2"))
+	nf1 := doc.Find("#foot #nf2 #nf1")
+	assertLength(t, nf1.Nodes, 1)
+
+	nf2 := doc.Find("#nf2")
+	assertLength(t, nf2.Nodes, 2)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapAll(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".odd").WrapAll("#nf1")
+	nf1 := doc.Find("#main #nf1")
+	assertLength(t, nf1.Nodes, 1)
+
+	sel := nf1.Find("#n2 ~ #n4 ~ #n6 ~ #nf2 ~ #nf4 ~ #nf6")
+	assertLength(t, sel.Nodes, 1)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapAllHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".odd").WrapAllHtml(wrapHtml)
+	nf1 := doc.Find("#main div#ins div p em b #n2 ~ #n4 ~ #n6 ~ #nf2 ~ #nf4 ~ #nf6")
+	assertLength(t, nf1.Nodes, 1)
+	printSel(t, doc.Selection)
+}
+
+func TestWrapInnerNoContent(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".one").WrapInner(".two")
+
+	twos := doc.Find(".two")
+	assertLength(t, twos.Nodes, 4)
+	assertLength(t, doc.Find(".one .two").Nodes, 2)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapInnerWithContent(t *testing.T) {
+	doc := Doc3Clone()
+	doc.Find(".one").WrapInner(".two")
+
+	twos := doc.Find(".two")
+	assertLength(t, twos.Nodes, 4)
+	assertLength(t, doc.Find(".one .two").Nodes, 2)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapInnerNoWrapper(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find(".one").WrapInner(".not-exist")
+
+	twos := doc.Find(".two")
+	assertLength(t, twos.Nodes, 2)
+	assertLength(t, doc.Find(".one").Nodes, 2)
+	assertLength(t, doc.Find(".one .two").Nodes, 0)
+
+	printSel(t, doc.Selection)
+}
+
+func TestWrapInnerHtml(t *testing.T) {
+	doc := Doc2Clone()
+	doc.Find("#foot").WrapInnerHtml(wrapHtml)
+
+	foot := doc.Find("#foot div#ins div p em b #nf1 ~ #nf2 ~ #nf3")
+	assertLength(t, foot.Nodes, 1)
+
+	printSel(t, doc.Selection)
+}

+ 292 - 0
src/github.com/PuerkitoBio/goquery/property.go

@@ -0,0 +1,292 @@
+package goquery
+
+import (
+	"bytes"
+	"regexp"
+	"strings"
+
+	"golang.org/x/net/html"
+)
+
+var rxClassTrim = regexp.MustCompile("[\t\r\n]")
+
+// Attr gets the specified attribute's value for the first element in the
+// Selection. To get the value for each element individually, use a looping
+// construct such as Each or Map method.
+func (s *Selection) Attr(attrName string) (val string, exists bool) {
+	if len(s.Nodes) == 0 {
+		return
+	}
+	return getAttributeValue(attrName, s.Nodes[0])
+}
+
+// AttrOr works like Attr but returns default value if attribute is not present.
+func (s *Selection) AttrOr(attrName, defaultValue string) string {
+	if len(s.Nodes) == 0 {
+		return defaultValue
+	}
+
+	val, exists := getAttributeValue(attrName, s.Nodes[0])
+	if !exists {
+		return defaultValue
+	}
+
+	return val
+}
+
+// RemoveAttr removes the named attribute from each element in the set of matched elements.
+func (s *Selection) RemoveAttr(attrName string) *Selection {
+	for _, n := range s.Nodes {
+		removeAttr(n, attrName)
+	}
+
+	return s
+}
+
+// SetAttr sets the given attribute on each element in the set of matched elements.
+func (s *Selection) SetAttr(attrName, val string) *Selection {
+	for _, n := range s.Nodes {
+		attr := getAttributePtr(attrName, n)
+		if attr == nil {
+			n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val})
+		} else {
+			attr.Val = val
+		}
+	}
+
+	return s
+}
+
+// Text gets the combined text contents of each element in the set of matched
+// elements, including their descendants.
+func (s *Selection) Text() string {
+	var buf bytes.Buffer
+
+	// Slightly optimized vs calling Each: no single selection object created
+	for _, n := range s.Nodes {
+		buf.WriteString(getNodeText(n))
+	}
+	return buf.String()
+}
+
+// Size is an alias for Length.
+func (s *Selection) Size() int {
+	return s.Length()
+}
+
+// Length returns the number of elements in the Selection object.
+func (s *Selection) Length() int {
+	return len(s.Nodes)
+}
+
+// Html gets the HTML contents of the first element in the set of matched
+// elements. It includes text and comment nodes.
+func (s *Selection) Html() (ret string, e error) {
+	// Since there is no .innerHtml, the HTML content must be re-created from
+	// the nodes using html.Render.
+	var buf bytes.Buffer
+
+	if len(s.Nodes) > 0 {
+		for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling {
+			e = html.Render(&buf, c)
+			if e != nil {
+				return
+			}
+		}
+		ret = buf.String()
+	}
+
+	return
+}
+
+func (s *Selection) OuterHtml() (ret string, e error) {
+	// Since there is no .innerHtml, the HTML content must be re-created from
+	// the nodes using html.Render.
+	var buf bytes.Buffer
+	if len(s.Nodes) > 0 {
+		e = html.Render(&buf, s.Nodes[0])
+		if e != nil {
+			return
+		}
+		ret = buf.String()
+	}
+	return
+}
+
+// AddClass adds the given class(es) to each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+func (s *Selection) AddClass(class ...string) *Selection {
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+
+	if classStr == "" {
+		return s
+	}
+
+	tcls := getClassesSlice(classStr)
+	for _, n := range s.Nodes {
+		curClasses, attr := getClassesAndAttr(n, true)
+		for _, newClass := range tcls {
+			if strings.Index(curClasses, " "+newClass+" ") == -1 {
+				curClasses += newClass + " "
+			}
+		}
+
+		setClasses(n, attr, curClasses)
+	}
+
+	return s
+}
+
+// HasClass determines whether any of the matched elements are assigned the
+// given class.
+func (s *Selection) HasClass(class string) bool {
+	class = " " + class + " "
+	for _, n := range s.Nodes {
+		classes, _ := getClassesAndAttr(n, false)
+		if strings.Index(classes, class) > -1 {
+			return true
+		}
+	}
+	return false
+}
+
+// RemoveClass removes the given class(es) from each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+// If no class name is provided, all classes are removed.
+func (s *Selection) RemoveClass(class ...string) *Selection {
+	var rclasses []string
+
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+	remove := classStr == ""
+
+	if !remove {
+		rclasses = getClassesSlice(classStr)
+	}
+
+	for _, n := range s.Nodes {
+		if remove {
+			removeAttr(n, "class")
+		} else {
+			classes, attr := getClassesAndAttr(n, true)
+			for _, rcl := range rclasses {
+				classes = strings.Replace(classes, " "+rcl+" ", " ", -1)
+			}
+
+			setClasses(n, attr, classes)
+		}
+	}
+
+	return s
+}
+
+// ToggleClass adds or removes the given class(es) for each element in the set of matched elements.
+// Multiple class names can be specified, separated by a space or via multiple arguments.
+func (s *Selection) ToggleClass(class ...string) *Selection {
+	classStr := strings.TrimSpace(strings.Join(class, " "))
+
+	if classStr == "" {
+		return s
+	}
+
+	tcls := getClassesSlice(classStr)
+
+	for _, n := range s.Nodes {
+		classes, attr := getClassesAndAttr(n, true)
+		for _, tcl := range tcls {
+			if strings.Index(classes, " "+tcl+" ") != -1 {
+				classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
+			} else {
+				classes += tcl + " "
+			}
+		}
+
+		setClasses(n, attr, classes)
+	}
+
+	return s
+}
+
+// Get the specified node's text content.
+func getNodeText(node *html.Node) string {
+	if node.Type == html.TextNode {
+		// Keep newlines and spaces, like jQuery
+		return node.Data
+	} else if node.FirstChild != nil {
+		var buf bytes.Buffer
+		for c := node.FirstChild; c != nil; c = c.NextSibling {
+			buf.WriteString(getNodeText(c))
+		}
+		return buf.String()
+	}
+
+	return ""
+}
+
+func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
+	if n == nil {
+		return nil
+	}
+
+	for i, a := range n.Attr {
+		if a.Key == attrName {
+			return &n.Attr[i]
+		}
+	}
+	return nil
+}
+
+// Private function to get the specified attribute's value from a node.
+func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) {
+	if a := getAttributePtr(attrName, n); a != nil {
+		val = a.Val
+		exists = true
+	}
+	return
+}
+
+// Get and normalize the "class" attribute from the node.
+func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) {
+	// Applies only to element nodes
+	if n.Type == html.ElementNode {
+		attr = getAttributePtr("class", n)
+		if attr == nil && create {
+			n.Attr = append(n.Attr, html.Attribute{
+				Key: "class",
+				Val: "",
+			})
+			attr = &n.Attr[len(n.Attr)-1]
+		}
+	}
+
+	if attr == nil {
+		classes = " "
+	} else {
+		classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ")
+	}
+
+	return
+}
+
+func getClassesSlice(classes string) []string {
+	return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ")
+}
+
+func removeAttr(n *html.Node, attrName string) {
+	for i, a := range n.Attr {
+		if a.Key == attrName {
+			n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr =
+				n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1]
+			return
+		}
+	}
+}
+
+func setClasses(n *html.Node, attr *html.Attribute, classes string) {
+	classes = strings.TrimSpace(classes)
+	if classes == "" {
+		removeAttr(n, "class")
+		return
+	}
+
+	attr.Val = classes
+}

+ 252 - 0
src/github.com/PuerkitoBio/goquery/property_test.go

@@ -0,0 +1,252 @@
+package goquery
+
+import (
+	"regexp"
+	"strings"
+	"testing"
+)
+
+func TestAttrExists(t *testing.T) {
+	if val, ok := Doc().Find("a").Attr("href"); !ok {
+		t.Error("Expected a value for the href attribute.")
+	} else {
+		t.Logf("Href of first anchor: %v.", val)
+	}
+}
+
+func TestAttrOr(t *testing.T) {
+	if val := Doc().Find("a").AttrOr("fake-attribute", "alternative"); val != "alternative" {
+		t.Error("Expected an alternative value for 'fake-attribute' attribute.")
+	} else {
+		t.Logf("Value returned for not existing attribute: %v.", val)
+	}
+	if val := Doc().Find("zz").AttrOr("fake-attribute", "alternative"); val != "alternative" {
+		t.Error("Expected an alternative value for 'fake-attribute' on an empty selection.")
+	} else {
+		t.Logf("Value returned for empty selection: %v.", val)
+	}
+}
+
+func TestAttrNotExist(t *testing.T) {
+	if val, ok := Doc().Find("div.row-fluid").Attr("href"); ok {
+		t.Errorf("Expected no value for the href attribute, got %v.", val)
+	}
+}
+
+func TestRemoveAttr(t *testing.T) {
+	sel := Doc2Clone().Find("div")
+
+	sel.RemoveAttr("id")
+
+	_, ok := sel.Attr("id")
+	if ok {
+		t.Error("Expected there to be no id attributes set")
+	}
+}
+
+func TestSetAttr(t *testing.T) {
+	sel := Doc2Clone().Find("#main")
+
+	sel.SetAttr("id", "not-main")
+
+	val, ok := sel.Attr("id")
+	if !ok {
+		t.Error("Expected an id attribute on main")
+	}
+
+	if val != "not-main" {
+		t.Errorf("Expected an attribute id to be not-main, got %s", val)
+	}
+}
+
+func TestSetAttr2(t *testing.T) {
+	sel := Doc2Clone().Find("#main")
+
+	sel.SetAttr("foo", "bar")
+
+	val, ok := sel.Attr("foo")
+	if !ok {
+		t.Error("Expected an 'foo' attribute on main")
+	}
+
+	if val != "bar" {
+		t.Errorf("Expected an attribute 'foo' to be 'bar', got '%s'", val)
+	}
+}
+
+func TestText(t *testing.T) {
+	txt := Doc().Find("h1").Text()
+	if strings.Trim(txt, " \n\r\t") != "Provok.in" {
+		t.Errorf("Expected text to be Provok.in, found %s.", txt)
+	}
+}
+
+func TestText2(t *testing.T) {
+	txt := Doc().Find(".hero-unit .container-fluid .row-fluid:nth-child(1)").Text()
+	if ok, e := regexp.MatchString(`^\s+Provok\.in\s+Prove your point.\s+$`, txt); !ok || e != nil {
+		t.Errorf("Expected text to be Provok.in Prove your point., found %s.", txt)
+		if e != nil {
+			t.Logf("Error: %s.", e.Error())
+		}
+	}
+}
+
+func TestText3(t *testing.T) {
+	txt := Doc().Find(".pvk-gutter").First().Text()
+	// There's an &nbsp; character in there...
+	if ok, e := regexp.MatchString(`^[\s\x{00A0}]+$`, txt); !ok || e != nil {
+		t.Errorf("Expected spaces, found <%v>.", txt)
+		if e != nil {
+			t.Logf("Error: %s.", e.Error())
+		}
+	}
+}
+
+func TestHtml(t *testing.T) {
+	txt, e := Doc().Find("h1").Html()
+	if e != nil {
+		t.Errorf("Error: %s.", e)
+	}
+
+	if ok, e := regexp.MatchString(`^\s*<a href="/">Provok<span class="green">\.</span><span class="red">i</span>n</a>\s*$`, txt); !ok || e != nil {
+		t.Errorf("Unexpected HTML content, found %s.", txt)
+		if e != nil {
+			t.Logf("Error: %s.", e.Error())
+		}
+	}
+}
+
+func TestNbsp(t *testing.T) {
+	src := `<p>Some&nbsp;text</p>`
+	d, err := NewDocumentFromReader(strings.NewReader(src))
+	if err != nil {
+		t.Fatal(err)
+	}
+	txt := d.Find("p").Text()
+	ix := strings.Index(txt, "\u00a0")
+	if ix != 4 {
+		t.Errorf("Text: expected a non-breaking space at index 4, got %d", ix)
+	}
+
+	h, err := d.Find("p").Html()
+	if err != nil {
+		t.Fatal(err)
+	}
+	ix = strings.Index(h, "\u00a0")
+	if ix != 4 {
+		t.Errorf("Html: expected a non-breaking space at index 4, got %d", ix)
+	}
+}
+
+func TestAddClass(t *testing.T) {
+	sel := Doc2Clone().Find("#main")
+	sel.AddClass("main main main")
+
+	// Make sure that class was only added once
+	if a, ok := sel.Attr("class"); !ok || a != "main" {
+		t.Error("Expected #main to have class main")
+	}
+}
+
+func TestAddClassSimilar(t *testing.T) {
+	sel := Doc2Clone().Find("#nf5")
+	sel.AddClass("odd")
+
+	assertClass(t, sel, "odd")
+	assertClass(t, sel, "odder")
+	printSel(t, sel.Parent())
+}
+
+func TestAddEmptyClass(t *testing.T) {
+	sel := Doc2Clone().Find("#main")
+	sel.AddClass("")
+
+	// Make sure that class was only added once
+	if a, ok := sel.Attr("class"); ok {
+		t.Errorf("Expected #main to not to have a class, have: %s", a)
+	}
+}
+
+func TestAddClasses(t *testing.T) {
+	sel := Doc2Clone().Find("#main")
+	sel.AddClass("a b")
+
+	// Make sure that class was only added once
+	if !sel.HasClass("a") || !sel.HasClass("b") {
+		t.Errorf("#main does not have classes")
+	}
+}
+
+func TestHasClass(t *testing.T) {
+	sel := Doc().Find("div")
+	if !sel.HasClass("span12") {
+		t.Error("Expected at least one div to have class span12.")
+	}
+}
+
+func TestHasClassNone(t *testing.T) {
+	sel := Doc().Find("h2")
+	if sel.HasClass("toto") {
+		t.Error("Expected h1 to have no class.")
+	}
+}
+
+func TestHasClassNotFirst(t *testing.T) {
+	sel := Doc().Find(".alert")
+	if !sel.HasClass("alert-error") {
+		t.Error("Expected .alert to also have class .alert-error.")
+	}
+}
+
+func TestRemoveClass(t *testing.T) {
+	sel := Doc2Clone().Find("#nf1")
+	sel.RemoveClass("one row")
+
+	if !sel.HasClass("even") || sel.HasClass("one") || sel.HasClass("row") {
+		classes, _ := sel.Attr("class")
+		t.Error("Expected #nf1 to have class even, has ", classes)
+	}
+}
+
+func TestRemoveClassSimilar(t *testing.T) {
+	sel := Doc2Clone().Find("#nf5, #nf6")
+	assertLength(t, sel.Nodes, 2)
+
+	sel.RemoveClass("odd")
+	assertClass(t, sel.Eq(0), "odder")
+	printSel(t, sel)
+}
+
+func TestRemoveAllClasses(t *testing.T) {
+	sel := Doc2Clone().Find("#nf1")
+	sel.RemoveClass()
+
+	if a, ok := sel.Attr("class"); ok {
+		t.Error("All classes were not removed, has ", a)
+	}
+
+	sel = Doc2Clone().Find("#main")
+	sel.RemoveClass()
+	if a, ok := sel.Attr("class"); ok {
+		t.Error("All classes were not removed, has ", a)
+	}
+}
+
+func TestToggleClass(t *testing.T) {
+	sel := Doc2Clone().Find("#nf1")
+
+	sel.ToggleClass("one")
+	if sel.HasClass("one") {
+		t.Error("Expected #nf1 to not have class one")
+	}
+
+	sel.ToggleClass("one")
+	if !sel.HasClass("one") {
+		t.Error("Expected #nf1 to have class one")
+	}
+
+	sel.ToggleClass("one even row")
+	if a, ok := sel.Attr("class"); ok {
+		t.Errorf("Expected #nf1 to have no classes, have %q", a)
+	}
+}

+ 56 - 0
src/github.com/PuerkitoBio/goquery/query.go

@@ -0,0 +1,56 @@
+package goquery
+
+import (
+	"github.com/andybalholm/cascadia"
+	"golang.org/x/net/html"
+)
+
+// Is checks the current matched set of elements against a selector and
+// returns true if at least one of these elements matches.
+func (s *Selection) Is(selector string) bool {
+	if len(s.Nodes) > 0 {
+		return s.IsMatcher(cascadia.MustCompile(selector))
+	}
+
+	return false
+}
+
+// IsMatcher checks the current matched set of elements against a matcher and
+// returns true if at least one of these elements matches.
+func (s *Selection) IsMatcher(m Matcher) bool {
+	if len(s.Nodes) > 0 {
+		if len(s.Nodes) == 1 {
+			return m.Match(s.Nodes[0])
+		}
+		return len(m.Filter(s.Nodes)) > 0
+	}
+
+	return false
+}
+
+// IsFunction checks the current matched set of elements against a predicate and
+// returns true if at least one of these elements matches.
+func (s *Selection) IsFunction(f func(int, *Selection) bool) bool {
+	return s.FilterFunction(f).Length() > 0
+}
+
+// IsSelection checks the current matched set of elements against a Selection object
+// and returns true if at least one of these elements matches.
+func (s *Selection) IsSelection(sel *Selection) bool {
+	return s.FilterSelection(sel).Length() > 0
+}
+
+// IsNodes checks the current matched set of elements against the specified nodes
+// and returns true if at least one of these elements matches.
+func (s *Selection) IsNodes(nodes ...*html.Node) bool {
+	return s.FilterNodes(nodes...).Length() > 0
+}
+
+// Contains returns true if the specified Node is within,
+// at any depth, one of the nodes in the Selection object.
+// It is NOT inclusive, to behave like jQuery's implementation, and
+// unlike Javascript's .contains, so if the contained
+// node is itself in the selection, it returns false.
+func (s *Selection) Contains(n *html.Node) bool {
+	return sliceContains(s.Nodes, n)
+}

+ 96 - 0
src/github.com/PuerkitoBio/goquery/query_test.go

@@ -0,0 +1,96 @@
+package goquery
+
+import (
+	"testing"
+)
+
+func TestIs(t *testing.T) {
+	sel := Doc().Find(".footer p:nth-child(1)")
+	if !sel.Is("p") {
+		t.Error("Expected .footer p:nth-child(1) to be p.")
+	}
+}
+
+func TestIsPositional(t *testing.T) {
+	sel := Doc().Find(".footer p:nth-child(2)")
+	if !sel.Is("p:nth-child(2)") {
+		t.Error("Expected .footer p:nth-child(2) to be p:nth-child(2).")
+	}
+}
+
+func TestIsPositionalNot(t *testing.T) {
+	sel := Doc().Find(".footer p:nth-child(1)")
+	if sel.Is("p:nth-child(2)") {
+		t.Error("Expected .footer p:nth-child(1) NOT to be p:nth-child(2).")
+	}
+}
+
+func TestIsFunction(t *testing.T) {
+	ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool {
+		return s.HasClass("container-fluid")
+	})
+
+	if !ok {
+		t.Error("Expected some div to have a container-fluid class.")
+	}
+}
+
+func TestIsFunctionRollback(t *testing.T) {
+	ok := Doc().Find("div").IsFunction(func(i int, s *Selection) bool {
+		return s.HasClass("container-fluid")
+	})
+
+	if !ok {
+		t.Error("Expected some div to have a container-fluid class.")
+	}
+}
+
+func TestIsSelection(t *testing.T) {
+	sel := Doc().Find("div")
+	sel2 := Doc().Find(".pvk-gutter")
+
+	if !sel.IsSelection(sel2) {
+		t.Error("Expected some div to have a pvk-gutter class.")
+	}
+}
+
+func TestIsSelectionNot(t *testing.T) {
+	sel := Doc().Find("div")
+	sel2 := Doc().Find("a")
+
+	if sel.IsSelection(sel2) {
+		t.Error("Expected some div NOT to be an anchor.")
+	}
+}
+
+func TestIsNodes(t *testing.T) {
+	sel := Doc().Find("div")
+	sel2 := Doc().Find(".footer")
+
+	if !sel.IsNodes(sel2.Nodes[0]) {
+		t.Error("Expected some div to have a footer class.")
+	}
+}
+
+func TestDocContains(t *testing.T) {
+	sel := Doc().Find("h1")
+	if !Doc().Contains(sel.Nodes[0]) {
+		t.Error("Expected document to contain H1 tag.")
+	}
+}
+
+func TestSelContains(t *testing.T) {
+	sel := Doc().Find(".row-fluid")
+	sel2 := Doc().Find("a[ng-click]")
+	if !sel.Contains(sel2.Nodes[0]) {
+		t.Error("Expected .row-fluid to contain a[ng-click] tag.")
+	}
+}
+
+func TestSelNotContains(t *testing.T) {
+	sel := Doc().Find("a.link")
+	sel2 := Doc().Find("span")
+	if sel.Contains(sel2.Nodes[0]) {
+		t.Error("Expected a.link to NOT contain span tag.")
+	}
+}

+ 696 - 0
src/github.com/PuerkitoBio/goquery/traversal.go

@@ -0,0 +1,696 @@
+package goquery
+
+import (
+	"github.com/andybalholm/cascadia"
+	"golang.org/x/net/html"
+)
+
+type siblingType int
+
+// Sibling type, used internally when iterating over children at the same
+// level (siblings) to specify which nodes are requested.
+const (
+	siblingPrevUntil siblingType = iota - 3
+	siblingPrevAll
+	siblingPrev
+	siblingAll
+	siblingNext
+	siblingNextAll
+	siblingNextUntil
+	siblingAllIncludingNonElements
+)
+
+// Find gets the descendants of each element in the current set of matched
+// elements, filtered by a selector. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) Find(selector string) *Selection {
+	return pushStack(s, findWithMatcher(s.Nodes, cascadia.MustCompile(selector)))
+}
+
+// FindMatcher gets the descendants of each element in the current set of matched
+// elements, filtered by the matcher. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindMatcher(m Matcher) *Selection {
+	return pushStack(s, findWithMatcher(s.Nodes, m))
+}
+
+// FindSelection gets the descendants of each element in the current
+// Selection, filtered by a Selection. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, nil)
+	}
+	return s.FindNodes(sel.Nodes...)
+}
+
+// FindNodes gets the descendants of each element in the current
+// Selection, filtered by some nodes. It returns a new Selection object
+// containing these matched elements.
+func (s *Selection) FindNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		if sliceContains(s.Nodes, n) {
+			return []*html.Node{n}
+		}
+		return nil
+	}))
+}
+
+// Contents gets the children of each element in the Selection,
+// including text and comment nodes. It returns a new Selection object
+// containing these elements.
+func (s *Selection) Contents() *Selection {
+	return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements))
+}
+
+// ContentsFiltered gets the children of each element in the Selection,
+// filtered by the specified selector. It returns a new Selection
+// object containing these elements. Since selectors only act on Element nodes,
+// this function is an alias to ChildrenFiltered unless the selector is empty,
+// in which case it is an alias to Contents.
+func (s *Selection) ContentsFiltered(selector string) *Selection {
+	if selector != "" {
+		return s.ChildrenFiltered(selector)
+	}
+	return s.Contents()
+}
+
+// ContentsMatcher gets the children of each element in the Selection,
+// filtered by the specified matcher. It returns a new Selection
+// object containing these elements. Since matchers only act on Element nodes,
+// this function is an alias to ChildrenMatcher.
+func (s *Selection) ContentsMatcher(m Matcher) *Selection {
+	return s.ChildrenMatcher(m)
+}
+
+// Children gets the child elements of each element in the Selection.
+// It returns a new Selection object containing these elements.
+func (s *Selection) Children() *Selection {
+	return pushStack(s, getChildrenNodes(s.Nodes, siblingAll))
+}
+
+// ChildrenFiltered gets the child elements of each element in the Selection,
+// filtered by the specified selector. It returns a new
+// Selection object containing these elements.
+func (s *Selection) ChildrenFiltered(selector string) *Selection {
+	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), cascadia.MustCompile(selector))
+}
+
+// ChildrenMatcher gets the child elements of each element in the Selection,
+// filtered by the specified matcher. It returns a new
+// Selection object containing these elements.
+func (s *Selection) ChildrenMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m)
+}
+
+// Parent gets the parent of each element in the Selection. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) Parent() *Selection {
+	return pushStack(s, getParentNodes(s.Nodes))
+}
+
+// ParentFiltered gets the parent of each element in the Selection filtered by a
+// selector. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentFiltered(selector string) *Selection {
+	return filterAndPush(s, getParentNodes(s.Nodes), cascadia.MustCompile(selector))
+}
+
+// ParentMatcher gets the parent of each element in the Selection filtered by a
+// matcher. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getParentNodes(s.Nodes), m)
+}
+
+// Closest gets the first element that matches the selector by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) Closest(selector string) *Selection {
+	cs := cascadia.MustCompile(selector)
+	return s.ClosestMatcher(cs)
+}
+
+// ClosestMatcher gets the first element that matches the matcher by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) ClosestMatcher(m Matcher) *Selection {
+	return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
+		// For each node in the selection, test the node itself, then each parent
+		// until a match is found.
+		for ; n != nil; n = n.Parent {
+			if m.Match(n) {
+				return []*html.Node{n}
+			}
+		}
+		return nil
+	}))
+}
+
+// ClosestNodes gets the first element that matches one of the nodes by testing the
+// element itself and traversing up through its ancestors in the DOM tree.
+func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
+		// For each node in the selection, test the node itself, then each parent
+		// until a match is found.
+		for ; n != nil; n = n.Parent {
+			if isInSlice(nodes, n) {
+				return []*html.Node{n}
+			}
+		}
+		return nil
+	}))
+}
+
+// ClosestSelection gets the first element that matches one of the nodes in the
+// Selection by testing the element itself and traversing up through its ancestors
+// in the DOM tree.
+func (s *Selection) ClosestSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return pushStack(s, nil)
+	}
+	return s.ClosestNodes(sel.Nodes...)
+}
+
+// Parents gets the ancestors of each element in the current Selection. It
+// returns a new Selection object with the matched elements.
+func (s *Selection) Parents() *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, nil, nil))
+}
+
+// ParentsFiltered gets the ancestors of each element in the current
+// Selection. It returns a new Selection object with the matched elements.
+func (s *Selection) ParentsFiltered(selector string) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), cascadia.MustCompile(selector))
+}
+
+// ParentsMatcher gets the ancestors of each element in the current
+// Selection. It returns a new Selection object with the matched elements.
+func (s *Selection) ParentsMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m)
+}
+
+// ParentsUntil gets the ancestors of each element in the Selection, up to but
+// not including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsUntil(selector string) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, cascadia.MustCompile(selector), nil))
+}
+
+// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
+// not including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, m, nil))
+}
+
+// ParentsUntilSelection gets the ancestors of each element in the Selection,
+// up to but not including the elements in the specified Selection. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.Parents()
+	}
+	return s.ParentsUntilNodes(sel.Nodes...)
+}
+
+// ParentsUntilNodes gets the ancestors of each element in the Selection,
+// up to but not including the specified nodes. It returns a
+// new Selection object containing the matched elements.
+func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getParentsNodes(s.Nodes, nil, nodes))
+}
+
+// ParentsFilteredUntil is like ParentsUntil, with the option to filter the
+// results based on a selector string. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+}
+
+// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
+// results based on a matcher. It returns a new Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter)
+}
+
+// ParentsFilteredUntilSelection is like ParentsUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.ParentsMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+}
+
+// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.ParentsMatcher(filter)
+	}
+	return s.ParentsMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// ParentsFilteredUntilNodes is like ParentsUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), cascadia.MustCompile(filterSelector))
+}
+
+// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter)
+}
+
+// Siblings gets the siblings of each element in the Selection. It returns
+// a new Selection object containing the matched elements.
+func (s *Selection) Siblings() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil))
+}
+
+// SiblingsFiltered gets the siblings of each element in the Selection
+// filtered by a selector. It returns a new Selection object containing the
+// matched elements.
+func (s *Selection) SiblingsFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), cascadia.MustCompile(selector))
+}
+
+// SiblingsMatcher gets the siblings of each element in the Selection
+// filtered by a matcher. It returns a new Selection object containing the
+// matched elements.
+func (s *Selection) SiblingsMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m)
+}
+
+// Next gets the immediately following sibling of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) Next() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil))
+}
+
+// NextFiltered gets the immediately following sibling of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), cascadia.MustCompile(selector))
+}
+
+// NextMatcher gets the immediately following sibling of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m)
+}
+
+// NextAll gets all the following siblings of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) NextAll() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil))
+}
+
+// NextAllFiltered gets all the following siblings of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextAllFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), cascadia.MustCompile(selector))
+}
+
+// NextAllMatcher gets all the following siblings of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) NextAllMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m)
+}
+
+// Prev gets the immediately preceding sibling of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) Prev() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil))
+}
+
+// PrevFiltered gets the immediately preceding sibling of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), cascadia.MustCompile(selector))
+}
+
+// PrevMatcher gets the immediately preceding sibling of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m)
+}
+
+// PrevAll gets all the preceding siblings of each element in the
+// Selection. It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevAll() *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil))
+}
+
+// PrevAllFiltered gets all the preceding siblings of each element in the
+// Selection filtered by a selector. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevAllFiltered(selector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), cascadia.MustCompile(selector))
+}
+
+// PrevAllMatcher gets all the preceding siblings of each element in the
+// Selection filtered by a matcher. It returns a new Selection object
+// containing the matched elements.
+func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m)
+}
+
+// NextUntil gets all following siblings of each element up to but not
+// including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntil(selector string) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		cascadia.MustCompile(selector), nil))
+}
+
+// NextUntilMatcher gets all following siblings of each element up to but not
+// including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		m, nil))
+}
+
+// NextUntilSelection gets all following siblings of each element up to but not
+// including the element matched by the Selection. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.NextAll()
+	}
+	return s.NextUntilNodes(sel.Nodes...)
+}
+
+// NextUntilNodes gets all following siblings of each element up to but not
+// including the element matched by the nodes. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes))
+}
+
+// PrevUntil gets all preceding siblings of each element up to but not
+// including the element matched by the selector. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntil(selector string) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		cascadia.MustCompile(selector), nil))
+}
+
+// PrevUntilMatcher gets all preceding siblings of each element up to but not
+// including the element matched by the matcher. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilMatcher(m Matcher) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		m, nil))
+}
+
+// PrevUntilSelection gets all preceding siblings of each element up to but not
+// including the element matched by the Selection. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilSelection(sel *Selection) *Selection {
+	if sel == nil {
+		return s.PrevAll()
+	}
+	return s.PrevUntilNodes(sel.Nodes...)
+}
+
+// PrevUntilNodes gets all preceding siblings of each element up to but not
+// including the element matched by the nodes. It returns a new Selection
+// object containing the matched elements.
+func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
+	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes))
+}
+
+// NextFilteredUntil is like NextUntil, with the option to filter
+// the results based on a selector string.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+}
+
+// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
+// the results based on a matcher.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		until, nil), filter)
+}
+
+// NextFilteredUntilSelection is like NextUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.NextMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+}
+
+// NextMatcherUntilSelection is like NextUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.NextMatcher(filter)
+	}
+	return s.NextMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// NextFilteredUntilNodes is like NextUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes), cascadia.MustCompile(filterSelector))
+}
+
+// NextMatcherUntilNodes is like NextUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
+		nil, nodes), filter)
+}
+
+// PrevFilteredUntil is like PrevUntil, with the option to filter
+// the results based on a selector string.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+}
+
+// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
+// the results based on a matcher.
+// It returns a new Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		until, nil), filter)
+}
+
+// PrevFilteredUntilSelection is like PrevUntilSelection, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
+	return s.PrevMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+}
+
+// PrevMatcherUntilSelection is like PrevUntilSelection, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection {
+	if sel == nil {
+		return s.PrevMatcher(filter)
+	}
+	return s.PrevMatcherUntilNodes(filter, sel.Nodes...)
+}
+
+// PrevFilteredUntilNodes is like PrevUntilNodes, with the
+// option to filter the results based on a selector string. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes), cascadia.MustCompile(filterSelector))
+}
+
+// PrevMatcherUntilNodes is like PrevUntilNodes, with the
+// option to filter the results based on a matcher. It returns a new
+// Selection object containing the matched elements.
+func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection {
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
+		nil, nodes), filter)
+}
+
+// Filter and push filters the nodes based on a matcher, and pushes the results
+// on the stack, with the srcSel as previous selection.
+func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection {
+	// Create a temporary Selection with the specified nodes to filter using winnow
+	sel := &Selection{nodes, srcSel.document, nil}
+	// Filter based on matcher and push on stack
+	return pushStack(srcSel, winnow(sel, m, true))
+}
+
+// Internal implementation of Find that return raw nodes.
+func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node {
+	// Map nodes to find the matches within the children of each node
+	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
+		// Go down one level, becausejQuery's Find selects only within descendants
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			if c.Type == html.ElementNode {
+				result = append(result, m.MatchAll(c)...)
+			}
+		}
+		return
+	})
+}
+
+// Internal implementation to get all parent nodes, stopping at the specified
+// node (or nil if no stop).
+func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) {
+		for p := n.Parent; p != nil; p = p.Parent {
+			sel := newSingleSelection(p, nil)
+			if stopm != nil {
+				if sel.IsMatcher(stopm) {
+					break
+				}
+			} else if len(stopNodes) > 0 {
+				if sel.IsNodes(stopNodes...) {
+					break
+				}
+			}
+			if p.Type == html.ElementNode {
+				result = append(result, p)
+			}
+		}
+		return
+	})
+}
+
+// Internal implementation of sibling nodes that return a raw slice of matches.
+func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node {
+	var f func(*html.Node) bool
+
+	// If the requested siblings are ...Until, create the test function to
+	// determine if the until condition is reached (returns true if it is)
+	if st == siblingNextUntil || st == siblingPrevUntil {
+		f = func(n *html.Node) bool {
+			if untilm != nil {
+				// Matcher-based condition
+				sel := newSingleSelection(n, nil)
+				return sel.IsMatcher(untilm)
+			} else if len(untilNodes) > 0 {
+				// Nodes-based condition
+				sel := newSingleSelection(n, nil)
+				return sel.IsNodes(untilNodes...)
+			}
+			return false
+		}
+	}
+
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		return getChildrenWithSiblingType(n.Parent, st, n, f)
+	})
+}
+
+// Gets the children nodes of each node in the specified slice of nodes,
+// based on the sibling type request.
+func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		return getChildrenWithSiblingType(n, st, nil, nil)
+	})
+}
+
+// Gets the children of the specified parent, based on the requested sibling
+// type, skipping a specified node if required.
+func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node,
+	untilFunc func(*html.Node) bool) (result []*html.Node) {
+
+	// Create the iterator function
+	var iter = func(cur *html.Node) (ret *html.Node) {
+		// Based on the sibling type requested, iterate the right way
+		for {
+			switch st {
+			case siblingAll, siblingAllIncludingNonElements:
+				if cur == nil {
+					// First iteration, start with first child of parent
+					// Skip node if required
+					if ret = parent.FirstChild; ret == skipNode && skipNode != nil {
+						ret = skipNode.NextSibling
+					}
+				} else {
+					// Skip node if required
+					if ret = cur.NextSibling; ret == skipNode && skipNode != nil {
+						ret = skipNode.NextSibling
+					}
+				}
+			case siblingPrev, siblingPrevAll, siblingPrevUntil:
+				if cur == nil {
+					// Start with previous sibling of the skip node
+					ret = skipNode.PrevSibling
+				} else {
+					ret = cur.PrevSibling
+				}
+			case siblingNext, siblingNextAll, siblingNextUntil:
+				if cur == nil {
+					// Start with next sibling of the skip node
+					ret = skipNode.NextSibling
+				} else {
+					ret = cur.NextSibling
+				}
+			default:
+				panic("Invalid sibling type.")
+			}
+			if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements {
+				return
+			}
+			// Not a valid node, try again from this one
+			cur = ret
+		}
+	}
+
+	for c := iter(nil); c != nil; c = iter(c) {
+		// If this is an ...Until case, test before append (returns true
+		// if the until condition is reached)
+		if st == siblingNextUntil || st == siblingPrevUntil {
+			if untilFunc(c) {
+				return
+			}
+		}
+		result = append(result, c)
+		if st == siblingNext || st == siblingPrev {
+			// Only one node was requested (immediate next or previous), so exit
+			return
+		}
+	}
+	return
+}
+
+// Internal implementation of parent nodes that return a raw slice of Nodes.
+func getParentNodes(nodes []*html.Node) []*html.Node {
+	return mapNodes(nodes, func(i int, n *html.Node) []*html.Node {
+		if n.Parent != nil && n.Parent.Type == html.ElementNode {
+			return []*html.Node{n.Parent}
+		}
+		return nil
+	})
+}
+
+// Internal map function used by many traversing methods. Takes the source nodes
+// to iterate on and the mapping function that returns an array of nodes.
+// Returns an array of nodes mapped by calling the callback function once for
+// each node in the source nodes.
+func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
+	for i, n := range nodes {
+		if vals := f(i, n); len(vals) > 0 {
+			result = appendWithoutDuplicates(result, vals)
+		}
+	}
+	return
+}

+ 697 - 0
src/github.com/PuerkitoBio/goquery/traversal_test.go

@@ -0,0 +1,697 @@
+package goquery
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestFind(t *testing.T) {
+	sel := Doc().Find("div.row-fluid")
+	assertLength(t, sel.Nodes, 9)
+}
+
+func TestFindRollback(t *testing.T) {
+	sel := Doc().Find("div.row-fluid")
+	sel2 := sel.Find("a").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestFindNotSelf(t *testing.T) {
+	sel := Doc().Find("h1").Find("h1")
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestFindInvalidSelector(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find(":+ ^")
+}
+
+func TestChainedFind(t *testing.T) {
+	sel := Doc().Find("div.hero-unit").Find(".row-fluid")
+	assertLength(t, sel.Nodes, 4)
+}
+
+func TestChildren(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Children()
+	assertLength(t, sel.Nodes, 5)
+}
+
+func TestChildrenRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Children().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestContents(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Contents()
+	assertLength(t, sel.Nodes, 13)
+}
+
+func TestContentsRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Contents().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestChildrenFiltered(t *testing.T) {
+	sel := Doc().Find(".pvk-content").ChildrenFiltered(".hero-unit")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestChildrenFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.ChildrenFiltered(".hero-unit").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestContentsFiltered(t *testing.T) {
+	sel := Doc().Find(".pvk-content").ContentsFiltered(".hero-unit")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestContentsFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.ContentsFiltered(".hero-unit").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestChildrenFilteredNone(t *testing.T) {
+	sel := Doc().Find(".pvk-content").ChildrenFiltered("a.btn")
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestParent(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Parent()
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestParentRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.Parent().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentBody(t *testing.T) {
+	sel := Doc().Find("body").Parent()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestParentFiltered(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentFiltered(".hero-unit")
+	assertLength(t, sel.Nodes, 1)
+	assertClass(t, sel, "hero-unit")
+}
+
+func TestParentFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ParentFiltered(".hero-unit").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParents(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Parents()
+	assertLength(t, sel.Nodes, 8)
+}
+
+func TestParentsOrder(t *testing.T) {
+	sel := Doc().Find("#cf2").Parents()
+	assertLength(t, sel.Nodes, 6)
+	assertSelectionIs(t, sel, ".hero-unit", ".pvk-content", "div.row-fluid", "#cf1", "body", "html")
+}
+
+func TestParentsRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.Parents().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsFiltered(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsFiltered("body")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestParentsFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ParentsFiltered("body").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsUntil(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsUntil("body")
+	assertLength(t, sel.Nodes, 6)
+}
+
+func TestParentsUntilRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ParentsUntil("body").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsUntilSelection(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".pvk-content")
+	sel = sel.ParentsUntilSelection(sel2)
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestParentsUntilSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".pvk-content")
+	sel2 = sel.ParentsUntilSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsUntilNodes(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".pvk-content, .hero-unit")
+	sel = sel.ParentsUntilNodes(sel2.Nodes...)
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestParentsUntilNodesRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".pvk-content, .hero-unit")
+	sel2 = sel.ParentsUntilNodes(sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsFilteredUntil(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsFilteredUntil(".pvk-content", "body")
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestParentsFilteredUntilRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ParentsFilteredUntil(".pvk-content", "body").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsFilteredUntilSelection(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".row-fluid")
+	sel = sel.ParentsFilteredUntilSelection("div", sel2)
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestParentsFilteredUntilSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".row-fluid")
+	sel2 = sel.ParentsFilteredUntilSelection("div", sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestParentsFilteredUntilNodes(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".row-fluid")
+	sel = sel.ParentsFilteredUntilNodes("body", sel2.Nodes...)
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestParentsFilteredUntilNodesRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := Doc().Find(".row-fluid")
+	sel2 = sel.ParentsFilteredUntilNodes("body", sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestSiblings(t *testing.T) {
+	sel := Doc().Find("h1").Siblings()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestSiblingsRollback(t *testing.T) {
+	sel := Doc().Find("h1")
+	sel2 := sel.Siblings().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestSiblings2(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").Siblings()
+	assertLength(t, sel.Nodes, 9)
+}
+
+func TestSiblings3(t *testing.T) {
+	sel := Doc().Find("body>.container-fluid").Siblings()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestSiblingsFiltered(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").SiblingsFiltered(".pvk-content")
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestSiblingsFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter")
+	sel2 := sel.SiblingsFiltered(".pvk-content").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNext(t *testing.T) {
+	sel := Doc().Find("h1").Next()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestNextRollback(t *testing.T) {
+	sel := Doc().Find("h1")
+	sel2 := sel.Next().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNext2(t *testing.T) {
+	sel := Doc().Find(".close").Next()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestNextNone(t *testing.T) {
+	sel := Doc().Find("small").Next()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestNextFiltered(t *testing.T) {
+	sel := Doc().Find(".container-fluid").NextFiltered("div")
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestNextFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.NextFiltered("div").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextFiltered2(t *testing.T) {
+	sel := Doc().Find(".container-fluid").NextFiltered("[ng-view]")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestPrev(t *testing.T) {
+	sel := Doc().Find(".red").Prev()
+	assertLength(t, sel.Nodes, 1)
+	assertClass(t, sel, "green")
+}
+
+func TestPrevRollback(t *testing.T) {
+	sel := Doc().Find(".red")
+	sel2 := sel.Prev().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrev2(t *testing.T) {
+	sel := Doc().Find(".row-fluid").Prev()
+	assertLength(t, sel.Nodes, 5)
+}
+
+func TestPrevNone(t *testing.T) {
+	sel := Doc().Find("h2").Prev()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestPrevFiltered(t *testing.T) {
+	sel := Doc().Find(".row-fluid").PrevFiltered(".row-fluid")
+	assertLength(t, sel.Nodes, 5)
+}
+
+func TestPrevFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".row-fluid")
+	sel2 := sel.PrevFiltered(".row-fluid").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextAll(t *testing.T) {
+	sel := Doc().Find("#cf2 div:nth-child(1)").NextAll()
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestNextAllRollback(t *testing.T) {
+	sel := Doc().Find("#cf2 div:nth-child(1)")
+	sel2 := sel.NextAll().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextAll2(t *testing.T) {
+	sel := Doc().Find("div[ng-cloak]").NextAll()
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestNextAllNone(t *testing.T) {
+	sel := Doc().Find(".footer").NextAll()
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestNextAllFiltered(t *testing.T) {
+	sel := Doc().Find("#cf2 .row-fluid").NextAllFiltered("[ng-cloak]")
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestNextAllFilteredRollback(t *testing.T) {
+	sel := Doc().Find("#cf2 .row-fluid")
+	sel2 := sel.NextAllFiltered("[ng-cloak]").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextAllFiltered2(t *testing.T) {
+	sel := Doc().Find(".close").NextAllFiltered("h4")
+	assertLength(t, sel.Nodes, 1)
+}
+
+func TestPrevAll(t *testing.T) {
+	sel := Doc().Find("[ng-view]").PrevAll()
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestPrevAllOrder(t *testing.T) {
+	sel := Doc().Find("[ng-view]").PrevAll()
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#cf4", "#cf3")
+}
+
+func TestPrevAllRollback(t *testing.T) {
+	sel := Doc().Find("[ng-view]")
+	sel2 := sel.PrevAll().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrevAll2(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").PrevAll()
+	assertLength(t, sel.Nodes, 6)
+}
+
+func TestPrevAllFiltered(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").PrevAllFiltered(".pvk-content")
+	assertLength(t, sel.Nodes, 3)
+}
+
+func TestPrevAllFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter")
+	sel2 := sel.PrevAllFiltered(".pvk-content").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextUntil(t *testing.T) {
+	sel := Doc().Find(".alert a").NextUntil("p")
+	assertLength(t, sel.Nodes, 1)
+	assertSelectionIs(t, sel, "h4")
+}
+
+func TestNextUntil2(t *testing.T) {
+	sel := Doc().Find("#cf2-1").NextUntil("[ng-cloak]")
+	assertLength(t, sel.Nodes, 1)
+	assertSelectionIs(t, sel, "#cf2-2")
+}
+
+func TestNextUntilOrder(t *testing.T) {
+	sel := Doc().Find("#cf2-1").NextUntil("#cf2-4")
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#cf2-2", "#cf2-3")
+}
+
+func TestNextUntilRollback(t *testing.T) {
+	sel := Doc().Find("#cf2-1")
+	sel2 := sel.PrevUntil("#cf2-4").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextUntilSelection(t *testing.T) {
+	sel := Doc2().Find("#n2")
+	sel2 := Doc2().Find("#n4")
+	sel2 = sel.NextUntilSelection(sel2)
+	assertLength(t, sel2.Nodes, 1)
+	assertSelectionIs(t, sel2, "#n3")
+}
+
+func TestNextUntilSelectionRollback(t *testing.T) {
+	sel := Doc2().Find("#n2")
+	sel2 := Doc2().Find("#n4")
+	sel2 = sel.NextUntilSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextUntilNodes(t *testing.T) {
+	sel := Doc2().Find("#n2")
+	sel2 := Doc2().Find("#n5")
+	sel2 = sel.NextUntilNodes(sel2.Nodes...)
+	assertLength(t, sel2.Nodes, 2)
+	assertSelectionIs(t, sel2, "#n3", "#n4")
+}
+
+func TestNextUntilNodesRollback(t *testing.T) {
+	sel := Doc2().Find("#n2")
+	sel2 := Doc2().Find("#n5")
+	sel2 = sel.NextUntilNodes(sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrevUntil(t *testing.T) {
+	sel := Doc().Find(".alert p").PrevUntil("a")
+	assertLength(t, sel.Nodes, 1)
+	assertSelectionIs(t, sel, "h4")
+}
+
+func TestPrevUntil2(t *testing.T) {
+	sel := Doc().Find("[ng-cloak]").PrevUntil(":not([ng-cloak])")
+	assertLength(t, sel.Nodes, 1)
+	assertSelectionIs(t, sel, "[ng-cloak]")
+}
+
+func TestPrevUntilOrder(t *testing.T) {
+	sel := Doc().Find("#cf2-4").PrevUntil("#cf2-1")
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#cf2-3", "#cf2-2")
+}
+
+func TestPrevUntilRollback(t *testing.T) {
+	sel := Doc().Find("#cf2-4")
+	sel2 := sel.PrevUntil("#cf2-1").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrevUntilSelection(t *testing.T) {
+	sel := Doc2().Find("#n4")
+	sel2 := Doc2().Find("#n2")
+	sel2 = sel.PrevUntilSelection(sel2)
+	assertLength(t, sel2.Nodes, 1)
+	assertSelectionIs(t, sel2, "#n3")
+}
+
+func TestPrevUntilSelectionRollback(t *testing.T) {
+	sel := Doc2().Find("#n4")
+	sel2 := Doc2().Find("#n2")
+	sel2 = sel.PrevUntilSelection(sel2).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrevUntilNodes(t *testing.T) {
+	sel := Doc2().Find("#n5")
+	sel2 := Doc2().Find("#n2")
+	sel2 = sel.PrevUntilNodes(sel2.Nodes...)
+	assertLength(t, sel2.Nodes, 2)
+	assertSelectionIs(t, sel2, "#n4", "#n3")
+}
+
+func TestPrevUntilNodesRollback(t *testing.T) {
+	sel := Doc2().Find("#n5")
+	sel2 := Doc2().Find("#n2")
+	sel2 = sel.PrevUntilNodes(sel2.Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextFilteredUntil(t *testing.T) {
+	sel := Doc2().Find(".two").NextFilteredUntil(".even", ".six")
+	assertLength(t, sel.Nodes, 4)
+	assertSelectionIs(t, sel, "#n3", "#n5", "#nf3", "#nf5")
+}
+
+func TestNextFilteredUntilRollback(t *testing.T) {
+	sel := Doc2().Find(".two")
+	sel2 := sel.NextFilteredUntil(".even", ".six").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestNextFilteredUntilSelection(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".five")
+	sel = sel.NextFilteredUntilSelection(".even", sel2)
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#n3", "#nf3")
+}
+
+func TestNextFilteredUntilSelectionRollback(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".five")
+	sel3 := sel.NextFilteredUntilSelection(".even", sel2).End()
+	assertEqual(t, sel, sel3)
+}
+
+func TestNextFilteredUntilNodes(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".four")
+	sel = sel.NextFilteredUntilNodes(".odd", sel2.Nodes...)
+	assertLength(t, sel.Nodes, 4)
+	assertSelectionIs(t, sel, "#n2", "#n6", "#nf2", "#nf6")
+}
+
+func TestNextFilteredUntilNodesRollback(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".four")
+	sel3 := sel.NextFilteredUntilNodes(".odd", sel2.Nodes...).End()
+	assertEqual(t, sel, sel3)
+}
+
+func TestPrevFilteredUntil(t *testing.T) {
+	sel := Doc2().Find(".five").PrevFilteredUntil(".odd", ".one")
+	assertLength(t, sel.Nodes, 4)
+	assertSelectionIs(t, sel, "#n4", "#n2", "#nf4", "#nf2")
+}
+
+func TestPrevFilteredUntilRollback(t *testing.T) {
+	sel := Doc2().Find(".four")
+	sel2 := sel.PrevFilteredUntil(".odd", ".one").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestPrevFilteredUntilSelection(t *testing.T) {
+	sel := Doc2().Find(".odd")
+	sel2 := Doc2().Find(".two")
+	sel = sel.PrevFilteredUntilSelection(".odd", sel2)
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#n4", "#nf4")
+}
+
+func TestPrevFilteredUntilSelectionRollback(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".five")
+	sel3 := sel.PrevFilteredUntilSelection(".even", sel2).End()
+	assertEqual(t, sel, sel3)
+}
+
+func TestPrevFilteredUntilNodes(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".four")
+	sel = sel.PrevFilteredUntilNodes(".odd", sel2.Nodes...)
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#n2", "#nf2")
+}
+
+func TestPrevFilteredUntilNodesRollback(t *testing.T) {
+	sel := Doc2().Find(".even")
+	sel2 := Doc2().Find(".four")
+	sel3 := sel.PrevFilteredUntilNodes(".odd", sel2.Nodes...).End()
+	assertEqual(t, sel, sel3)
+}
+
+func TestClosestItself(t *testing.T) {
+	sel := Doc2().Find(".three")
+	sel2 := sel.Closest(".row")
+	assertLength(t, sel2.Nodes, sel.Length())
+	assertSelectionIs(t, sel2, "#n3", "#nf3")
+}
+
+func TestClosestNoDupes(t *testing.T) {
+	sel := Doc().Find(".span12")
+	sel2 := sel.Closest(".pvk-content")
+	assertLength(t, sel2.Nodes, 1)
+	assertClass(t, sel2, "pvk-content")
+}
+
+func TestClosestNone(t *testing.T) {
+	sel := Doc().Find("h4")
+	sel2 := sel.Closest("a")
+	assertLength(t, sel2.Nodes, 0)
+}
+
+func TestClosestMany(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.Closest(".pvk-content")
+	assertLength(t, sel2.Nodes, 2)
+	assertSelectionIs(t, sel2, "#pc1", "#pc2")
+}
+
+func TestClosestRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.Closest(".pvk-content").End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestClosestSelectionItself(t *testing.T) {
+	sel := Doc2().Find(".three")
+	sel2 := sel.ClosestSelection(Doc2().Find(".row"))
+	assertLength(t, sel2.Nodes, sel.Length())
+}
+
+func TestClosestSelectionNoDupes(t *testing.T) {
+	sel := Doc().Find(".span12")
+	sel2 := sel.ClosestSelection(Doc().Find(".pvk-content"))
+	assertLength(t, sel2.Nodes, 1)
+	assertClass(t, sel2, "pvk-content")
+}
+
+func TestClosestSelectionNone(t *testing.T) {
+	sel := Doc().Find("h4")
+	sel2 := sel.ClosestSelection(Doc().Find("a"))
+	assertLength(t, sel2.Nodes, 0)
+}
+
+func TestClosestSelectionMany(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ClosestSelection(Doc().Find(".pvk-content"))
+	assertLength(t, sel2.Nodes, 2)
+	assertSelectionIs(t, sel2, "#pc1", "#pc2")
+}
+
+func TestClosestSelectionRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ClosestSelection(Doc().Find(".pvk-content")).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestClosestNodesItself(t *testing.T) {
+	sel := Doc2().Find(".three")
+	sel2 := sel.ClosestNodes(Doc2().Find(".row").Nodes...)
+	assertLength(t, sel2.Nodes, sel.Length())
+}
+
+func TestClosestNodesNoDupes(t *testing.T) {
+	sel := Doc().Find(".span12")
+	sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...)
+	assertLength(t, sel2.Nodes, 1)
+	assertClass(t, sel2, "pvk-content")
+}
+
+func TestClosestNodesNone(t *testing.T) {
+	sel := Doc().Find("h4")
+	sel2 := sel.ClosestNodes(Doc().Find("a").Nodes...)
+	assertLength(t, sel2.Nodes, 0)
+}
+
+func TestClosestNodesMany(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...)
+	assertLength(t, sel2.Nodes, 2)
+	assertSelectionIs(t, sel2, "#pc1", "#pc2")
+}
+
+func TestClosestNodesRollback(t *testing.T) {
+	sel := Doc().Find(".container-fluid")
+	sel2 := sel.ClosestNodes(Doc().Find(".pvk-content").Nodes...).End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestIssue26(t *testing.T) {
+	img1 := `<img src="assets/images/gallery/thumb-1.jpg" alt="150x150" />`
+	img2 := `<img alt="150x150" src="assets/images/gallery/thumb-1.jpg" />`
+	cases := []struct {
+		s string
+		l int
+	}{
+		{s: img1 + img2, l: 2},
+		{s: img1, l: 1},
+		{s: img2, l: 1},
+	}
+	for _, c := range cases {
+		doc, err := NewDocumentFromReader(strings.NewReader(c.s))
+		if err != nil {
+			t.Fatal(err)
+		}
+		sel := doc.Find("img[src]")
+		assertLength(t, sel.Nodes, c.l)
+	}
+}

+ 113 - 0
src/github.com/PuerkitoBio/goquery/type.go

@@ -0,0 +1,113 @@
+package goquery
+
+import (
+	"errors"
+	"io"
+	"net/http"
+	"net/url"
+
+	"golang.org/x/net/html"
+)
+
+// Document represents an HTML document to be manipulated. Unlike jQuery, which
+// is loaded as part of a DOM document, and thus acts upon its containing
+// document, GoQuery doesn't know which HTML document to act upon. So it needs
+// to be told, and that's what the Document class is for. It holds the root
+// document node to manipulate, and can make selections on this document.
+type Document struct {
+	*Selection
+	Url      *url.URL
+	rootNode *html.Node
+}
+
+// NewDocumentFromNode is a Document constructor that takes a root html Node
+// as argument.
+func NewDocumentFromNode(root *html.Node) *Document {
+	return newDocument(root, nil)
+}
+
+// NewDocument is a Document constructor that takes a string URL as argument.
+// It loads the specified document, parses it, and stores the root Document
+// node, ready to be manipulated.
+func NewDocument(url string) (*Document, error) {
+	// Load the URL
+	res, e := http.Get(url)
+	if e != nil {
+		return nil, e
+	}
+	return NewDocumentFromResponse(res)
+}
+
+// NewDocumentFromReader returns a Document from a generic reader.
+// It returns an error as second value if the reader's data cannot be parsed
+// as html. It does *not* check if the reader is also an io.Closer, so the
+// provided reader is never closed by this call, it is the responsibility
+// of the caller to close it if required.
+func NewDocumentFromReader(r io.Reader) (*Document, error) {
+	root, e := html.Parse(r)
+	if e != nil {
+		return nil, e
+	}
+	return newDocument(root, nil), nil
+}
+
+// NewDocumentFromResponse is another Document constructor that takes an http response as argument.
+// It loads the specified response's document, parses it, and stores the root Document
+// node, ready to be manipulated. The response's body is closed on return.
+func NewDocumentFromResponse(res *http.Response) (*Document, error) {
+	if res == nil {
+		return nil, errors.New("Response is nil pointer")
+	}
+
+	defer res.Body.Close()
+
+	// Parse the HTML into nodes
+	root, e := html.Parse(res.Body)
+	if e != nil {
+		return nil, e
+	}
+
+	// Create and fill the document
+	return newDocument(root, res.Request.URL), nil
+}
+
+// CloneDocument creates a deep-clone of a document.
+func CloneDocument(doc *Document) *Document {
+	return newDocument(cloneNode(doc.rootNode), doc.Url)
+}
+
+// Private constructor, make sure all fields are correctly filled.
+func newDocument(root *html.Node, url *url.URL) *Document {
+	// Create and fill the document
+	d := &Document{nil, url, root}
+	d.Selection = newSingleSelection(root, d)
+	return d
+}
+
+// Selection represents a collection of nodes matching some criteria. The
+// initial Selection can be created by using Document.Find, and then
+// manipulated using the jQuery-like chainable syntax and methods.
+type Selection struct {
+	Nodes    []*html.Node
+	document *Document
+	prevSel  *Selection
+}
+
+// Helper constructor to create an empty selection
+func newEmptySelection(doc *Document) *Selection {
+	return &Selection{nil, doc, nil}
+}
+
+// Helper constructor to create a selection of only one node
+func newSingleSelection(node *html.Node, doc *Document) *Selection {
+	return &Selection{[]*html.Node{node}, doc, nil}
+}
+
+// Matcher is an interface that defines the methods to match
+// HTML nodes against a compiled selector string. Cascadia's
+// Selector implements this interface.
+type Matcher interface {
+	Match(*html.Node) bool
+	MatchAll(*html.Node) []*html.Node
+	Filter([]*html.Node) []*html.Node
+}

+ 192 - 0
src/github.com/PuerkitoBio/goquery/type_test.go

@@ -0,0 +1,192 @@
+package goquery
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"testing"
+
+	"golang.org/x/net/html"
+)
+
+// Test helper functions and members
+var doc *Document
+var doc2 *Document
+var doc3 *Document
+var docB *Document
+var docW *Document
+
+func Doc() *Document {
+	if doc == nil {
+		doc = loadDoc("page.html")
+	}
+	return doc
+}
+func DocClone() *Document {
+	return CloneDocument(Doc())
+}
+func Doc2() *Document {
+	if doc2 == nil {
+		doc2 = loadDoc("page2.html")
+	}
+	return doc2
+}
+func Doc2Clone() *Document {
+	return CloneDocument(Doc2())
+}
+func Doc3() *Document {
+	if doc3 == nil {
+		doc3 = loadDoc("page3.html")
+	}
+	return doc3
+}
+func Doc3Clone() *Document {
+	return CloneDocument(Doc3())
+}
+func DocB() *Document {
+	if docB == nil {
+		docB = loadDoc("gotesting.html")
+	}
+	return docB
+}
+func DocBClone() *Document {
+	return CloneDocument(DocB())
+}
+func DocW() *Document {
+	if docW == nil {
+		docW = loadDoc("gowiki.html")
+	}
+	return docW
+}
+func DocWClone() *Document {
+	return CloneDocument(DocW())
+}
+
+func assertLength(t *testing.T, nodes []*html.Node, length int) {
+	if len(nodes) != length {
+		t.Errorf("Expected %d nodes, found %d.", length, len(nodes))
+		for i, n := range nodes {
+			t.Logf("Node %d: %+v.", i, n)
+		}
+	}
+}
+
+func assertClass(t *testing.T, sel *Selection, class string) {
+	if !sel.HasClass(class) {
+		t.Errorf("Expected node to have class %s, found %+v.", class, sel.Get(0))
+	}
+}
+
+func assertPanic(t *testing.T) {
+	if e := recover(); e == nil {
+		t.Error("Expected a panic.")
+	}
+}
+
+func assertEqual(t *testing.T, s1 *Selection, s2 *Selection) {
+	if s1 != s2 {
+		t.Error("Expected selection objects to be the same.")
+	}
+}
+
+func assertSelectionIs(t *testing.T, sel *Selection, is ...string) {
+	for i := 0; i < sel.Length(); i++ {
+		if !sel.Eq(i).Is(is[i]) {
+			t.Errorf("Expected node %d to be %s, found %+v", i, is[i], sel.Get(i))
+		}
+	}
+}
+
+func printSel(t *testing.T, sel *Selection) {
+	if testing.Verbose() {
+		h, err := sel.Html()
+		if err != nil {
+			t.Fatal(err)
+		}
+		t.Log(h)
+	}
+}
+
+func loadDoc(page string) *Document {
+	var f *os.File
+	var e error
+
+	if f, e = os.Open(fmt.Sprintf("./testdata/%s", page)); e != nil {
+		panic(e.Error())
+	}
+	defer f.Close()
+
+	var node *html.Node
+	if node, e = html.Parse(f); e != nil {
+		panic(e.Error())
+	}
+	return NewDocumentFromNode(node)
+}
+
+func TestNewDocument(t *testing.T) {
+	if f, e := os.Open("./testdata/page.html"); e != nil {
+		t.Error(e.Error())
+	} else {
+		defer f.Close()
+		if node, e := html.Parse(f); e != nil {
+			t.Error(e.Error())
+		} else {
+			doc = NewDocumentFromNode(node)
+		}
+	}
+}
+
+func TestNewDocumentFromReader(t *testing.T) {
+	cases := []struct {
+		src string
+		err bool
+		sel string
+		cnt int
+	}{
+		0: {
+			src: `
+<html>
+<head>
+<title>Test</title>
+<body>
+<h1>Hi</h1>
+</body>
+</html>`,
+			sel: "h1",
+			cnt: 1,
+		},
+		1: {
+			// Actually pretty hard to make html.Parse return an error
+			// based on content...
+			src: `<html><body><aef<eqf>>>qq></body></ht>`,
+		},
+	}
+	buf := bytes.NewBuffer(nil)
+
+	for i, c := range cases {
+		buf.Reset()
+		buf.WriteString(c.src)
+
+		d, e := NewDocumentFromReader(buf)
+		if (e != nil) != c.err {
+			if c.err {
+				t.Errorf("[%d] - expected error, got none", i)
+			} else {
+				t.Errorf("[%d] - expected no error, got %s", i, e)
+			}
+		}
+		if c.sel != "" {
+			s := d.Find(c.sel)
+			if s.Length() != c.cnt {
+				t.Errorf("[%d] - expected %d nodes, found %d", i, c.cnt, s.Length())
+			}
+		}
+	}
+}
+
+func TestNewDocumentFromResponseNil(t *testing.T) {
+	_, e := NewDocumentFromResponse(nil)
+	if e == nil {
+		t.Error("Expected error, got none")
+	}
+}

+ 84 - 0
src/github.com/PuerkitoBio/goquery/utilities.go

@@ -0,0 +1,84 @@
+package goquery
+
+import (
+	"golang.org/x/net/html"
+)
+
+func getChildren(n *html.Node) (result []*html.Node) {
+	for c := n.FirstChild; c != nil; c = c.NextSibling {
+		result = append(result, c)
+	}
+	return result
+}
+
+// Loop through all container nodes to search for the target node.
+func sliceContains(container []*html.Node, contained *html.Node) bool {
+	for _, n := range container {
+		if nodeContains(n, contained) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Checks if the contained node is within the container node.
+func nodeContains(container *html.Node, contained *html.Node) bool {
+	// Check if the parent of the contained node is the container node, traversing
+	// upward until the top is reached, or the container is found.
+	for contained = contained.Parent; contained != nil; contained = contained.Parent {
+		if container == contained {
+			return true
+		}
+	}
+	return false
+}
+
+// Checks if the target node is in the slice of nodes.
+func isInSlice(slice []*html.Node, node *html.Node) bool {
+	return indexInSlice(slice, node) > -1
+}
+
+// Returns the index of the target node in the slice, or -1.
+func indexInSlice(slice []*html.Node, node *html.Node) int {
+	if node != nil {
+		for i, n := range slice {
+			if n == node {
+				return i
+			}
+		}
+	}
+	return -1
+}
+
+// Appends the new nodes to the target slice, making sure no duplicate is added.
+// There is no check to the original state of the target slice, so it may still
+// contain duplicates. The target slice is returned because append() may create
+// a new underlying array.
+func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
+	for _, n := range nodes {
+		if !isInSlice(target, n) {
+			target = append(target, n)
+		}
+	}
+
+	return target
+}
+
+// Loop through a selection, returning only those nodes that pass the predicate
+// function.
+func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
+	for i, n := range sel.Nodes {
+		if predicate(i, newSingleSelection(n, sel.document)) {
+			result = append(result, n)
+		}
+	}
+	return result
+}
+
+// Creates a new Selection object based on the specified nodes, and keeps the
+// source Selection object on the stack (linked list).
+func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
+	result := &Selection{nodes, fromSel.document, fromSel}
+	return result
+}

+ 149 - 0
src/github.com/SKatiyar/qr/coding/gen.go

@@ -0,0 +1,149 @@
+// +build ignore
+
+package main
+
+import "fmt"
+
+// tables from qrencode-3.1.1/qrspec.c
+
+var capacity = [41]struct {
+	width     int
+	words     int
+	remainder int
+	ec        [4]int
+}{
+	{0, 0, 0, [4]int{0, 0, 0, 0}},
+	{21, 26, 0, [4]int{7, 10, 13, 17}}, // 1
+	{25, 44, 7, [4]int{10, 16, 22, 28}},
+	{29, 70, 7, [4]int{15, 26, 36, 44}},
+	{33, 100, 7, [4]int{20, 36, 52, 64}},
+	{37, 134, 7, [4]int{26, 48, 72, 88}}, // 5
+	{41, 172, 7, [4]int{36, 64, 96, 112}},
+	{45, 196, 0, [4]int{40, 72, 108, 130}},
+	{49, 242, 0, [4]int{48, 88, 132, 156}},
+	{53, 292, 0, [4]int{60, 110, 160, 192}},
+	{57, 346, 0, [4]int{72, 130, 192, 224}}, //10
+	{61, 404, 0, [4]int{80, 150, 224, 264}},
+	{65, 466, 0, [4]int{96, 176, 260, 308}},
+	{69, 532, 0, [4]int{104, 198, 288, 352}},
+	{73, 581, 3, [4]int{120, 216, 320, 384}},
+	{77, 655, 3, [4]int{132, 240, 360, 432}}, //15
+	{81, 733, 3, [4]int{144, 280, 408, 480}},
+	{85, 815, 3, [4]int{168, 308, 448, 532}},
+	{89, 901, 3, [4]int{180, 338, 504, 588}},
+	{93, 991, 3, [4]int{196, 364, 546, 650}},
+	{97, 1085, 3, [4]int{224, 416, 600, 700}}, //20
+	{101, 1156, 4, [4]int{224, 442, 644, 750}},
+	{105, 1258, 4, [4]int{252, 476, 690, 816}},
+	{109, 1364, 4, [4]int{270, 504, 750, 900}},
+	{113, 1474, 4, [4]int{300, 560, 810, 960}},
+	{117, 1588, 4, [4]int{312, 588, 870, 1050}}, //25
+	{121, 1706, 4, [4]int{336, 644, 952, 1110}},
+	{125, 1828, 4, [4]int{360, 700, 1020, 1200}},
+	{129, 1921, 3, [4]int{390, 728, 1050, 1260}},
+	{133, 2051, 3, [4]int{420, 784, 1140, 1350}},
+	{137, 2185, 3, [4]int{450, 812, 1200, 1440}}, //30
+	{141, 2323, 3, [4]int{480, 868, 1290, 1530}},
+	{145, 2465, 3, [4]int{510, 924, 1350, 1620}},
+	{149, 2611, 3, [4]int{540, 980, 1440, 1710}},
+	{153, 2761, 3, [4]int{570, 1036, 1530, 1800}},
+	{157, 2876, 0, [4]int{570, 1064, 1590, 1890}}, //35
+	{161, 3034, 0, [4]int{600, 1120, 1680, 1980}},
+	{165, 3196, 0, [4]int{630, 1204, 1770, 2100}},
+	{169, 3362, 0, [4]int{660, 1260, 1860, 2220}},
+	{173, 3532, 0, [4]int{720, 1316, 1950, 2310}},
+	{177, 3706, 0, [4]int{750, 1372, 2040, 2430}}, //40
+}
+
+var eccTable = [41][4][2]int{
+	{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
+	{{1, 0}, {1, 0}, {1, 0}, {1, 0}}, // 1
+	{{1, 0}, {1, 0}, {1, 0}, {1, 0}},
+	{{1, 0}, {1, 0}, {2, 0}, {2, 0}},
+	{{1, 0}, {2, 0}, {2, 0}, {4, 0}},
+	{{1, 0}, {2, 0}, {2, 2}, {2, 2}}, // 5
+	{{2, 0}, {4, 0}, {4, 0}, {4, 0}},
+	{{2, 0}, {4, 0}, {2, 4}, {4, 1}},
+	{{2, 0}, {2, 2}, {4, 2}, {4, 2}},
+	{{2, 0}, {3, 2}, {4, 4}, {4, 4}},
+	{{2, 2}, {4, 1}, {6, 2}, {6, 2}}, //10
+	{{4, 0}, {1, 4}, {4, 4}, {3, 8}},
+	{{2, 2}, {6, 2}, {4, 6}, {7, 4}},
+	{{4, 0}, {8, 1}, {8, 4}, {12, 4}},
+	{{3, 1}, {4, 5}, {11, 5}, {11, 5}},
+	{{5, 1}, {5, 5}, {5, 7}, {11, 7}}, //15
+	{{5, 1}, {7, 3}, {15, 2}, {3, 13}},
+	{{1, 5}, {10, 1}, {1, 15}, {2, 17}},
+	{{5, 1}, {9, 4}, {17, 1}, {2, 19}},
+	{{3, 4}, {3, 11}, {17, 4}, {9, 16}},
+	{{3, 5}, {3, 13}, {15, 5}, {15, 10}}, //20
+	{{4, 4}, {17, 0}, {17, 6}, {19, 6}},
+	{{2, 7}, {17, 0}, {7, 16}, {34, 0}},
+	{{4, 5}, {4, 14}, {11, 14}, {16, 14}},
+	{{6, 4}, {6, 14}, {11, 16}, {30, 2}},
+	{{8, 4}, {8, 13}, {7, 22}, {22, 13}}, //25
+	{{10, 2}, {19, 4}, {28, 6}, {33, 4}},
+	{{8, 4}, {22, 3}, {8, 26}, {12, 28}},
+	{{3, 10}, {3, 23}, {4, 31}, {11, 31}},
+	{{7, 7}, {21, 7}, {1, 37}, {19, 26}},
+	{{5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30
+	{{13, 3}, {2, 29}, {42, 1}, {23, 28}},
+	{{17, 0}, {10, 23}, {10, 35}, {19, 35}},
+	{{17, 1}, {14, 21}, {29, 19}, {11, 46}},
+	{{13, 6}, {14, 23}, {44, 7}, {59, 1}},
+	{{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35
+	{{6, 14}, {6, 34}, {46, 10}, {2, 64}},
+	{{17, 4}, {29, 14}, {49, 10}, {24, 46}},
+	{{4, 18}, {13, 32}, {48, 14}, {42, 32}},
+	{{20, 4}, {40, 7}, {43, 22}, {10, 67}},
+	{{19, 6}, {18, 31}, {34, 34}, {20, 61}}, //40
+}
+
+var align = [41][2]int{
+	{0, 0},
+	{0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5
+	{34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10
+	{30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15
+	{26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20
+	{28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25
+	{30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30
+	{30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35
+	{24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40
+}
+
+var versionPattern = [41]int{
+	0,
+	0, 0, 0, 0, 0, 0,
+	0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
+	0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
+	0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
+	0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
+	0x27541, 0x28c69,
+}
+
+func main() {
+	fmt.Printf("\t{},\n")
+	for i := 1; i <= 40; i++ {
+		apos := align[i][0] - 2
+		if apos < 0 {
+			apos = 100
+		}
+		astride := align[i][1] - align[i][0]
+		if astride < 1 {
+			astride = 100
+		}
+		fmt.Printf("\t{%v, %v, %v, %#x, [4]level{{%v, %v}, {%v, %v}, {%v, %v}, {%v, %v}}},  // %v\n",
+			apos, astride, capacity[i].words,
+			versionPattern[i],
+			eccTable[i][0][0]+eccTable[i][0][1],
+			float64(capacity[i].ec[0])/float64(eccTable[i][0][0]+eccTable[i][0][1]),
+			eccTable[i][1][0]+eccTable[i][1][1],
+			float64(capacity[i].ec[1])/float64(eccTable[i][1][0]+eccTable[i][1][1]),
+			eccTable[i][2][0]+eccTable[i][2][1],
+			float64(capacity[i].ec[2])/float64(eccTable[i][2][0]+eccTable[i][2][1]),
+			eccTable[i][3][0]+eccTable[i][3][1],
+			float64(capacity[i].ec[3])/float64(eccTable[i][3][0]+eccTable[i][3][1]),
+			i,
+		)
+	}
+}

+ 815 - 0
src/github.com/SKatiyar/qr/coding/qr.go

@@ -0,0 +1,815 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package coding implements low-level QR coding details.
+package coding
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/SKatiyar/qr/gf256"
+)
+
+// Field is the field for QR error correction.
+var Field = gf256.NewField(0x11d, 2)
+
+// A Version represents a QR version.
+// The version specifies the size of the QR code:
+// a QR code with version v has 4v+17 pixels on a side.
+// Versions number from 1 to 40: the larger the version,
+// the more information the code can store.
+type Version int
+
+const MinVersion = 1
+const MaxVersion = 40
+
+func (v Version) String() string {
+	return strconv.Itoa(int(v))
+}
+
+func (v Version) sizeClass() int {
+	if v <= 9 {
+		return 0
+	}
+	if v <= 26 {
+		return 1
+	}
+	return 2
+}
+
+// DataBytes returns the number of data bytes that can be
+// stored in a QR code with the given version and level.
+func (v Version) DataBytes(l Level) int {
+	vt := &vtab[v]
+	lev := &vt.level[l]
+	return vt.bytes - lev.nblock*lev.check
+}
+
+// Encoding implements a QR data encoding scheme.
+// The implementations--Numeric, Alphanumeric, and String--specify
+// the character set and the mapping from UTF-8 to code bits.
+// The more restrictive the mode, the fewer code bits are needed.
+type Encoding interface {
+	Check() error
+	Bits(v Version) int
+	Encode(b *Bits, v Version)
+}
+
+type Bits struct {
+	b    []byte
+	nbit int
+}
+
+func (b *Bits) Reset() {
+	b.b = b.b[:0]
+	b.nbit = 0
+}
+
+func (b *Bits) Bits() int {
+	return b.nbit
+}
+
+func (b *Bits) Bytes() []byte {
+	if b.nbit%8 != 0 {
+		panic("fractional byte")
+	}
+	return b.b
+}
+
+func (b *Bits) Append(p []byte) {
+	if b.nbit%8 != 0 {
+		panic("fractional byte")
+	}
+	b.b = append(b.b, p...)
+	b.nbit += 8 * len(p)
+}
+
+func (b *Bits) Write(v uint, nbit int) {
+	for nbit > 0 {
+		n := nbit
+		if n > 8 {
+			n = 8
+		}
+		if b.nbit%8 == 0 {
+			b.b = append(b.b, 0)
+		} else {
+			m := -b.nbit & 7
+			if n > m {
+				n = m
+			}
+		}
+		b.nbit += n
+		sh := uint(nbit - n)
+		b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7))
+		v -= v >> sh << sh
+		nbit -= n
+	}
+}
+
+// Num is the encoding for numeric data.
+// The only valid characters are the decimal digits 0 through 9.
+type Num string
+
+func (s Num) String() string {
+	return fmt.Sprintf("Num(%#q)", string(s))
+}
+
+func (s Num) Check() error {
+	for _, c := range s {
+		if c < '0' || '9' < c {
+			return fmt.Errorf("non-numeric string %#q", string(s))
+		}
+	}
+	return nil
+}
+
+var numLen = [3]int{10, 12, 14}
+
+func (s Num) Bits(v Version) int {
+	return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3
+}
+
+func (s Num) Encode(b *Bits, v Version) {
+	b.Write(1, 4)
+	b.Write(uint(len(s)), numLen[v.sizeClass()])
+	var i int
+	for i = 0; i+3 <= len(s); i += 3 {
+		w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0')
+		b.Write(w, 10)
+	}
+	switch len(s) - i {
+	case 1:
+		w := uint(s[i] - '0')
+		b.Write(w, 4)
+	case 2:
+		w := uint(s[i]-'0')*10 + uint(s[i+1]-'0')
+		b.Write(w, 7)
+	}
+}
+
+// Alpha is the encoding for alphanumeric data.
+// The valid characters are 0-9A-Z$%*+-./: and space.
+type Alpha string
+
+const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
+
+func (s Alpha) String() string {
+	return fmt.Sprintf("Alpha(%#q)", string(s))
+}
+
+func (s Alpha) Check() error {
+	for _, c := range s {
+		if strings.IndexRune(alphabet, c) < 0 {
+			return fmt.Errorf("non-alphanumeric string %#q", string(s))
+		}
+	}
+	return nil
+}
+
+var alphaLen = [3]int{9, 11, 13}
+
+func (s Alpha) Bits(v Version) int {
+	return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2
+}
+
+func (s Alpha) Encode(b *Bits, v Version) {
+	b.Write(2, 4)
+	b.Write(uint(len(s)), alphaLen[v.sizeClass()])
+	var i int
+	for i = 0; i+2 <= len(s); i += 2 {
+		w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 +
+			uint(strings.IndexRune(alphabet, rune(s[i+1])))
+		b.Write(w, 11)
+	}
+
+	if i < len(s) {
+		w := uint(strings.IndexRune(alphabet, rune(s[i])))
+		b.Write(w, 6)
+	}
+}
+
+// String is the encoding for 8-bit data.  All bytes are valid.
+type String string
+
+func (s String) String() string {
+	return fmt.Sprintf("String(%#q)", string(s))
+}
+
+func (s String) Check() error {
+	return nil
+}
+
+var stringLen = [3]int{8, 16, 16}
+
+func (s String) Bits(v Version) int {
+	return 4 + stringLen[v.sizeClass()] + 8*len(s)
+}
+
+func (s String) Encode(b *Bits, v Version) {
+	b.Write(4, 4)
+	b.Write(uint(len(s)), stringLen[v.sizeClass()])
+	for i := 0; i < len(s); i++ {
+		b.Write(uint(s[i]), 8)
+	}
+}
+
+// A Pixel describes a single pixel in a QR code.
+type Pixel uint32
+
+const (
+	Black Pixel = 1 << iota
+	Invert
+)
+
+func (p Pixel) Offset() uint {
+	return uint(p >> 6)
+}
+
+func OffsetPixel(o uint) Pixel {
+	return Pixel(o << 6)
+}
+
+func (r PixelRole) Pixel() Pixel {
+	return Pixel(r << 2)
+}
+
+func (p Pixel) Role() PixelRole {
+	return PixelRole(p>>2) & 15
+}
+
+func (p Pixel) String() string {
+	s := p.Role().String()
+	if p&Black != 0 {
+		s += "+black"
+	}
+	if p&Invert != 0 {
+		s += "+invert"
+	}
+	s += "+" + strconv.FormatUint(uint64(p.Offset()), 10)
+	return s
+}
+
+// A PixelRole describes the role of a QR pixel.
+type PixelRole uint32
+
+const (
+	_         PixelRole = iota
+	Position            // position squares (large)
+	Alignment           // alignment squares (small)
+	Timing              // timing strip between position squares
+	Format              // format metadata
+	PVersion            // version pattern
+	Unused              // unused pixel
+	Data                // data bit
+	Check               // error correction check bit
+	Extra
+)
+
+var roles = []string{
+	"",
+	"position",
+	"alignment",
+	"timing",
+	"format",
+	"pversion",
+	"unused",
+	"data",
+	"check",
+	"extra",
+}
+
+func (r PixelRole) String() string {
+	if Position <= r && r <= Check {
+		return roles[r]
+	}
+	return strconv.Itoa(int(r))
+}
+
+// A Level represents a QR error correction level.
+// From least to most tolerant of errors, they are L, M, Q, H.
+type Level int
+
+const (
+	L Level = iota
+	M
+	Q
+	H
+)
+
+func (l Level) String() string {
+	if L <= l && l <= H {
+		return "LMQH"[l : l+1]
+	}
+	return strconv.Itoa(int(l))
+}
+
+// A Code is a square pixel grid.
+type Code struct {
+	Bitmap []byte // 1 is black, 0 is white
+	Size   int    // number of pixels on a side
+	Stride int    // number of bytes per row
+}
+
+func (c *Code) Black(x, y int) bool {
+	return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
+		c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
+}
+
+// A Mask describes a mask that is applied to the QR
+// code to avoid QR artifacts being interpreted as
+// alignment and timing patterns (such as the squares
+// in the corners).  Valid masks are integers from 0 to 7.
+type Mask int
+
+// http://www.swetake.com/qr/qr5_en.html
+var mfunc = []func(int, int) bool{
+	func(i, j int) bool { return (i+j)%2 == 0 },
+	func(i, j int) bool { return i%2 == 0 },
+	func(i, j int) bool { return j%3 == 0 },
+	func(i, j int) bool { return (i+j)%3 == 0 },
+	func(i, j int) bool { return (i/2+j/3)%2 == 0 },
+	func(i, j int) bool { return i*j%2+i*j%3 == 0 },
+	func(i, j int) bool { return (i*j%2+i*j%3)%2 == 0 },
+	func(i, j int) bool { return (i*j%3+(i+j)%2)%2 == 0 },
+}
+
+func (m Mask) Invert(y, x int) bool {
+	if m < 0 {
+		return false
+	}
+	return mfunc[m](y, x)
+}
+
+// A Plan describes how to construct a QR code
+// with a specific version, level, and mask.
+type Plan struct {
+	Version Version
+	Level   Level
+	Mask    Mask
+
+	DataBytes  int // number of data bytes
+	CheckBytes int // number of error correcting (checksum) bytes
+	Blocks     int // number of data blocks
+
+	Pixel [][]Pixel // pixel map
+}
+
+// NewPlan returns a Plan for a QR code with the given
+// version, level, and mask.
+func NewPlan(version Version, level Level, mask Mask) (*Plan, error) {
+	p, err := vplan(version)
+	if err != nil {
+		return nil, err
+	}
+	if err := fplan(level, mask, p); err != nil {
+		return nil, err
+	}
+	if err := lplan(version, level, p); err != nil {
+		return nil, err
+	}
+	if err := mplan(mask, p); err != nil {
+		return nil, err
+	}
+	return p, nil
+}
+
+func (b *Bits) Pad(n int) {
+	if n < 0 {
+		panic("qr: invalid pad size")
+	}
+	if n <= 4 {
+		b.Write(0, n)
+	} else {
+		b.Write(0, 4)
+		n -= 4
+		n -= -b.Bits() & 7
+		b.Write(0, -b.Bits()&7)
+		pad := n / 8
+		for i := 0; i < pad; i += 2 {
+			b.Write(0xec, 8)
+			if i+1 >= pad {
+				break
+			}
+			b.Write(0x11, 8)
+		}
+	}
+}
+
+func (b *Bits) AddCheckBytes(v Version, l Level) {
+	nd := v.DataBytes(l)
+	if b.nbit < nd*8 {
+		b.Pad(nd*8 - b.nbit)
+	}
+	if b.nbit != nd*8 {
+		panic("qr: too much data")
+	}
+
+	dat := b.Bytes()
+	vt := &vtab[v]
+	lev := &vt.level[l]
+	db := nd / lev.nblock
+	extra := nd % lev.nblock
+	chk := make([]byte, lev.check)
+	rs := gf256.NewRSEncoder(Field, lev.check)
+	for i := 0; i < lev.nblock; i++ {
+		if i == lev.nblock-extra {
+			db++
+		}
+		rs.ECC(dat[:db], chk)
+		b.Append(chk)
+		dat = dat[db:]
+	}
+
+	if len(b.Bytes()) != vt.bytes {
+		panic("qr: internal error")
+	}
+}
+
+func (p *Plan) Encode(text ...Encoding) (*Code, error) {
+	var b Bits
+	for _, t := range text {
+		if err := t.Check(); err != nil {
+			return nil, err
+		}
+		t.Encode(&b, p.Version)
+	}
+	if b.Bits() > p.DataBytes*8 {
+		return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8)
+	}
+	b.AddCheckBytes(p.Version, p.Level)
+	bytes := b.Bytes()
+
+	// Now we have the checksum bytes and the data bytes.
+	// Construct the actual code.
+	c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7}
+	c.Bitmap = make([]byte, c.Stride*c.Size)
+	crow := c.Bitmap
+	for _, row := range p.Pixel {
+		for x, pix := range row {
+			switch pix.Role() {
+			case Data, Check:
+				o := pix.Offset()
+				if bytes[o/8]&(1<<uint(7-o&7)) != 0 {
+					pix ^= Black
+				}
+			}
+			if pix&Black != 0 {
+				crow[x/8] |= 1 << uint(7-x&7)
+			}
+		}
+		crow = crow[c.Stride:]
+	}
+	return c, nil
+}
+
+// A version describes metadata associated with a version.
+type version struct {
+	apos    int
+	astride int
+	bytes   int
+	pattern int
+	level   [4]level
+}
+
+type level struct {
+	nblock int
+	check  int
+}
+
+var vtab = []version{
+	{},
+	{100, 100, 26, 0x0, [4]level{{1, 7}, {1, 10}, {1, 13}, {1, 17}}},          // 1
+	{16, 100, 44, 0x0, [4]level{{1, 10}, {1, 16}, {1, 22}, {1, 28}}},          // 2
+	{20, 100, 70, 0x0, [4]level{{1, 15}, {1, 26}, {2, 18}, {2, 22}}},          // 3
+	{24, 100, 100, 0x0, [4]level{{1, 20}, {2, 18}, {2, 26}, {4, 16}}},         // 4
+	{28, 100, 134, 0x0, [4]level{{1, 26}, {2, 24}, {4, 18}, {4, 22}}},         // 5
+	{32, 100, 172, 0x0, [4]level{{2, 18}, {4, 16}, {4, 24}, {4, 28}}},         // 6
+	{20, 16, 196, 0x7c94, [4]level{{2, 20}, {4, 18}, {6, 18}, {5, 26}}},       // 7
+	{22, 18, 242, 0x85bc, [4]level{{2, 24}, {4, 22}, {6, 22}, {6, 26}}},       // 8
+	{24, 20, 292, 0x9a99, [4]level{{2, 30}, {5, 22}, {8, 20}, {8, 24}}},       // 9
+	{26, 22, 346, 0xa4d3, [4]level{{4, 18}, {5, 26}, {8, 24}, {8, 28}}},       // 10
+	{28, 24, 404, 0xbbf6, [4]level{{4, 20}, {5, 30}, {8, 28}, {11, 24}}},      // 11
+	{30, 26, 466, 0xc762, [4]level{{4, 24}, {8, 22}, {10, 26}, {11, 28}}},     // 12
+	{32, 28, 532, 0xd847, [4]level{{4, 26}, {9, 22}, {12, 24}, {16, 22}}},     // 13
+	{24, 20, 581, 0xe60d, [4]level{{4, 30}, {9, 24}, {16, 20}, {16, 24}}},     // 14
+	{24, 22, 655, 0xf928, [4]level{{6, 22}, {10, 24}, {12, 30}, {18, 24}}},    // 15
+	{24, 24, 733, 0x10b78, [4]level{{6, 24}, {10, 28}, {17, 24}, {16, 30}}},   // 16
+	{28, 24, 815, 0x1145d, [4]level{{6, 28}, {11, 28}, {16, 28}, {19, 28}}},   // 17
+	{28, 26, 901, 0x12a17, [4]level{{6, 30}, {13, 26}, {18, 28}, {21, 28}}},   // 18
+	{28, 28, 991, 0x13532, [4]level{{7, 28}, {14, 26}, {21, 26}, {25, 26}}},   // 19
+	{32, 28, 1085, 0x149a6, [4]level{{8, 28}, {16, 26}, {20, 30}, {25, 28}}},  // 20
+	{26, 22, 1156, 0x15683, [4]level{{8, 28}, {17, 26}, {23, 28}, {25, 30}}},  // 21
+	{24, 24, 1258, 0x168c9, [4]level{{9, 28}, {17, 28}, {23, 30}, {34, 24}}},  // 22
+	{28, 24, 1364, 0x177ec, [4]level{{9, 30}, {18, 28}, {25, 30}, {30, 30}}},  // 23
+	{26, 26, 1474, 0x18ec4, [4]level{{10, 30}, {20, 28}, {27, 30}, {32, 30}}}, // 24
+	{30, 26, 1588, 0x191e1, [4]level{{12, 26}, {21, 28}, {29, 30}, {35, 30}}}, // 25
+	{28, 28, 1706, 0x1afab, [4]level{{12, 28}, {23, 28}, {34, 28}, {37, 30}}}, // 26
+	{32, 28, 1828, 0x1b08e, [4]level{{12, 30}, {25, 28}, {34, 30}, {40, 30}}}, // 27
+	{24, 24, 1921, 0x1cc1a, [4]level{{13, 30}, {26, 28}, {35, 30}, {42, 30}}}, // 28
+	{28, 24, 2051, 0x1d33f, [4]level{{14, 30}, {28, 28}, {38, 30}, {45, 30}}}, // 29
+	{24, 26, 2185, 0x1ed75, [4]level{{15, 30}, {29, 28}, {40, 30}, {48, 30}}}, // 30
+	{28, 26, 2323, 0x1f250, [4]level{{16, 30}, {31, 28}, {43, 30}, {51, 30}}}, // 31
+	{32, 26, 2465, 0x209d5, [4]level{{17, 30}, {33, 28}, {45, 30}, {54, 30}}}, // 32
+	{28, 28, 2611, 0x216f0, [4]level{{18, 30}, {35, 28}, {48, 30}, {57, 30}}}, // 33
+	{32, 28, 2761, 0x228ba, [4]level{{19, 30}, {37, 28}, {51, 30}, {60, 30}}}, // 34
+	{28, 24, 2876, 0x2379f, [4]level{{19, 30}, {38, 28}, {53, 30}, {63, 30}}}, // 35
+	{22, 26, 3034, 0x24b0b, [4]level{{20, 30}, {40, 28}, {56, 30}, {66, 30}}}, // 36
+	{26, 26, 3196, 0x2542e, [4]level{{21, 30}, {43, 28}, {59, 30}, {70, 30}}}, // 37
+	{30, 26, 3362, 0x26a64, [4]level{{22, 30}, {45, 28}, {62, 30}, {74, 30}}}, // 38
+	{24, 28, 3532, 0x27541, [4]level{{24, 30}, {47, 28}, {65, 30}, {77, 30}}}, // 39
+	{28, 28, 3706, 0x28c69, [4]level{{25, 30}, {49, 28}, {68, 30}, {81, 30}}}, // 40
+}
+
+func grid(siz int) [][]Pixel {
+	m := make([][]Pixel, siz)
+	pix := make([]Pixel, siz*siz)
+	for i := range m {
+		m[i], pix = pix[:siz], pix[siz:]
+	}
+	return m
+}
+
+// vplan creates a Plan for the given version.
+func vplan(v Version) (*Plan, error) {
+	p := &Plan{Version: v}
+	if v < 1 || v > 40 {
+		return nil, fmt.Errorf("invalid QR version %d", int(v))
+	}
+	siz := 17 + int(v)*4
+	m := grid(siz)
+	p.Pixel = m
+
+	// Timing markers (overwritten by boxes).
+	const ti = 6 // timing is in row/column 6 (counting from 0)
+	for i := range m {
+		p := Timing.Pixel()
+		if i&1 == 0 {
+			p |= Black
+		}
+		m[i][ti] = p
+		m[ti][i] = p
+	}
+
+	// Position boxes.
+	posBox(m, 0, 0)
+	posBox(m, siz-7, 0)
+	posBox(m, 0, siz-7)
+
+	// Alignment boxes.
+	info := &vtab[v]
+	for x := 4; x+5 < siz; {
+		for y := 4; y+5 < siz; {
+			// don't overwrite timing markers
+			if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) {
+			} else {
+				alignBox(m, x, y)
+			}
+			if y == 4 {
+				y = info.apos
+			} else {
+				y += info.astride
+			}
+		}
+		if x == 4 {
+			x = info.apos
+		} else {
+			x += info.astride
+		}
+	}
+
+	// Version pattern.
+	pat := vtab[v].pattern
+	if pat != 0 {
+		v := pat
+		for x := 0; x < 6; x++ {
+			for y := 0; y < 3; y++ {
+				p := PVersion.Pixel()
+				if v&1 != 0 {
+					p |= Black
+				}
+				m[siz-11+y][x] = p
+				m[x][siz-11+y] = p
+				v >>= 1
+			}
+		}
+	}
+
+	// One lonely black pixel
+	m[siz-8][8] = Unused.Pixel() | Black
+
+	return p, nil
+}
+
+// fplan adds the format pixels
+func fplan(l Level, m Mask, p *Plan) error {
+	// Format pixels.
+	fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10
+	fb |= uint32(m) << 10   // mask
+	const formatPoly = 0x537
+	rem := fb
+	for i := 14; i >= 10; i-- {
+		if rem&(1<<uint(i)) != 0 {
+			rem ^= formatPoly << uint(i-10)
+		}
+	}
+	fb |= rem
+	invert := uint32(0x5412)
+	siz := len(p.Pixel)
+	for i := uint(0); i < 15; i++ {
+		pix := Format.Pixel() + OffsetPixel(i)
+		if (fb>>i)&1 == 1 {
+			pix |= Black
+		}
+		if (invert>>i)&1 == 1 {
+			pix ^= Invert | Black
+		}
+		// top left
+		switch {
+		case i < 6:
+			p.Pixel[i][8] = pix
+		case i < 8:
+			p.Pixel[i+1][8] = pix
+		case i < 9:
+			p.Pixel[8][7] = pix
+		default:
+			p.Pixel[8][14-i] = pix
+		}
+		// bottom right
+		switch {
+		case i < 8:
+			p.Pixel[8][siz-1-int(i)] = pix
+		default:
+			p.Pixel[siz-1-int(14-i)][8] = pix
+		}
+	}
+	return nil
+}
+
+// lplan edits a version-only Plan to add information
+// about the error correction levels.
+func lplan(v Version, l Level, p *Plan) error {
+	p.Level = l
+
+	nblock := vtab[v].level[l].nblock
+	ne := vtab[v].level[l].check
+	nde := (vtab[v].bytes - ne*nblock) / nblock
+	extra := (vtab[v].bytes - ne*nblock) % nblock
+	dataBits := (nde*nblock + extra) * 8
+	checkBits := ne * nblock * 8
+
+	p.DataBytes = vtab[v].bytes - ne*nblock
+	p.CheckBytes = ne * nblock
+	p.Blocks = nblock
+
+	// Make data + checksum pixels.
+	data := make([]Pixel, dataBits)
+	for i := range data {
+		data[i] = Data.Pixel() | OffsetPixel(uint(i))
+	}
+	check := make([]Pixel, checkBits)
+	for i := range check {
+		check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits))
+	}
+
+	// Split into blocks.
+	dataList := make([][]Pixel, nblock)
+	checkList := make([][]Pixel, nblock)
+	for i := 0; i < nblock; i++ {
+		// The last few blocks have an extra data byte (8 pixels).
+		nd := nde
+		if i >= nblock-extra {
+			nd++
+		}
+		dataList[i], data = data[0:nd*8], data[nd*8:]
+		checkList[i], check = check[0:ne*8], check[ne*8:]
+	}
+	if len(data) != 0 || len(check) != 0 {
+		panic("data/check math")
+	}
+
+	// Build up bit sequence, taking first byte of each block,
+	// then second byte, and so on.  Then checksums.
+	bits := make([]Pixel, dataBits+checkBits)
+	dst := bits
+	for i := 0; i < nde+1; i++ {
+		for _, b := range dataList {
+			if i*8 < len(b) {
+				copy(dst, b[i*8:(i+1)*8])
+				dst = dst[8:]
+			}
+		}
+	}
+	for i := 0; i < ne; i++ {
+		for _, b := range checkList {
+			if i*8 < len(b) {
+				copy(dst, b[i*8:(i+1)*8])
+				dst = dst[8:]
+			}
+		}
+	}
+	if len(dst) != 0 {
+		panic("dst math")
+	}
+
+	// Sweep up pair of columns,
+	// then down, assigning to right then left pixel.
+	// Repeat.
+	// See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm
+	siz := len(p.Pixel)
+	rem := make([]Pixel, 7)
+	for i := range rem {
+		rem[i] = Extra.Pixel()
+	}
+	src := append(bits, rem...)
+	for x := siz; x > 0; {
+		for y := siz - 1; y >= 0; y-- {
+			if p.Pixel[y][x-1].Role() == 0 {
+				p.Pixel[y][x-1], src = src[0], src[1:]
+			}
+			if p.Pixel[y][x-2].Role() == 0 {
+				p.Pixel[y][x-2], src = src[0], src[1:]
+			}
+		}
+		x -= 2
+		if x == 7 { // vertical timing strip
+			x--
+		}
+		for y := 0; y < siz; y++ {
+			if p.Pixel[y][x-1].Role() == 0 {
+				p.Pixel[y][x-1], src = src[0], src[1:]
+			}
+			if p.Pixel[y][x-2].Role() == 0 {
+				p.Pixel[y][x-2], src = src[0], src[1:]
+			}
+		}
+		x -= 2
+	}
+	return nil
+}
+
+// mplan edits a version+level-only Plan to add the mask.
+func mplan(m Mask, p *Plan) error {
+	p.Mask = m
+	for y, row := range p.Pixel {
+		for x, pix := range row {
+			if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) {
+				row[x] ^= Black | Invert
+			}
+		}
+	}
+	return nil
+}
+
+// posBox draws a position (large) box at upper left x, y.
+func posBox(m [][]Pixel, x, y int) {
+	pos := Position.Pixel()
+	// box
+	for dy := 0; dy < 7; dy++ {
+		for dx := 0; dx < 7; dx++ {
+			p := pos
+			if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 {
+				p |= Black
+			}
+			m[y+dy][x+dx] = p
+		}
+	}
+	// white border
+	for dy := -1; dy < 8; dy++ {
+		if 0 <= y+dy && y+dy < len(m) {
+			if x > 0 {
+				m[y+dy][x-1] = pos
+			}
+			if x+7 < len(m) {
+				m[y+dy][x+7] = pos
+			}
+		}
+	}
+	for dx := -1; dx < 8; dx++ {
+		if 0 <= x+dx && x+dx < len(m) {
+			if y > 0 {
+				m[y-1][x+dx] = pos
+			}
+			if y+7 < len(m) {
+				m[y+7][x+dx] = pos
+			}
+		}
+	}
+}
+
+// alignBox draw an alignment (small) box at upper left x, y.
+func alignBox(m [][]Pixel, x, y int) {
+	// box
+	align := Alignment.Pixel()
+	for dy := 0; dy < 5; dy++ {
+		for dx := 0; dx < 5; dx++ {
+			p := align
+			if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 {
+				p |= Black
+			}
+			m[y+dy][x+dx] = p
+		}
+	}
+}

+ 133 - 0
src/github.com/SKatiyar/qr/coding/qr_test.go

@@ -0,0 +1,133 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package coding
+
+import (
+	"bytes"
+	"testing"
+
+	"github.com/SKatiyar/qr/gf256"
+	"github.com/SKatiyar/qr/libqrencode"
+)
+
+func test(t *testing.T, v Version, l Level, text ...Encoding) bool {
+	s := ""
+	ty := libqrencode.EightBit
+	switch x := text[0].(type) {
+	case String:
+		s = string(x)
+	case Alpha:
+		s = string(x)
+		ty = libqrencode.Alphanumeric
+	case Num:
+		s = string(x)
+		ty = libqrencode.Numeric
+	}
+	key, err := libqrencode.Encode(libqrencode.Version(v), libqrencode.Level(l), ty, s)
+	if err != nil {
+		t.Errorf("libqrencode.Encode(%v, %v, %d, %#q): %v", v, l, ty, s, err)
+		return false
+	}
+	mask := (^key.Pixel[8][2]&1)<<2 | (key.Pixel[8][3]&1)<<1 | (^key.Pixel[8][4] & 1)
+	p, err := NewPlan(v, l, Mask(mask))
+	if err != nil {
+		t.Errorf("NewPlan(%v, L, %d): %v", v, err, mask)
+		return false
+	}
+	if len(p.Pixel) != len(key.Pixel) {
+		t.Errorf("%v: NewPlan uses %dx%d, libqrencode uses %dx%d", v, len(p.Pixel), len(p.Pixel), len(key.Pixel), len(key.Pixel))
+		return false
+	}
+	c, err := p.Encode(text...)
+	if err != nil {
+		t.Errorf("Encode: %v", err)
+		return false
+	}
+	badpix := 0
+Pixel:
+	for y, prow := range p.Pixel {
+		for x, pix := range prow {
+			pix &^= Black
+			if c.Black(x, y) {
+				pix |= Black
+			}
+
+			keypix := key.Pixel[y][x]
+			want := Pixel(0)
+			switch {
+			case keypix&libqrencode.Finder != 0:
+				want = Position.Pixel()
+			case keypix&libqrencode.Alignment != 0:
+				want = Alignment.Pixel()
+			case keypix&libqrencode.Timing != 0:
+				want = Timing.Pixel()
+			case keypix&libqrencode.Format != 0:
+				want = Format.Pixel()
+				want |= OffsetPixel(pix.Offset()) // sic
+				want |= pix & Invert
+			case keypix&libqrencode.PVersion != 0:
+				want = PVersion.Pixel()
+			case keypix&libqrencode.DataECC != 0:
+				if pix.Role() == Check || pix.Role() == Extra {
+					want = pix.Role().Pixel()
+				} else {
+					want = Data.Pixel()
+				}
+				want |= OffsetPixel(pix.Offset())
+				want |= pix & Invert
+			default:
+				want = Unused.Pixel()
+			}
+			if keypix&libqrencode.Black != 0 {
+				want |= Black
+			}
+			if pix != want {
+				t.Errorf("%v/%v: Pixel[%d][%d] = %v, want %v %#x", v, mask, y, x, pix, want, keypix)
+				if badpix++; badpix >= 100 {
+					t.Errorf("stopping after %d bad pixels", badpix)
+					break Pixel
+				}
+			}
+		}
+	}
+	return badpix == 0
+}
+
+var input = []Encoding{
+	String("hello"),
+	Num("1"),
+	Num("12"),
+	Num("123"),
+	Alpha("AB"),
+	Alpha("ABC"),
+}
+
+func TestVersion(t *testing.T) {
+	badvers := 0
+Version:
+	for v := Version(1); v <= 40; v++ {
+		for l := L; l <= H; l++ {
+			for _, in := range input {
+				if !test(t, v, l, in) {
+					if badvers++; badvers >= 10 {
+						t.Errorf("stopping after %d bad versions", badvers)
+						break Version
+					}
+				}
+			}
+		}
+	}
+}
+
+func TestEncode(t *testing.T) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
+	rs := gf256.NewRSEncoder(Field, len(check))
+	out := make([]byte, len(check))
+	rs.ECC(data, out)
+	if !bytes.Equal(out, check) {
+		t.Errorf("have %x want %x", out, check)
+	}
+}

+ 85 - 0
src/github.com/SKatiyar/qr/gf256/blog_test.go

@@ -0,0 +1,85 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains a straightforward implementation of
+// Reed-Solomon encoding, along with a benchmark.
+// It goes with http://research.swtch.com/field.
+//
+// For an optimized implementation, see gf256.go.
+
+package gf256
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+)
+
+// BlogECC writes to check the error correcting code bytes
+// for data using the given Reed-Solomon parameters.
+func BlogECC(rs *RSEncoder, m []byte, check []byte) {
+	if len(check) < rs.c {
+		panic("gf256: invalid check byte length")
+	}
+	if rs.c == 0 {
+		return
+	}
+
+	// The check bytes are the remainder after dividing
+	// data padded with c zeros by the generator polynomial.
+
+	// p = data padded with c zeros.
+	var p []byte
+	n := len(m) + rs.c
+	if len(rs.p) >= n {
+		p = rs.p
+	} else {
+		p = make([]byte, n)
+	}
+	copy(p, m)
+	for i := len(m); i < len(p); i++ {
+		p[i] = 0
+	}
+
+	gen := rs.gen
+
+	// Divide p by gen, leaving the remainder in p[len(data):].
+	// p[0] is the most significant term in p, and
+	// gen[0] is the most significant term in the generator.
+	for i := 0; i < len(m); i++ {
+		k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0
+		// p -= k·g
+		for j, g := range gen {
+			p[i+j] = f.Add(p[i+j], f.Mul(k, g))
+		}
+	}
+
+	copy(check, p[len(m):])
+	rs.p = p
+}
+
+func BenchmarkBlogECC(b *testing.B) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	for i := 0; i < b.N; i++ {
+		BlogECC(rs, data, out)
+	}
+	b.SetBytes(int64(len(data)))
+	if !bytes.Equal(out, check) {
+		fmt.Printf("have %#v want %#v\n", out, check)
+	}
+}
+
+func TestBlogECC(t *testing.T) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	BlogECC(rs, data, out)
+	if !bytes.Equal(out, check) {
+		t.Errorf("have %x want %x", out, check)
+	}
+}

+ 241 - 0
src/github.com/SKatiyar/qr/gf256/gf256.go

@@ -0,0 +1,241 @@
+// Copyright 2010 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gf256 implements arithmetic over the Galois Field GF(256).
+package gf256
+
+import "strconv"
+
+// A Field represents an instance of GF(256) defined by a specific polynomial.
+type Field struct {
+	log [256]byte // log[0] is unused
+	exp [510]byte
+}
+
+// NewField returns a new field corresponding to the polynomial poly
+// and generator α.  The Reed-Solomon encoding in QR codes uses
+// polynomial 0x11d with generator 2.
+//
+// The choice of generator α only affects the Exp and Log operations.
+func NewField(poly, α int) *Field {
+	if poly < 0x100 || poly >= 0x200 || reducible(poly) {
+		panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
+	}
+
+	var f Field
+	x := 1
+	for i := 0; i < 255; i++ {
+		if x == 1 && i != 0 {
+			panic("gf256: invalid generator " + strconv.Itoa(α) +
+				" for polynomial " + strconv.Itoa(poly))
+		}
+		f.exp[i] = byte(x)
+		f.exp[i+255] = byte(x)
+		f.log[x] = byte(i)
+		x = mul(x, α, poly)
+	}
+	f.log[0] = 255
+	for i := 0; i < 255; i++ {
+		if f.log[f.exp[i]] != byte(i) {
+			panic("bad log")
+		}
+		if f.log[f.exp[i+255]] != byte(i) {
+			panic("bad log")
+		}
+	}
+	for i := 1; i < 256; i++ {
+		if f.exp[f.log[i]] != byte(i) {
+			panic("bad log")
+		}
+	}
+
+	return &f
+}
+
+// nbit returns the number of significant in p.
+func nbit(p int) uint {
+	n := uint(0)
+	for ; p > 0; p >>= 1 {
+		n++
+	}
+	return n
+}
+
+// polyDiv divides the polynomial p by q and returns the remainder.
+func polyDiv(p, q int) int {
+	np := nbit(p)
+	nq := nbit(q)
+	for ; np >= nq; np-- {
+		if p&(1<<(np-1)) != 0 {
+			p ^= q << (np - nq)
+		}
+	}
+	return p
+}
+
+// mul returns the product x*y mod poly, a GF(256) multiplication.
+func mul(x, y, poly int) int {
+	z := 0
+	for x > 0 {
+		if x&1 != 0 {
+			z ^= y
+		}
+		x >>= 1
+		y <<= 1
+		if y&0x100 != 0 {
+			y ^= poly
+		}
+	}
+	return z
+}
+
+// reducible reports whether p is reducible.
+func reducible(p int) bool {
+	// Multiplying n-bit * n-bit produces (2n-1)-bit,
+	// so if p is reducible, one of its factors must be
+	// of np/2+1 bits or fewer.
+	np := nbit(p)
+	for q := 2; q < 1<<(np/2+1); q++ {
+		if polyDiv(p, q) == 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// Add returns the sum of x and y in the field.
+func (f *Field) Add(x, y byte) byte {
+	return x ^ y
+}
+
+// Exp returns the base-α exponential of e in the field.
+// If e < 0, Exp returns 0.
+func (f *Field) Exp(e int) byte {
+	if e < 0 {
+		return 0
+	}
+	return f.exp[e%255]
+}
+
+// Log returns the base-α logarithm of x in the field.
+// If x == 0, Log returns -1.
+func (f *Field) Log(x byte) int {
+	if x == 0 {
+		return -1
+	}
+	return int(f.log[x])
+}
+
+// Inv returns the multiplicative inverse of x in the field.
+// If x == 0, Inv returns 0.
+func (f *Field) Inv(x byte) byte {
+	if x == 0 {
+		return 0
+	}
+	return f.exp[255-f.log[x]]
+}
+
+// Mul returns the product of x and y in the field.
+func (f *Field) Mul(x, y byte) byte {
+	if x == 0 || y == 0 {
+		return 0
+	}
+	return f.exp[int(f.log[x])+int(f.log[y])]
+}
+
+// An RSEncoder implements Reed-Solomon encoding
+// over a given field using a given number of error correction bytes.
+type RSEncoder struct {
+	f    *Field
+	c    int
+	gen  []byte
+	lgen []byte
+	p    []byte
+}
+
+func (f *Field) gen(e int) (gen, lgen []byte) {
+	// p = 1
+	p := make([]byte, e+1)
+	p[e] = 1
+
+	for i := 0; i < e; i++ {
+		// p *= (x + Exp(i))
+		// p[j] = p[j]*Exp(i) + p[j+1].
+		c := f.Exp(i)
+		for j := 0; j < e; j++ {
+			p[j] = f.Mul(p[j], c) ^ p[j+1]
+		}
+		p[e] = f.Mul(p[e], c)
+	}
+
+	// lp = log p.
+	lp := make([]byte, e+1)
+	for i, c := range p {
+		if c == 0 {
+			lp[i] = 255
+		} else {
+			lp[i] = byte(f.Log(c))
+		}
+	}
+
+	return p, lp
+}
+
+// NewRSEncoder returns a new Reed-Solomon encoder
+// over the given field and number of error correction bytes.
+func NewRSEncoder(f *Field, c int) *RSEncoder {
+	gen, lgen := f.gen(c)
+	return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen}
+}
+
+// ECC writes to check the error correcting code bytes
+// for data using the given Reed-Solomon parameters.
+func (rs *RSEncoder) ECC(data []byte, check []byte) {
+	if len(check) < rs.c {
+		panic("gf256: invalid check byte length")
+	}
+	if rs.c == 0 {
+		return
+	}
+
+	// The check bytes are the remainder after dividing
+	// data padded with c zeros by the generator polynomial.
+
+	// p = data padded with c zeros.
+	var p []byte
+	n := len(data) + rs.c
+	if len(rs.p) >= n {
+		p = rs.p
+	} else {
+		p = make([]byte, n)
+	}
+	copy(p, data)
+	for i := len(data); i < len(p); i++ {
+		p[i] = 0
+	}
+
+	// Divide p by gen, leaving the remainder in p[len(data):].
+	// p[0] is the most significant term in p, and
+	// gen[0] is the most significant term in the generator,
+	// which is always 1.
+	// To avoid repeated work, we store various values as
+	// lv, not v, where lv = log[v].
+	f := rs.f
+	lgen := rs.lgen[1:]
+	for i := 0; i < len(data); i++ {
+		c := p[i]
+		if c == 0 {
+			continue
+		}
+		q := p[i+1:]
+		exp := f.exp[f.log[c]:]
+		for j, lg := range lgen {
+			if lg != 255 { // lgen uses 255 for log 0
+				q[j] ^= exp[lg]
+			}
+		}
+	}
+	copy(check, p[len(data):])
+	rs.p = p
+}

+ 194 - 0
src/github.com/SKatiyar/qr/gf256/gf256_test.go

@@ -0,0 +1,194 @@
+// Copyright 2010 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gf256
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+)
+
+var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1
+
+func TestBasic(t *testing.T) {
+	if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
+		panic("bad Exp")
+	}
+}
+
+func TestECC(t *testing.T) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	rs.ECC(data, out)
+	if !bytes.Equal(out, check) {
+		t.Errorf("have %x want %x", out, check)
+	}
+}
+
+func TestLinear(t *testing.T) {
+	d1 := []byte{0x00, 0x00}
+	c1 := []byte{0x00, 0x00}
+	out := make([]byte, len(c1))
+	rs := NewRSEncoder(f, len(c1))
+	if rs.ECC(d1, out); !bytes.Equal(out, c1) {
+		t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out)
+	}
+	d2 := []byte{0x00, 0x01}
+	c2 := make([]byte, 2)
+	rs.ECC(d2, c2)
+	d3 := []byte{0x00, 0x02}
+	c3 := make([]byte, 2)
+	rs.ECC(d3, c3)
+	cx := make([]byte, 2)
+	for i := range cx {
+		cx[i] = c2[i] ^ c3[i]
+	}
+	d4 := []byte{0x00, 0x03}
+	c4 := make([]byte, 2)
+	rs.ECC(d4, c4)
+	if !bytes.Equal(cx, c4) {
+		t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x",
+			d2, c2, d3, c3, cx, d4, c4)
+	}
+}
+
+func TestGaussJordan(t *testing.T) {
+	rs := NewRSEncoder(f, 2)
+	m := make([][]byte, 16)
+	for i := range m {
+		m[i] = make([]byte, 4)
+		m[i][i/8] = 1 << uint(i%8)
+		rs.ECC(m[i][:2], m[i][2:])
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x\n", row)
+		}
+	}
+	b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27}
+	for i := 0; i < 16; i++ {
+		bi := b[i]
+		if m[i][bi/8]&(1<<(7-bi%8)) == 0 {
+			for j := i + 1; ; j++ {
+				if j >= len(m) {
+					t.Errorf("lost track for %d", bi)
+					break
+				}
+				if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+					m[i], m[j] = m[j], m[i]
+					break
+				}
+			}
+		}
+		for j := i + 1; j < len(m); j++ {
+			if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+				for k := range m[j] {
+					m[j][k] ^= m[i][k]
+				}
+			}
+		}
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x\n", row)
+		}
+	}
+	for i := 15; i >= 0; i-- {
+		bi := b[i]
+		for j := i - 1; j >= 0; j-- {
+			if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
+				for k := range m[j] {
+					m[j][k] ^= m[i][k]
+				}
+			}
+		}
+	}
+	if false {
+		fmt.Printf("---\n")
+		for _, row := range m {
+			fmt.Printf("%x", row)
+			out := make([]byte, 2)
+			if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) {
+				fmt.Printf(" - want %x", out)
+			}
+			fmt.Printf("\n")
+		}
+	}
+}
+
+func BenchmarkECC(b *testing.B) {
+	data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
+	check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
+	out := make([]byte, len(check))
+	rs := NewRSEncoder(f, len(check))
+	for i := 0; i < b.N; i++ {
+		rs.ECC(data, out)
+	}
+	b.SetBytes(int64(len(data)))
+	if !bytes.Equal(out, check) {
+		fmt.Printf("have %#v want %#v\n", out, check)
+	}
+}
+
+func TestGen(t *testing.T) {
+	for i := 0; i < 256; i++ {
+		_, lg := f.gen(i)
+		if lg[0] != 0 {
+			t.Errorf("#%d: %x", i, lg)
+		}
+	}
+}
+
+func TestReducible(t *testing.T) {
+	var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037
+	for i, want := range count {
+		n := 0
+		for p := 1 << uint(i+2); p < 1<<uint(i+3); p++ {
+			if !reducible(p) {
+				n++
+			}
+		}
+		if n != want {
+			t.Errorf("#reducible(%d-bit) = %d, want %d", i+2, n, want)
+		}
+	}
+}
+
+func TestExhaustive(t *testing.T) {
+	for poly := 0x100; poly < 0x200; poly++ {
+		if reducible(poly) {
+			continue
+		}
+		α := 2
+		for !generates(α, poly) {
+			α++
+		}
+		f := NewField(poly, α)
+		for p := 0; p < 256; p++ {
+			for q := 0; q < 256; q++ {
+				fm := int(f.Mul(byte(p), byte(q)))
+				pm := mul(p, q, poly)
+				if fm != pm {
+					t.Errorf("NewField(%#x).Mul(%#x, %#x) = %#x, want %#x", poly, p, q, fm, pm)
+				}
+			}
+		}
+	}
+}
+
+func generates(α, poly int) bool {
+	x := α
+	for i := 0; i < 254; i++ {
+		if x == 1 {
+			return false
+		}
+		x = mul(x, α, poly)
+	}
+	return true
+}

+ 149 - 0
src/github.com/SKatiyar/qr/libqrencode/qrencode.go

@@ -0,0 +1,149 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package libqrencode wraps the C libqrencode library.
+// The qr package (in this package's parent directory)
+// does not use any C wrapping.  This code is here only
+// for use during that package's tests.
+package libqrencode
+
+/*
+#cgo LDFLAGS: -lqrencode
+#include <qrencode.h>
+*/
+import "C"
+
+import (
+	"fmt"
+	"image"
+	"image/color"
+	"unsafe"
+)
+
+type Version int
+
+type Mode int
+
+const (
+	Numeric      Mode = C.QR_MODE_NUM
+	Alphanumeric Mode = C.QR_MODE_AN
+	EightBit     Mode = C.QR_MODE_8
+)
+
+type Level int
+
+const (
+	L Level = C.QR_ECLEVEL_L
+	M Level = C.QR_ECLEVEL_M
+	Q Level = C.QR_ECLEVEL_Q
+	H Level = C.QR_ECLEVEL_H
+)
+
+type Pixel int
+
+const (
+	Black Pixel = 1 << iota
+	DataECC
+	Format
+	PVersion
+	Timing
+	Alignment
+	Finder
+	NonData
+)
+
+type Code struct {
+	Version int
+	Width   int
+	Pixel   [][]Pixel
+	Scale   int
+}
+
+func (*Code) ColorModel() color.Model {
+	return color.RGBAModel
+}
+
+func (c *Code) Bounds() image.Rectangle {
+	d := (c.Width + 8) * c.Scale
+	return image.Rect(0, 0, d, d)
+}
+
+var (
+	white  color.Color = color.RGBA{0xFF, 0xFF, 0xFF, 0xFF}
+	black  color.Color = color.RGBA{0x00, 0x00, 0x00, 0xFF}
+	blue   color.Color = color.RGBA{0x00, 0x00, 0x80, 0xFF}
+	red    color.Color = color.RGBA{0xFF, 0x40, 0x40, 0xFF}
+	yellow color.Color = color.RGBA{0xFF, 0xFF, 0x00, 0xFF}
+	gray   color.Color = color.RGBA{0x80, 0x80, 0x80, 0xFF}
+	green  color.Color = color.RGBA{0x22, 0x8B, 0x22, 0xFF}
+)
+
+func (c *Code) At(x, y int) color.Color {
+	x = x/c.Scale - 4
+	y = y/c.Scale - 4
+	if 0 <= x && x < c.Width && 0 <= y && y < c.Width {
+		switch p := c.Pixel[y][x]; {
+		case p&Black == 0:
+			// nothing
+		case p&DataECC != 0:
+			return black
+		case p&Format != 0:
+			return blue
+		case p&PVersion != 0:
+			return red
+		case p&Timing != 0:
+			return yellow
+		case p&Alignment != 0:
+			return gray
+		case p&Finder != 0:
+			return green
+		}
+	}
+	return white
+}
+
+type Chunk struct {
+	Mode Mode
+	Text string
+}
+
+func Encode(version Version, level Level, mode Mode, text string) (*Code, error) {
+	return EncodeChunk(version, level, Chunk{mode, text})
+}
+
+func EncodeChunk(version Version, level Level, chunk ...Chunk) (*Code, error) {
+	qi, err := C.QRinput_new2(C.int(version), C.QRecLevel(level))
+	if qi == nil {
+		return nil, fmt.Errorf("QRinput_new2: %v", err)
+	}
+	defer C.QRinput_free(qi)
+	for _, ch := range chunk {
+		data := []byte(ch.Text)
+		n, err := C.QRinput_append(qi, C.QRencodeMode(ch.Mode), C.int(len(data)), (*C.uchar)(&data[0]))
+		if n < 0 {
+			return nil, fmt.Errorf("QRinput_append %q: %v", data, err)
+		}
+	}
+
+	qc, err := C.QRcode_encodeInput(qi)
+	if qc == nil {
+		return nil, fmt.Errorf("QRinput_encodeInput: %v", err)
+	}
+
+	c := &Code{
+		Version: int(qc.version),
+		Width:   int(qc.width),
+		Scale:   16,
+	}
+	pix := make([]Pixel, c.Width*c.Width)
+	cdat := (*[1000 * 1000]byte)(unsafe.Pointer(qc.data))[:len(pix)]
+	for i := range pix {
+		pix[i] = Pixel(cdat[i])
+	}
+	c.Pixel = make([][]Pixel, c.Width)
+	for i := range c.Pixel {
+		c.Pixel[i] = pix[i*c.Width : (i+1)*c.Width]
+	}
+	return c, nil
+}

+ 400 - 0
src/github.com/SKatiyar/qr/png.go

@@ -0,0 +1,400 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qr
+
+// PNG writer for QR codes.
+
+import (
+	"bytes"
+	"encoding/binary"
+	"hash"
+	"hash/crc32"
+)
+
+// PNG returns a PNG image displaying the code.
+//
+// PNG uses a custom encoder tailored to QR codes.
+// Its compressed size is about 2x away from optimal,
+// but it runs about 20x faster than calling png.Encode
+// on c.Image().
+func (c *Code) PNG() []byte {
+	var p pngWriter
+	return p.encode(c)
+}
+
+type pngWriter struct {
+	tmp   [16]byte
+	wctmp [4]byte
+	buf   bytes.Buffer
+	zlib  bitWriter
+	crc   hash.Hash32
+}
+
+var pngHeader = []byte("\x89PNG\r\n\x1a\n")
+
+func (w *pngWriter) encode(c *Code) []byte {
+	scale := c.Scale
+	siz := c.Size
+
+	w.buf.Reset()
+
+	// Header
+	w.buf.Write(pngHeader)
+
+	// Header block
+	binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale))
+	binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale))
+	w.tmp[8] = 1 // 1-bit
+	w.tmp[9] = 0 // gray
+	w.tmp[10] = 0
+	w.tmp[11] = 0
+	w.tmp[12] = 0
+	w.writeChunk("IHDR", w.tmp[:13])
+
+	// Comment
+	w.writeChunk("tEXt", comment)
+
+	// Data
+	w.zlib.writeCode(c)
+	w.writeChunk("IDAT", w.zlib.bytes.Bytes())
+
+	// End
+	w.writeChunk("IEND", nil)
+
+	return w.buf.Bytes()
+}
+
+var comment = []byte("Software\x00QR-PNG http://qr.swtch.com/")
+
+func (w *pngWriter) writeChunk(name string, data []byte) {
+	if w.crc == nil {
+		w.crc = crc32.NewIEEE()
+	}
+	binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data)))
+	w.buf.Write(w.wctmp[0:4])
+	w.crc.Reset()
+	copy(w.wctmp[0:4], name)
+	w.buf.Write(w.wctmp[0:4])
+	w.crc.Write(w.wctmp[0:4])
+	w.buf.Write(data)
+	w.crc.Write(data)
+	crc := w.crc.Sum32()
+	binary.BigEndian.PutUint32(w.wctmp[0:4], crc)
+	w.buf.Write(w.wctmp[0:4])
+}
+
+func (b *bitWriter) writeCode(c *Code) {
+	const ftNone = 0
+
+	b.adler32.Reset()
+	b.bytes.Reset()
+	b.nbit = 0
+
+	scale := c.Scale
+	siz := c.Size
+
+	// zlib header
+	b.tmp[0] = 0x78
+	b.tmp[1] = 0
+	b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31)
+	b.bytes.Write(b.tmp[0:2])
+
+	// Start flate block.
+	b.writeBits(1, 1, false) // final block
+	b.writeBits(1, 2, false) // compressed, fixed Huffman tables
+
+	// White border.
+	// First row.
+	b.byte(ftNone)
+	n := (scale*(siz+8) + 7) / 8
+	b.byte(255)
+	b.repeat(n-1, 1)
+	// 4*scale rows total.
+	b.repeat((4*scale-1)*(1+n), 1+n)
+
+	for i := 0; i < 4*scale; i++ {
+		b.adler32.WriteNByte(ftNone, 1)
+		b.adler32.WriteNByte(255, n)
+	}
+
+	row := make([]byte, 1+n)
+	for y := 0; y < siz; y++ {
+		row[0] = ftNone
+		j := 1
+		var z uint8
+		nz := 0
+		for x := -4; x < siz+4; x++ {
+			// Raw data.
+			for i := 0; i < scale; i++ {
+				z <<= 1
+				if !c.Black(x, y) {
+					z |= 1
+				}
+				if nz++; nz == 8 {
+					row[j] = z
+					j++
+					nz = 0
+				}
+			}
+		}
+		if j < len(row) {
+			row[j] = z
+		}
+		for _, z := range row {
+			b.byte(z)
+		}
+
+		// Scale-1 copies.
+		b.repeat((scale-1)*(1+n), 1+n)
+
+		b.adler32.WriteN(row, scale)
+	}
+
+	// White border.
+	// First row.
+	b.byte(ftNone)
+	b.byte(255)
+	b.repeat(n-1, 1)
+	// 4*scale rows total.
+	b.repeat((4*scale-1)*(1+n), 1+n)
+
+	for i := 0; i < 4*scale; i++ {
+		b.adler32.WriteNByte(ftNone, 1)
+		b.adler32.WriteNByte(255, n)
+	}
+
+	// End of block.
+	b.hcode(256)
+	b.flushBits()
+
+	// adler32
+	binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32())
+	b.bytes.Write(b.tmp[0:4])
+}
+
+// A bitWriter is a write buffer for bit-oriented data like deflate.
+type bitWriter struct {
+	bytes bytes.Buffer
+	bit   uint32
+	nbit  uint
+
+	tmp     [4]byte
+	adler32 adigest
+}
+
+func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) {
+	// reverse, for huffman codes
+	if rev {
+		br := uint32(0)
+		for i := uint(0); i < nbit; i++ {
+			br |= ((bit >> i) & 1) << (nbit - 1 - i)
+		}
+		bit = br
+	}
+	b.bit |= bit << b.nbit
+	b.nbit += nbit
+	for b.nbit >= 8 {
+		b.bytes.WriteByte(byte(b.bit))
+		b.bit >>= 8
+		b.nbit -= 8
+	}
+}
+
+func (b *bitWriter) flushBits() {
+	if b.nbit > 0 {
+		b.bytes.WriteByte(byte(b.bit))
+		b.nbit = 0
+		b.bit = 0
+	}
+}
+
+func (b *bitWriter) hcode(v int) {
+	/*
+	   Lit Value    Bits        Codes
+	   ---------    ----        -----
+	     0 - 143     8          00110000 through
+	                            10111111
+	   144 - 255     9          110010000 through
+	                            111111111
+	   256 - 279     7          0000000 through
+	                            0010111
+	   280 - 287     8          11000000 through
+	                            11000111
+	*/
+	switch {
+	case v <= 143:
+		b.writeBits(uint32(v)+0x30, 8, true)
+	case v <= 255:
+		b.writeBits(uint32(v-144)+0x190, 9, true)
+	case v <= 279:
+		b.writeBits(uint32(v-256)+0, 7, true)
+	case v <= 287:
+		b.writeBits(uint32(v-280)+0xc0, 8, true)
+	default:
+		panic("invalid hcode")
+	}
+}
+
+func (b *bitWriter) byte(x byte) {
+	b.hcode(int(x))
+}
+
+func (b *bitWriter) codex(c int, val int, nx uint) {
+	b.hcode(c + val>>nx)
+	b.writeBits(uint32(val)&(1<<nx-1), nx, false)
+}
+
+func (b *bitWriter) repeat(n, d int) {
+	for ; n >= 258+3; n -= 258 {
+		b.repeat1(258, d)
+	}
+	if n > 258 {
+		// 258 < n < 258+3
+		b.repeat1(10, d)
+		b.repeat1(n-10, d)
+		return
+	}
+	if n < 3 {
+		panic("invalid flate repeat")
+	}
+	b.repeat1(n, d)
+}
+
+func (b *bitWriter) repeat1(n, d int) {
+	/*
+	        Extra               Extra               Extra
+	   Code Bits Length(s) Code Bits Lengths   Code Bits Length(s)
+	   ---- ---- ------     ---- ---- -------   ---- ---- -------
+	    257   0     3       267   1   15,16     277   4   67-82
+	    258   0     4       268   1   17,18     278   4   83-98
+	    259   0     5       269   2   19-22     279   4   99-114
+	    260   0     6       270   2   23-26     280   4  115-130
+	    261   0     7       271   2   27-30     281   5  131-162
+	    262   0     8       272   2   31-34     282   5  163-194
+	    263   0     9       273   3   35-42     283   5  195-226
+	    264   0    10       274   3   43-50     284   5  227-257
+	    265   1  11,12      275   3   51-58     285   0    258
+	    266   1  13,14      276   3   59-66
+	*/
+	switch {
+	case n <= 10:
+		b.codex(257, n-3, 0)
+	case n <= 18:
+		b.codex(265, n-11, 1)
+	case n <= 34:
+		b.codex(269, n-19, 2)
+	case n <= 66:
+		b.codex(273, n-35, 3)
+	case n <= 130:
+		b.codex(277, n-67, 4)
+	case n <= 257:
+		b.codex(281, n-131, 5)
+	case n == 258:
+		b.hcode(285)
+	default:
+		panic("invalid repeat length")
+	}
+
+	/*
+	        Extra           Extra               Extra
+	   Code Bits Dist  Code Bits   Dist     Code Bits Distance
+	   ---- ---- ----  ---- ----  ------    ---- ---- --------
+	     0   0    1     10   4     33-48    20    9   1025-1536
+	     1   0    2     11   4     49-64    21    9   1537-2048
+	     2   0    3     12   5     65-96    22   10   2049-3072
+	     3   0    4     13   5     97-128   23   10   3073-4096
+	     4   1   5,6    14   6    129-192   24   11   4097-6144
+	     5   1   7,8    15   6    193-256   25   11   6145-8192
+	     6   2   9-12   16   7    257-384   26   12  8193-12288
+	     7   2  13-16   17   7    385-512   27   12 12289-16384
+	     8   3  17-24   18   8    513-768   28   13 16385-24576
+	     9   3  25-32   19   8   769-1024   29   13 24577-32768
+	*/
+	if d <= 4 {
+		b.writeBits(uint32(d-1), 5, true)
+	} else if d <= 32768 {
+		nbit := uint(16)
+		for d <= 1<<(nbit-1) {
+			nbit--
+		}
+		v := uint32(d - 1)
+		v &^= 1 << (nbit - 1)      // top bit is implicit
+		code := uint32(2*nbit - 2) // second bit is low bit of code
+		code |= v >> (nbit - 2)
+		v &^= 1 << (nbit - 2)
+		b.writeBits(code, 5, true)
+		// rest of bits follow
+		b.writeBits(uint32(v), nbit-2, false)
+	} else {
+		panic("invalid repeat distance")
+	}
+}
+
+func (b *bitWriter) run(v byte, n int) {
+	if n == 0 {
+		return
+	}
+	b.byte(v)
+	if n-1 < 3 {
+		for i := 0; i < n-1; i++ {
+			b.byte(v)
+		}
+	} else {
+		b.repeat(n-1, 1)
+	}
+}
+
+type adigest struct {
+	a, b uint32
+}
+
+func (d *adigest) Reset() { d.a, d.b = 1, 0 }
+
+const amod = 65521
+
+func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) {
+	// TODO(rsc): 6g doesn't do magic multiplies for b %= amod,
+	// only for b = b%amod.
+
+	// invariant: a, b < amod
+	if pi == 0 {
+		b += uint32(n%amod) * a
+		b = b % amod
+		return a, b
+	}
+
+	// n times:
+	//	a += pi
+	//	b += a
+	// is same as
+	//	b += n*a + n*(n+1)/2*pi
+	//	a += n*pi
+	m := uint32(n)
+	b += (m % amod) * a
+	b = b % amod
+	b += (m * (m + 1) / 2) % amod * uint32(pi)
+	b = b % amod
+	a += (m % amod) * uint32(pi)
+	a = a % amod
+	return a, b
+}
+
+func afinish(a, b uint32) uint32 {
+	return b<<16 | a
+}
+
+func (d *adigest) WriteN(p []byte, n int) {
+	for i := 0; i < n; i++ {
+		for _, pi := range p {
+			d.a, d.b = aupdate(d.a, d.b, pi, 1)
+		}
+	}
+}
+
+func (d *adigest) WriteNByte(pi byte, n int) {
+	d.a, d.b = aupdate(d.a, d.b, pi, n)
+}
+
+func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) }

+ 56 - 0
src/github.com/SKatiyar/qr/png_test.go

@@ -0,0 +1,56 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package qr
+
+import (
+	"bytes"
+	"image/png"
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+func Test2File(t *testing.T) {
+	r, _ := Encode("http://www.baidu.com", L)
+	fi, _ := os.OpenFile("C:\\Users\\admin\\Desktop\\2.png", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0x666)
+	fi.Write(r.PNG())
+}
+
+func TestPNG(t *testing.T) {
+	c, err := Encode("http://www.baidu.com", H)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pngdat := c.PNG()
+	if true {
+		ioutil.WriteFile("x.png", pngdat, 0666)
+	}
+
+}
+
+func BenchmarkPNG(b *testing.B) {
+	c, err := Encode("0123456789012345678901234567890123456789", L)
+	if err != nil {
+		panic(err)
+	}
+	var bytes []byte
+	for i := 0; i < b.N; i++ {
+		bytes = c.PNG()
+	}
+	b.SetBytes(int64(len(bytes)))
+}
+
+func BenchmarkImagePNG(b *testing.B) {
+	c, err := Encode("0123456789012345678901234567890123456789", L)
+	if err != nil {
+		panic(err)
+	}
+	var buf bytes.Buffer
+	for i := 0; i < b.N; i++ {
+		buf.Reset()
+		png.Encode(&buf, c.Image())
+	}
+	b.SetBytes(int64(buf.Len()))
+}

+ 116 - 0
src/github.com/SKatiyar/qr/qr.go

@@ -0,0 +1,116 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package qr encodes QR codes.
+*/
+package qr
+
+import (
+	"errors"
+	"image"
+	"image/color"
+
+	"github.com/SKatiyar/qr/coding"
+)
+
+// A Level denotes a QR error correction level.
+// From least to most tolerant of errors, they are L, M, Q, H.
+type Level int
+
+const (
+	L Level = iota // 20% redundant
+	M              // 38% redundant
+	Q              // 55% redundant
+	H              // 65% redundant
+)
+
+// Encode returns an encoding of text at the given error correction level.
+func Encode(text string, level Level) (*Code, error) {
+	// Pick data encoding, smallest first.
+	// We could split the string and use different encodings
+	// but that seems like overkill for now.
+	var enc coding.Encoding
+	switch {
+	case coding.Num(text).Check() == nil:
+		enc = coding.Num(text)
+	case coding.Alpha(text).Check() == nil:
+		enc = coding.Alpha(text)
+	default:
+		enc = coding.String(text)
+	}
+
+	// Pick size.
+	l := coding.Level(level)
+	var v coding.Version
+	for v = coding.MinVersion; ; v++ {
+		if v > coding.MaxVersion {
+			return nil, errors.New("text too long to encode as QR")
+		}
+		if enc.Bits(v) <= v.DataBytes(l)*8 {
+			break
+		}
+	}
+
+	// Build and execute plan.
+	p, err := coding.NewPlan(v, l, 0)
+	if err != nil {
+		return nil, err
+	}
+	cc, err := p.Encode(enc)
+	if err != nil {
+		return nil, err
+	}
+
+	// TODO: Pick appropriate mask.
+
+	return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil
+}
+
+// A Code is a square pixel grid.
+// It implements image.Image and direct PNG encoding.
+type Code struct {
+	Bitmap []byte // 1 is black, 0 is white
+	Size   int    // number of pixels on a side
+	Stride int    // number of bytes per row
+	Scale  int    // number of image pixels per QR pixel
+}
+
+// Black returns true if the pixel at (x,y) is black.
+func (c *Code) Black(x, y int) bool {
+	return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
+		c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
+}
+
+// Image returns an Image displaying the code.
+func (c *Code) Image() image.Image {
+	return &codeImage{c}
+
+}
+
+// codeImage implements image.Image
+type codeImage struct {
+	*Code
+}
+
+var (
+	whiteColor color.Color = color.Gray{0xFF}
+	blackColor color.Color = color.Gray{0x00}
+)
+
+func (c *codeImage) Bounds() image.Rectangle {
+	d := (c.Size + 8) * c.Scale
+	return image.Rect(0, 0, d, d)
+}
+
+func (c *codeImage) At(x, y int) color.Color {
+	if c.Black(x, y) {
+		return blackColor
+	}
+	return whiteColor
+}
+
+func (c *codeImage) ColorModel() color.Model {
+	return color.GrayModel
+}

+ 503 - 0
src/github.com/SKatiyar/qr/web/pic.go

@@ -0,0 +1,503 @@
+package web
+
+import (
+	"bytes"
+	"fmt"
+	"image"
+	"image/color"
+	"image/draw"
+	"image/png"
+	"net/http"
+	"strconv"
+	"strings"
+
+	"github.com/SKatiyar/qr"
+	"github.com/SKatiyar/qr/coding"
+)
+
+func makeImage(req *http.Request, caption, font string, pt, size, border, scale int, f func(x, y int) uint32) *image.RGBA {
+	d := (size + 2*border) * scale
+	csize := 0
+	if caption != "" {
+		if pt == 0 {
+			pt = 11
+		}
+		csize = pt * 2
+	}
+	c := image.NewRGBA(image.Rect(0, 0, d, d+csize))
+
+	// white
+	u := &image.Uniform{C: color.White}
+	draw.Draw(c, c.Bounds(), u, image.ZP, draw.Src)
+
+	for y := 0; y < size; y++ {
+		for x := 0; x < size; x++ {
+			r := image.Rect((x+border)*scale, (y+border)*scale, (x+border+1)*scale, (y+border+1)*scale)
+			rgba := f(x, y)
+			u.C = color.RGBA{byte(rgba >> 24), byte(rgba >> 16), byte(rgba >> 8), byte(rgba)}
+			draw.Draw(c, r, u, image.ZP, draw.Src)
+		}
+	}
+
+	if csize != 0 {
+		if font == "" {
+			font = "data/luxisr.ttf"
+		}
+		ctxt := fs.NewContext(req)
+		dat, _, err := ctxt.Read(font)
+		if err != nil {
+			panic(err)
+		}
+		tfont, err := freetype.ParseFont(dat)
+		if err != nil {
+			panic(err)
+		}
+		ft := freetype.NewContext()
+		ft.SetDst(c)
+		ft.SetDPI(100)
+		ft.SetFont(tfont)
+		ft.SetFontSize(float64(pt))
+		ft.SetSrc(image.NewUniform(color.Black))
+		ft.SetClip(image.Rect(0, 0, 0, 0))
+		wid, err := ft.DrawString(caption, freetype.Pt(0, 0))
+		if err != nil {
+			panic(err)
+		}
+		p := freetype.Pt(d, d+3*pt/2)
+		p.X -= wid.X
+		p.X /= 2
+		ft.SetClip(c.Bounds())
+		ft.DrawString(caption, p)
+	}
+
+	return c
+}
+
+func makeFrame(req *http.Request, font string, pt, vers, l, scale, dots int) image.Image {
+	lev := coding.Level(l)
+	p, err := coding.NewPlan(coding.Version(vers), lev, 0)
+	if err != nil {
+		panic(err)
+	}
+
+	nd := p.DataBytes / p.Blocks
+	nc := p.CheckBytes / p.Blocks
+	extra := p.DataBytes - nd*p.Blocks
+
+	cap := fmt.Sprintf("QR v%d, %s", vers, lev)
+	if dots > 0 {
+		cap = fmt.Sprintf("QR v%d order, from bottom right", vers)
+	}
+	m := makeImage(req, cap, font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 {
+		pix := p.Pixel[y][x]
+		switch pix.Role() {
+		case coding.Data:
+			if dots > 0 {
+				return 0xffffffff
+			}
+			off := int(pix.Offset() / 8)
+			nd := nd
+			var i int
+			for i = 0; i < p.Blocks; i++ {
+				if i == extra {
+					nd++
+				}
+				if off < nd {
+					break
+				}
+				off -= nd
+			}
+			return blockColors[i%len(blockColors)]
+		case coding.Check:
+			if dots > 0 {
+				return 0xffffffff
+			}
+			i := (int(pix.Offset()/8) - p.DataBytes) / nc
+			return dark(blockColors[i%len(blockColors)])
+		}
+		if pix&coding.Black != 0 {
+			return 0x000000ff
+		}
+		return 0xffffffff
+	})
+
+	if dots > 0 {
+		b := m.Bounds()
+		for y := 0; y <= len(p.Pixel); y++ {
+			for x := 0; x < b.Dx(); x++ {
+				m.SetRGBA(x, y*scale-(y/len(p.Pixel)), color.RGBA{127, 127, 127, 255})
+			}
+		}
+		for x := 0; x <= len(p.Pixel); x++ {
+			for y := 0; y < b.Dx(); y++ {
+				m.SetRGBA(x*scale-(x/len(p.Pixel)), y, color.RGBA{127, 127, 127, 255})
+			}
+		}
+		order := make([]image.Point, (p.DataBytes+p.CheckBytes)*8+1)
+		for y, row := range p.Pixel {
+			for x, pix := range row {
+				if r := pix.Role(); r != coding.Data && r != coding.Check {
+					continue
+				}
+				//	draw.Draw(m, m.Bounds().Add(image.Pt(x*scale, y*scale)), dot, image.ZP, draw.Over)
+				order[pix.Offset()] = image.Point{x*scale + scale/2, y*scale + scale/2}
+			}
+		}
+
+		for mode := 0; mode < 2; mode++ {
+			for i, p := range order {
+				q := order[i+1]
+				if q.X == 0 {
+					break
+				}
+				line(m, p, q, mode)
+			}
+		}
+	}
+	return m
+}
+
+func line(m *image.RGBA, p, q image.Point, mode int) {
+	x := 0
+	y := 0
+	dx := q.X - p.X
+	dy := q.Y - p.Y
+	xsign := +1
+	ysign := +1
+	if dx < 0 {
+		xsign = -1
+		dx = -dx
+	}
+	if dy < 0 {
+		ysign = -1
+		dy = -dy
+	}
+	pt := func() {
+		switch mode {
+		case 0:
+			for dx := -2; dx <= 2; dx++ {
+				for dy := -2; dy <= 2; dy++ {
+					if dy*dx <= -4 || dy*dx >= 4 {
+						continue
+					}
+					m.SetRGBA(p.X+x*xsign+dx, p.Y+y*ysign+dy, color.RGBA{255, 192, 192, 255})
+				}
+			}
+
+		case 1:
+			m.SetRGBA(p.X+x*xsign, p.Y+y*ysign, color.RGBA{128, 0, 0, 255})
+		}
+	}
+	if dx > dy {
+		for x < dx || y < dy {
+			pt()
+			x++
+			if float64(x)*float64(dy)/float64(dx)-float64(y) > 0.5 {
+				y++
+			}
+		}
+	} else {
+		for x < dx || y < dy {
+			pt()
+			y++
+			if float64(y)*float64(dx)/float64(dy)-float64(x) > 0.5 {
+				x++
+			}
+		}
+	}
+	pt()
+}
+
+func pngEncode(c image.Image) []byte {
+	var b bytes.Buffer
+	png.Encode(&b, c)
+	return b.Bytes()
+}
+
+// Frame handles a request for a single QR frame.
+func Frame(w http.ResponseWriter, req *http.Request) {
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	v := arg("v")
+	scale := arg("scale")
+	if scale == 0 {
+		scale = 8
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(pngEncode(makeFrame(req, req.FormValue("font"), arg("pt"), v, arg("l"), scale, arg("dots"))))
+}
+
+// Frames handles a request for multiple QR frames.
+func Frames(w http.ResponseWriter, req *http.Request) {
+	vs := strings.Split(req.FormValue("v"), ",")
+
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	scale := arg("scale")
+	if scale == 0 {
+		scale = 8
+	}
+	font := req.FormValue("font")
+	pt := arg("pt")
+	dots := arg("dots")
+
+	var images []image.Image
+	l := arg("l")
+	for _, v := range vs {
+		l := l
+		if i := strings.Index(v, "."); i >= 0 {
+			l, _ = strconv.Atoi(v[i+1:])
+			v = v[:i]
+		}
+		vv, _ := strconv.Atoi(v)
+		images = append(images, makeFrame(req, font, pt, vv, l, scale, dots))
+	}
+
+	b := images[len(images)-1].Bounds()
+
+	dx := arg("dx")
+	if dx == 0 {
+		dx = b.Dx()
+	}
+	x, y := 0, 0
+	xmax := 0
+	sep := arg("sep")
+	if sep == 0 {
+		sep = 10
+	}
+	var points []image.Point
+	for i, m := range images {
+		if x > 0 {
+			x += sep
+		}
+		if x > 0 && x+m.Bounds().Dx() > dx {
+			y += sep + images[i-1].Bounds().Dy()
+			x = 0
+		}
+		points = append(points, image.Point{x, y})
+		x += m.Bounds().Dx()
+		if x > xmax {
+			xmax = x
+		}
+
+	}
+
+	c := image.NewRGBA(image.Rect(0, 0, xmax, y+b.Dy()))
+	for i, m := range images {
+		draw.Draw(c, c.Bounds().Add(points[i]), m, image.ZP, draw.Src)
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(pngEncode(c))
+}
+
+// Mask handles a request for a single QR mask.
+func Mask(w http.ResponseWriter, req *http.Request) {
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	v := arg("v")
+	m := arg("m")
+	scale := arg("scale")
+	if scale == 0 {
+		scale = 8
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(pngEncode(makeMask(req, req.FormValue("font"), arg("pt"), v, m, scale)))
+}
+
+// Masks handles a request for multiple QR masks.
+func Masks(w http.ResponseWriter, req *http.Request) {
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	v := arg("v")
+	scale := arg("scale")
+	if scale == 0 {
+		scale = 8
+	}
+	font := req.FormValue("font")
+	pt := arg("pt")
+	var mm []image.Image
+	for m := 0; m < 8; m++ {
+		mm = append(mm, makeMask(req, font, pt, v, m, scale))
+	}
+	dx := mm[0].Bounds().Dx()
+	dy := mm[0].Bounds().Dy()
+
+	sep := arg("sep")
+	if sep == 0 {
+		sep = 10
+	}
+	c := image.NewRGBA(image.Rect(0, 0, (dx+sep)*4-sep, (dy+sep)*2-sep))
+	for m := 0; m < 8; m++ {
+		x := (m % 4) * (dx + sep)
+		y := (m / 4) * (dy + sep)
+		draw.Draw(c, c.Bounds().Add(image.Pt(x, y)), mm[m], image.ZP, draw.Src)
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(pngEncode(c))
+}
+
+var maskName = []string{
+	"(x+y) % 2",
+	"y % 2",
+	"x % 3",
+	"(x+y) % 3",
+	"(y/2 + x/3) % 2",
+	"xy%2 + xy%3",
+	"(xy%2 + xy%3) % 2",
+	"(xy%3 + (x+y)%2) % 2",
+}
+
+func makeMask(req *http.Request, font string, pt int, vers, mask, scale int) image.Image {
+	p, err := coding.NewPlan(coding.Version(vers), coding.L, coding.Mask(mask))
+	if err != nil {
+		panic(err)
+	}
+	m := makeImage(req, maskName[mask], font, pt, len(p.Pixel), 0, scale, func(x, y int) uint32 {
+		pix := p.Pixel[y][x]
+		switch pix.Role() {
+		case coding.Data, coding.Check:
+			if pix&coding.Invert != 0 {
+				return 0x000000ff
+			}
+		}
+		return 0xffffffff
+	})
+	return m
+}
+
+var blockColors = []uint32{
+	0x7777ffff,
+	0xffff77ff,
+	0xff7777ff,
+	0x77ffffff,
+	0x1e90ffff,
+	0xffffe0ff,
+	0x8b6969ff,
+	0x77ff77ff,
+	0x9b30ffff,
+	0x00bfffff,
+	0x90e890ff,
+	0xfff68fff,
+	0xffec8bff,
+	0xffa07aff,
+	0xffa54fff,
+	0xeee8aaff,
+	0x98fb98ff,
+	0xbfbfbfff,
+	0x54ff9fff,
+	0xffaeb9ff,
+	0xb23aeeff,
+	0xbbffffff,
+	0x7fffd4ff,
+	0xff7a7aff,
+	0x00007fff,
+}
+
+func dark(x uint32) uint32 {
+	r, g, b, a := byte(x>>24), byte(x>>16), byte(x>>8), byte(x)
+	r = r/2 + r/4
+	g = g/2 + g/4
+	b = b/2 + b/4
+	return uint32(r)<<24 | uint32(g)<<16 | uint32(b)<<8 | uint32(a)
+}
+
+func clamp(x int) byte {
+	if x < 0 {
+		return 0
+	}
+	if x > 255 {
+		return 255
+	}
+	return byte(x)
+}
+
+func max(x, y int) int {
+	if x > y {
+		return x
+	}
+	return y
+}
+
+// Arrow handles a request for an arrow pointing in a given direction.
+func Arrow(w http.ResponseWriter, req *http.Request) {
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	dir := arg("dir")
+	size := arg("size")
+	if size == 0 {
+		size = 50
+	}
+	del := size / 10
+
+	m := image.NewRGBA(image.Rect(0, 0, size, size))
+
+	if dir == 4 {
+		draw.Draw(m, m.Bounds(), image.Black, image.ZP, draw.Src)
+		draw.Draw(m, image.Rect(5, 5, size-5, size-5), image.White, image.ZP, draw.Src)
+	}
+
+	pt := func(x, y int, c color.RGBA) {
+		switch dir {
+		case 0:
+			m.SetRGBA(x, y, c)
+		case 1:
+			m.SetRGBA(y, size-1-x, c)
+		case 2:
+			m.SetRGBA(size-1-x, size-1-y, c)
+		case 3:
+			m.SetRGBA(size-1-y, x, c)
+		}
+	}
+
+	for y := 0; y < size/2; y++ {
+		for x := 0; x < del && x < y; x++ {
+			pt(x, y, color.RGBA{0, 0, 0, 255})
+		}
+		for x := del; x < y-del; x++ {
+			pt(x, y, color.RGBA{128, 128, 255, 255})
+		}
+		for x := max(y-del, 0); x <= y; x++ {
+			pt(x, y, color.RGBA{0, 0, 0, 255})
+		}
+	}
+	for y := size / 2; y < size; y++ {
+		for x := 0; x < del && x < size-1-y; x++ {
+			pt(x, y, color.RGBA{0, 0, 0, 255})
+		}
+		for x := del; x < size-1-y-del; x++ {
+			pt(x, y, color.RGBA{128, 128, 192, 255})
+		}
+		for x := max(size-1-y-del, 0); x <= size-1-y; x++ {
+			pt(x, y, color.RGBA{0, 0, 0, 255})
+		}
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(pngEncode(m))
+}
+
+// Encode encodes a string using the given version, level, and mask.
+func Encode(w http.ResponseWriter, req *http.Request) {
+	val := func(s string) int {
+		v, _ := strconv.Atoi(req.FormValue(s))
+		return v
+	}
+
+	l := coding.Level(val("l"))
+	v := coding.Version(val("v"))
+	enc := coding.String(req.FormValue("t"))
+	m := coding.Mask(val("m"))
+
+	p, err := coding.NewPlan(v, l, m)
+	if err != nil {
+		panic(err)
+	}
+	cc, err := p.Encode(enc)
+	if err != nil {
+		panic(err)
+	}
+
+	c := &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: 8}
+	w.Header().Set("Content-Type", "image/png")
+	w.Header().Set("Cache-Control", "public, max-age=3600")
+	w.Write(c.PNG())
+}

+ 1118 - 0
src/github.com/SKatiyar/qr/web/play.go

@@ -0,0 +1,1118 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+QR data layout
+
+qr/
+	upload/
+		id.png
+		id.fix
+	flag/
+		id
+
+*/
+// TODO: Random seed taken from GET for caching, repeatability.
+// TODO: Flag for abuse button + some kind of dashboard.
+// TODO: +1 button on web page?  permalink?
+// TODO: Flag for abuse button on permalinks too?
+// TODO: Make the page prettier.
+// TODO: Cache headers.
+
+package web
+
+import (
+	"bytes"
+	"crypto/md5"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"html/template"
+	"image"
+	"image/color"
+	_ "image/gif"
+	_ "image/jpeg"
+	"image/png"
+	"io"
+	"math/rand"
+	"net/http"
+	"net/url"
+	"os"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+
+	"code.google.com/p/rsc/appfs/fs"
+	"code.google.com/p/rsc/gf256"
+	"code.google.com/p/rsc/qr"
+	"code.google.com/p/rsc/qr/coding"
+	"code.google.com/p/rsc/qr/web/resize"
+)
+
+func runTemplate(c *fs.Context, w http.ResponseWriter, name string, data interface{}) {
+	t := template.New("main")
+
+	main, _, err := c.Read(name)
+	if err != nil {
+		panic(err)
+	}
+	style, _, _ := c.Read("style.html")
+	main = append(main, style...)
+	_, err = t.Parse(string(main))
+	if err != nil {
+		panic(err)
+	}
+
+	var buf bytes.Buffer
+	if err := t.Execute(&buf, &data); err != nil {
+		panic(err)
+	}
+	w.Write(buf.Bytes())
+}
+
+func isImgName(s string) bool {
+	if len(s) != 32 {
+		return false
+	}
+	for i := 0; i < len(s); i++ {
+		if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' {
+			continue
+		}
+		return false
+	}
+	return true
+}
+
+func isTagName(s string) bool {
+	if len(s) != 16 {
+		return false
+	}
+	for i := 0; i < len(s); i++ {
+		if '0' <= s[i] && s[i] <= '9' || 'a' <= s[i] && s[i] <= 'f' {
+			continue
+		}
+		return false
+	}
+	return true
+}
+
+// Draw is the handler for drawing a QR code.
+func Draw(w http.ResponseWriter, req *http.Request) {
+	ctxt := fs.NewContext(req)
+
+	url := req.FormValue("url")
+	if url == "" {
+		url = "http://swtch.com/qr"
+	}
+	if req.FormValue("upload") == "1" {
+		upload(w, req, url)
+		return
+	}
+
+	t0 := time.Now()
+	img := req.FormValue("i")
+	if !isImgName(img) {
+		img = "pjw"
+	}
+	if req.FormValue("show") == "png" {
+		i := loadSize(ctxt, img, 48)
+		var buf bytes.Buffer
+		png.Encode(&buf, i)
+		w.Write(buf.Bytes())
+		return
+	}
+	if req.FormValue("flag") == "1" {
+		flag(w, req, img, ctxt)
+		return
+	}
+	if req.FormValue("x") == "" {
+		var data = struct {
+			Name string
+			URL  string
+		}{
+			Name: img,
+			URL:  url,
+		}
+		runTemplate(ctxt, w, "qr/main.html", &data)
+		return
+	}
+
+	arg := func(s string) int { x, _ := strconv.Atoi(req.FormValue(s)); return x }
+	targ := makeTarg(ctxt, img, 17+4*arg("v")+arg("z"))
+
+	m := &Image{
+		Name:         img,
+		Dx:           arg("x"),
+		Dy:           arg("y"),
+		URL:          req.FormValue("u"),
+		Version:      arg("v"),
+		Mask:         arg("m"),
+		RandControl:  arg("r") > 0,
+		Dither:       arg("i") > 0,
+		OnlyDataBits: arg("d") > 0,
+		SaveControl:  arg("c") > 0,
+		Scale:        arg("scale"),
+		Target:       targ,
+		Seed:         int64(arg("s")),
+		Rotation:     arg("o"),
+		Size:         arg("z"),
+	}
+	if m.Version > 8 {
+		m.Version = 8
+	}
+
+	if m.Scale == 0 {
+		if arg("l") > 1 {
+			m.Scale = 8
+		} else {
+			m.Scale = 4
+		}
+	}
+	if m.Version >= 12 && m.Scale >= 4 {
+		m.Scale /= 2
+	}
+
+	if arg("l") == 1 {
+		data, err := json.Marshal(m)
+		if err != nil {
+			panic(err)
+		}
+		h := md5.New()
+		h.Write(data)
+		tag := fmt.Sprintf("%x", h.Sum(nil))[:16]
+		if err := ctxt.Write("qrsave/"+tag, data); err != nil {
+			panic(err)
+		}
+		http.Redirect(w, req, "/qr/show/"+tag, http.StatusTemporaryRedirect)
+		return
+	}
+
+	if err := m.Encode(req); err != nil {
+		fmt.Fprintf(w, "%s\n", err)
+		return
+	}
+
+	var dat []byte
+	switch {
+	case m.SaveControl:
+		dat = m.Control
+	default:
+		dat = m.Code.PNG()
+	}
+
+	if arg("l") > 0 {
+		w.Header().Set("Content-Type", "image/png")
+		w.Write(dat)
+		return
+	}
+
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+	fmt.Fprint(w, "<center><img src=\"data:image/png;base64,")
+	io.WriteString(w, base64.StdEncoding.EncodeToString(dat))
+	fmt.Fprint(w, "\" /><br>")
+	fmt.Fprintf(w, "<form method=\"POST\" action=\"%s&l=1\"><input type=\"submit\" value=\"Save this QR code\"></form>\n", m.Link())
+	fmt.Fprintf(w, "</center>\n")
+	fmt.Fprintf(w, "<br><center><font size=-1>%v</font></center>\n", time.Now().Sub(t0))
+}
+
+func (m *Image) Small() bool {
+	return 8*(17+4*int(m.Version)) < 512
+}
+
+func (m *Image) Link() string {
+	s := fmt.Sprint
+	b := func(v bool) string {
+		if v {
+			return "1"
+		}
+		return "0"
+	}
+	val := url.Values{
+		"i": {m.Name},
+		"x": {s(m.Dx)},
+		"y": {s(m.Dy)},
+		"z": {s(m.Size)},
+		"u": {m.URL},
+		"v": {s(m.Version)},
+		"m": {s(m.Mask)},
+		"r": {b(m.RandControl)},
+		"t": {b(m.Dither)},
+		"d": {b(m.OnlyDataBits)},
+		"c": {b(m.SaveControl)},
+		"s": {s(m.Seed)},
+	}
+	return "/qr/draw?" + val.Encode()
+}
+
+// Show is the handler for showing a stored QR code.
+func Show(w http.ResponseWriter, req *http.Request) {
+	ctxt := fs.NewContext(req)
+	tag := req.URL.Path[len("/qr/show/"):]
+	png := strings.HasSuffix(tag, ".png")
+	if png {
+		tag = tag[:len(tag)-len(".png")]
+	}
+	if !isTagName(tag) {
+		fmt.Fprintf(w, "Sorry, QR code not found\n")
+		return
+	}
+	if req.FormValue("flag") == "1" {
+		flag(w, req, tag, ctxt)
+		return
+	}
+	data, _, err := ctxt.Read("qrsave/" + tag)
+	if err != nil {
+		fmt.Fprintf(w, "Sorry, QR code not found.\n")
+		return
+	}
+
+	var m Image
+	if err := json.Unmarshal(data, &m); err != nil {
+		panic(err)
+	}
+	m.Tag = tag
+
+	switch req.FormValue("size") {
+	case "big":
+		m.Scale *= 2
+	case "small":
+		m.Scale /= 2
+	}
+
+	if png {
+		if err := m.Encode(req); err != nil {
+			panic(err)
+			return
+		}
+		w.Header().Set("Cache-Control", "public, max-age=3600")
+		w.Write(m.Code.PNG())
+		return
+	}
+
+	w.Header().Set("Cache-Control", "public, max-age=300")
+	runTemplate(ctxt, w, "qr/permalink.html", &m)
+}
+
+func upload(w http.ResponseWriter, req *http.Request, link string) {
+	// Upload of a new image.
+	// Copied from Moustachio demo.
+	f, _, err := req.FormFile("image")
+	if err != nil {
+		fmt.Fprintf(w, "You need to select an image to upload.\n")
+		return
+	}
+	defer f.Close()
+
+	i, _, err := image.Decode(f)
+	if err != nil {
+		panic(err)
+	}
+
+	// Convert image to 128x128 gray+alpha.
+	b := i.Bounds()
+	const max = 128
+	// If it's gigantic, it's more efficient to downsample first
+	// and then resize; resizing will smooth out the roughness.
+	var i1 *image.RGBA
+	if b.Dx() > 4*max || b.Dy() > 4*max {
+		w, h := 2*max, 2*max
+		if b.Dx() > b.Dy() {
+			h = b.Dy() * h / b.Dx()
+		} else {
+			w = b.Dx() * w / b.Dy()
+		}
+		i1 = resize.Resample(i, b, w, h)
+	} else {
+		// "Resample" to same size, just to convert to RGBA.
+		i1 = resize.Resample(i, b, b.Dx(), b.Dy())
+	}
+	b = i1.Bounds()
+
+	// Encode to PNG.
+	dx, dy := 128, 128
+	if b.Dx() > b.Dy() {
+		dy = b.Dy() * dx / b.Dx()
+	} else {
+		dx = b.Dx() * dy / b.Dy()
+	}
+	i128 := resize.ResizeRGBA(i1, i1.Bounds(), dx, dy)
+
+	var buf bytes.Buffer
+	if err := png.Encode(&buf, i128); err != nil {
+		panic(err)
+	}
+
+	h := md5.New()
+	h.Write(buf.Bytes())
+	tag := fmt.Sprintf("%x", h.Sum(nil))[:32]
+
+	ctxt := fs.NewContext(req)
+	if err := ctxt.Write("qr/upload/"+tag+".png", buf.Bytes()); err != nil {
+		panic(err)
+	}
+
+	// Redirect with new image tag.
+	// Redirect to draw with new image tag.
+	http.Redirect(w, req, req.URL.Path+"?"+url.Values{"i": {tag}, "url": {link}}.Encode(), 302)
+}
+
+func flag(w http.ResponseWriter, req *http.Request, img string, ctxt *fs.Context) {
+	if !isImgName(img) && !isTagName(img) {
+		fmt.Fprintf(w, "Invalid image.\n")
+		return
+	}
+	data, _, _ := ctxt.Read("qr/flag/" + img)
+	data = append(data, '!')
+	ctxt.Write("qr/flag/"+img, data)
+
+	fmt.Fprintf(w, "Thank you.  The image has been reported.\n")
+}
+
+func loadSize(ctxt *fs.Context, name string, max int) *image.RGBA {
+	data, _, err := ctxt.Read("qr/upload/" + name + ".png")
+	if err != nil {
+		panic(err)
+	}
+	i, _, err := image.Decode(bytes.NewBuffer(data))
+	if err != nil {
+		panic(err)
+	}
+	b := i.Bounds()
+	dx, dy := max, max
+	if b.Dx() > b.Dy() {
+		dy = b.Dy() * dx / b.Dx()
+	} else {
+		dx = b.Dx() * dy / b.Dy()
+	}
+	var irgba *image.RGBA
+	switch i := i.(type) {
+	case *image.RGBA:
+		irgba = resize.ResizeRGBA(i, i.Bounds(), dx, dy)
+	case *image.NRGBA:
+		irgba = resize.ResizeNRGBA(i, i.Bounds(), dx, dy)
+	}
+	return irgba
+}
+
+func makeTarg(ctxt *fs.Context, name string, max int) [][]int {
+	i := loadSize(ctxt, name, max)
+	b := i.Bounds()
+	dx, dy := b.Dx(), b.Dy()
+	targ := make([][]int, dy)
+	arr := make([]int, dx*dy)
+	for y := 0; y < dy; y++ {
+		targ[y], arr = arr[:dx], arr[dx:]
+		row := targ[y]
+		for x := 0; x < dx; x++ {
+			p := i.Pix[y*i.Stride+4*x:]
+			r, g, b, a := p[0], p[1], p[2], p[3]
+			if a == 0 {
+				row[x] = -1
+			} else {
+				row[x] = int((299*uint32(r) + 587*uint32(g) + 114*uint32(b) + 500) / 1000)
+			}
+		}
+	}
+	return targ
+}
+
+type Image struct {
+	Name     string
+	Target   [][]int
+	Dx       int
+	Dy       int
+	URL      string
+	Tag      string
+	Version  int
+	Mask     int
+	Scale    int
+	Rotation int
+	Size     int
+
+	// RandControl says to pick the pixels randomly.
+	RandControl bool
+	Seed        int64
+
+	// Dither says to dither instead of using threshold pixel layout.
+	Dither bool
+
+	// OnlyDataBits says to use only data bits, not check bits.
+	OnlyDataBits bool
+
+	// Code is the final QR code.
+	Code *qr.Code
+
+	// Control is a PNG showing the pixels that we controlled.
+	// Pixels we don't control are grayed out.
+	SaveControl bool
+	Control     []byte
+}
+
+type Pixinfo struct {
+	X        int
+	Y        int
+	Pix      coding.Pixel
+	Targ     byte
+	DTarg    int
+	Contrast int
+	HardZero bool
+	Block    *BitBlock
+	Bit      uint
+}
+
+type Pixorder struct {
+	Off      int
+	Priority int
+}
+
+type byPriority []Pixorder
+
+func (x byPriority) Len() int           { return len(x) }
+func (x byPriority) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x byPriority) Less(i, j int) bool { return x[i].Priority > x[j].Priority }
+
+func (m *Image) target(x, y int) (targ byte, contrast int) {
+	tx := x + m.Dx
+	ty := y + m.Dy
+	if ty < 0 || ty >= len(m.Target) || tx < 0 || tx >= len(m.Target[ty]) {
+		return 255, -1
+	}
+
+	v0 := m.Target[ty][tx]
+	if v0 < 0 {
+		return 255, -1
+	}
+	targ = byte(v0)
+
+	n := 0
+	sum := 0
+	sumsq := 0
+	const del = 5
+	for dy := -del; dy <= del; dy++ {
+		for dx := -del; dx <= del; dx++ {
+			if 0 <= ty+dy && ty+dy < len(m.Target) && 0 <= tx+dx && tx+dx < len(m.Target[ty+dy]) {
+				v := m.Target[ty+dy][tx+dx]
+				sum += v
+				sumsq += v * v
+				n++
+			}
+		}
+	}
+
+	avg := sum / n
+	contrast = sumsq/n - avg*avg
+	return
+}
+
+func (m *Image) rotate(p *coding.Plan, rot int) {
+	if rot == 0 {
+		return
+	}
+
+	N := len(p.Pixel)
+	pix := make([][]coding.Pixel, N)
+	apix := make([]coding.Pixel, N*N)
+	for i := range pix {
+		pix[i], apix = apix[:N], apix[N:]
+	}
+
+	switch rot {
+	case 0:
+		// ok
+	case 1:
+		for y := 0; y < N; y++ {
+			for x := 0; x < N; x++ {
+				pix[y][x] = p.Pixel[x][N-1-y]
+			}
+		}
+	case 2:
+		for y := 0; y < N; y++ {
+			for x := 0; x < N; x++ {
+				pix[y][x] = p.Pixel[N-1-y][N-1-x]
+			}
+		}
+	case 3:
+		for y := 0; y < N; y++ {
+			for x := 0; x < N; x++ {
+				pix[y][x] = p.Pixel[N-1-x][y]
+			}
+		}
+	}
+
+	p.Pixel = pix
+}
+
+func (m *Image) Encode(req *http.Request) error {
+	p, err := coding.NewPlan(coding.Version(m.Version), coding.L, coding.Mask(m.Mask))
+	if err != nil {
+		return err
+	}
+
+	m.rotate(p, m.Rotation)
+
+	rand := rand.New(rand.NewSource(m.Seed))
+
+	// QR parameters.
+	nd := p.DataBytes / p.Blocks
+	nc := p.CheckBytes / p.Blocks
+	extra := p.DataBytes - nd*p.Blocks
+	rs := gf256.NewRSEncoder(coding.Field, nc)
+
+	// Build information about pixels, indexed by data/check bit number.
+	pixByOff := make([]Pixinfo, (p.DataBytes+p.CheckBytes)*8)
+	expect := make([][]bool, len(p.Pixel))
+	for y, row := range p.Pixel {
+		expect[y] = make([]bool, len(row))
+		for x, pix := range row {
+			targ, contrast := m.target(x, y)
+			if m.RandControl && contrast >= 0 {
+				contrast = rand.Intn(128) + 64*((x+y)%2) + 64*((x+y)%3%2)
+			}
+			expect[y][x] = pix&coding.Black != 0
+			if r := pix.Role(); r == coding.Data || r == coding.Check {
+				pixByOff[pix.Offset()] = Pixinfo{X: x, Y: y, Pix: pix, Targ: targ, Contrast: contrast}
+			}
+		}
+	}
+
+Again:
+	// Count fixed initial data bits, prepare template URL.
+	url := m.URL + "#"
+	var b coding.Bits
+	coding.String(url).Encode(&b, p.Version)
+	coding.Num("").Encode(&b, p.Version)
+	bbit := b.Bits()
+	dbit := p.DataBytes*8 - bbit
+	if dbit < 0 {
+		return fmt.Errorf("cannot encode URL into available bits")
+	}
+	num := make([]byte, dbit/10*3)
+	for i := range num {
+		num[i] = '0'
+	}
+	b.Pad(dbit)
+	b.Reset()
+	coding.String(url).Encode(&b, p.Version)
+	coding.Num(num).Encode(&b, p.Version)
+	b.AddCheckBytes(p.Version, p.Level)
+	data := b.Bytes()
+
+	doff := 0 // data offset
+	coff := 0 // checksum offset
+	mbit := bbit + dbit/10*10
+
+	// Choose pixels.
+	bitblocks := make([]*BitBlock, p.Blocks)
+	for blocknum := 0; blocknum < p.Blocks; blocknum++ {
+		if blocknum == p.Blocks-extra {
+			nd++
+		}
+
+		bdata := data[doff/8 : doff/8+nd]
+		cdata := data[p.DataBytes+coff/8 : p.DataBytes+coff/8+nc]
+		bb := newBlock(nd, nc, rs, bdata, cdata)
+		bitblocks[blocknum] = bb
+
+		// Determine which bits in this block we can try to edit.
+		lo, hi := 0, nd*8
+		if lo < bbit-doff {
+			lo = bbit - doff
+			if lo > hi {
+				lo = hi
+			}
+		}
+		if hi > mbit-doff {
+			hi = mbit - doff
+			if hi < lo {
+				hi = lo
+			}
+		}
+
+		// Preserve [0, lo) and [hi, nd*8).
+		for i := 0; i < lo; i++ {
+			if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) {
+				return fmt.Errorf("cannot preserve required bits")
+			}
+		}
+		for i := hi; i < nd*8; i++ {
+			if !bb.canSet(uint(i), (bdata[i/8]>>uint(7-i&7))&1) {
+				return fmt.Errorf("cannot preserve required bits")
+			}
+		}
+
+		// Can edit [lo, hi) and checksum bits to hit target.
+		// Determine which ones to try first.
+		order := make([]Pixorder, (hi-lo)+nc*8)
+		for i := lo; i < hi; i++ {
+			order[i-lo].Off = doff + i
+		}
+		for i := 0; i < nc*8; i++ {
+			order[hi-lo+i].Off = p.DataBytes*8 + coff + i
+		}
+		if m.OnlyDataBits {
+			order = order[:hi-lo]
+		}
+		for i := range order {
+			po := &order[i]
+			po.Priority = pixByOff[po.Off].Contrast<<8 | rand.Intn(256)
+		}
+		sort.Sort(byPriority(order))
+
+		const mark = false
+		for i := range order {
+			po := &order[i]
+			pinfo := &pixByOff[po.Off]
+			bval := pinfo.Targ
+			if bval < 128 {
+				bval = 1
+			} else {
+				bval = 0
+			}
+			pix := pinfo.Pix
+			if pix&coding.Invert != 0 {
+				bval ^= 1
+			}
+			if pinfo.HardZero {
+				bval = 0
+			}
+
+			var bi int
+			if pix.Role() == coding.Data {
+				bi = po.Off - doff
+			} else {
+				bi = po.Off - p.DataBytes*8 - coff + nd*8
+			}
+			if bb.canSet(uint(bi), bval) {
+				pinfo.Block = bb
+				pinfo.Bit = uint(bi)
+				if mark {
+					p.Pixel[pinfo.Y][pinfo.X] = coding.Black
+				}
+			} else {
+				if pinfo.HardZero {
+					panic("hard zero")
+				}
+				if mark {
+					p.Pixel[pinfo.Y][pinfo.X] = 0
+				}
+			}
+		}
+		bb.copyOut()
+
+		const cheat = false
+		for i := 0; i < nd*8; i++ {
+			pinfo := &pixByOff[doff+i]
+			pix := p.Pixel[pinfo.Y][pinfo.X]
+			if bb.B[i/8]&(1<<uint(7-i&7)) != 0 {
+				pix ^= coding.Black
+			}
+			expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0
+			if cheat {
+				p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black
+			}
+		}
+		for i := 0; i < nc*8; i++ {
+			pinfo := &pixByOff[p.DataBytes*8+coff+i]
+			pix := p.Pixel[pinfo.Y][pinfo.X]
+			if bb.B[nd+i/8]&(1<<uint(7-i&7)) != 0 {
+				pix ^= coding.Black
+			}
+			expect[pinfo.Y][pinfo.X] = pix&coding.Black != 0
+			if cheat {
+				p.Pixel[pinfo.Y][pinfo.X] = pix & coding.Black
+			}
+		}
+		doff += nd * 8
+		coff += nc * 8
+	}
+
+	// Pass over all pixels again, dithering.
+	if m.Dither {
+		for i := range pixByOff {
+			pinfo := &pixByOff[i]
+			pinfo.DTarg = int(pinfo.Targ)
+		}
+		for y, row := range p.Pixel {
+			for x, pix := range row {
+				if pix.Role() != coding.Data && pix.Role() != coding.Check {
+					continue
+				}
+				pinfo := &pixByOff[pix.Offset()]
+				if pinfo.Block == nil {
+					// did not choose this pixel
+					continue
+				}
+
+				pix := pinfo.Pix
+
+				pval := byte(1) // pixel value (black)
+				v := 0          // gray value (black)
+				targ := pinfo.DTarg
+				if targ >= 128 {
+					// want white
+					pval = 0
+					v = 255
+				}
+
+				bval := pval // bit value
+				if pix&coding.Invert != 0 {
+					bval ^= 1
+				}
+				if pinfo.HardZero && bval != 0 {
+					bval ^= 1
+					pval ^= 1
+					v ^= 255
+				}
+
+				// Set pixel value as we want it.
+				pinfo.Block.reset(pinfo.Bit, bval)
+
+				_, _ = x, y
+
+				err := targ - v
+				if x+1 < len(row) {
+					addDither(pixByOff, row[x+1], err*7/16)
+				}
+				if false && y+1 < len(p.Pixel) {
+					if x > 0 {
+						addDither(pixByOff, p.Pixel[y+1][x-1], err*3/16)
+					}
+					addDither(pixByOff, p.Pixel[y+1][x], err*5/16)
+					if x+1 < len(row) {
+						addDither(pixByOff, p.Pixel[y+1][x+1], err*1/16)
+					}
+				}
+			}
+		}
+
+		for _, bb := range bitblocks {
+			bb.copyOut()
+		}
+	}
+
+	noops := 0
+	// Copy numbers back out.
+	for i := 0; i < dbit/10; i++ {
+		// Pull out 10 bits.
+		v := 0
+		for j := 0; j < 10; j++ {
+			bi := uint(bbit + 10*i + j)
+			v <<= 1
+			v |= int((data[bi/8] >> (7 - bi&7)) & 1)
+		}
+		// Turn into 3 digits.
+		if v >= 1000 {
+			// Oops - too many 1 bits.
+			// We know the 512, 256, 128, 64, 32 bits are all set.
+			// Pick one at random to clear.  This will break some
+			// checksum bits, but so be it.
+			println("oops", i, v)
+			pinfo := &pixByOff[bbit+10*i+3] // TODO random
+			pinfo.Contrast = 1e9 >> 8
+			pinfo.HardZero = true
+			noops++
+		}
+		num[i*3+0] = byte(v/100 + '0')
+		num[i*3+1] = byte(v/10%10 + '0')
+		num[i*3+2] = byte(v%10 + '0')
+	}
+	if noops > 0 {
+		goto Again
+	}
+
+	var b1 coding.Bits
+	coding.String(url).Encode(&b1, p.Version)
+	coding.Num(num).Encode(&b1, p.Version)
+	b1.AddCheckBytes(p.Version, p.Level)
+	if !bytes.Equal(b.Bytes(), b1.Bytes()) {
+		fmt.Printf("mismatch\n%d %x\n%d %x\n", len(b.Bytes()), b.Bytes(), len(b1.Bytes()), b1.Bytes())
+		panic("byte mismatch")
+	}
+
+	cc, err := p.Encode(coding.String(url), coding.Num(num))
+	if err != nil {
+		return err
+	}
+
+	if !m.Dither {
+		for y, row := range expect {
+			for x, pix := range row {
+				if cc.Black(x, y) != pix {
+					println("mismatch", x, y, p.Pixel[y][x].String())
+				}
+			}
+		}
+	}
+
+	m.Code = &qr.Code{Bitmap: cc.Bitmap, Size: cc.Size, Stride: cc.Stride, Scale: m.Scale}
+
+	if m.SaveControl {
+		m.Control = pngEncode(makeImage(req, "", "", 0, cc.Size, 4, m.Scale, func(x, y int) (rgba uint32) {
+			pix := p.Pixel[y][x]
+			if pix.Role() == coding.Data || pix.Role() == coding.Check {
+				pinfo := &pixByOff[pix.Offset()]
+				if pinfo.Block != nil {
+					if cc.Black(x, y) {
+						return 0x000000ff
+					}
+					return 0xffffffff
+				}
+			}
+			if cc.Black(x, y) {
+				return 0x3f3f3fff
+			}
+			return 0xbfbfbfff
+		}))
+	}
+
+	return nil
+}
+
+func addDither(pixByOff []Pixinfo, pix coding.Pixel, err int) {
+	if pix.Role() != coding.Data && pix.Role() != coding.Check {
+		return
+	}
+	pinfo := &pixByOff[pix.Offset()]
+	println("add", pinfo.X, pinfo.Y, pinfo.DTarg, err)
+	pinfo.DTarg += err
+}
+
+func readTarget(name string) ([][]int, error) {
+	f, err := os.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	m, err := png.Decode(f)
+	if err != nil {
+		return nil, fmt.Errorf("decode %s: %v", name, err)
+	}
+	rect := m.Bounds()
+	target := make([][]int, rect.Dy())
+	for i := range target {
+		target[i] = make([]int, rect.Dx())
+	}
+	for y, row := range target {
+		for x := range row {
+			a := int(color.RGBAModel.Convert(m.At(x, y)).(color.RGBA).A)
+			t := int(color.GrayModel.Convert(m.At(x, y)).(color.Gray).Y)
+			if a == 0 {
+				t = -1
+			}
+			row[x] = t
+		}
+	}
+	return target, nil
+}
+
+type BitBlock struct {
+	DataBytes  int
+	CheckBytes int
+	B          []byte
+	M          [][]byte
+	Tmp        []byte
+	RS         *gf256.RSEncoder
+	bdata      []byte
+	cdata      []byte
+}
+
+func newBlock(nd, nc int, rs *gf256.RSEncoder, dat, cdata []byte) *BitBlock {
+	b := &BitBlock{
+		DataBytes:  nd,
+		CheckBytes: nc,
+		B:          make([]byte, nd+nc),
+		Tmp:        make([]byte, nc),
+		RS:         rs,
+		bdata:      dat,
+		cdata:      cdata,
+	}
+	copy(b.B, dat)
+	rs.ECC(b.B[:nd], b.B[nd:])
+	b.check()
+	if !bytes.Equal(b.Tmp, cdata) {
+		panic("cdata")
+	}
+
+	b.M = make([][]byte, nd*8)
+	for i := range b.M {
+		row := make([]byte, nd+nc)
+		b.M[i] = row
+		for j := range row {
+			row[j] = 0
+		}
+		row[i/8] = 1 << (7 - uint(i%8))
+		rs.ECC(row[:nd], row[nd:])
+	}
+	return b
+}
+
+func (b *BitBlock) check() {
+	b.RS.ECC(b.B[:b.DataBytes], b.Tmp)
+	if !bytes.Equal(b.B[b.DataBytes:], b.Tmp) {
+		fmt.Printf("ecc mismatch\n%x\n%x\n", b.B[b.DataBytes:], b.Tmp)
+		panic("mismatch")
+	}
+}
+
+func (b *BitBlock) reset(bi uint, bval byte) {
+	if (b.B[bi/8]>>(7-bi&7))&1 == bval {
+		// already has desired bit
+		return
+	}
+	// rows that have already been set
+	m := b.M[len(b.M):cap(b.M)]
+	for _, row := range m {
+		if row[bi/8]&(1<<(7-bi&7)) != 0 {
+			// Found it.
+			for j, v := range row {
+				b.B[j] ^= v
+			}
+			return
+		}
+	}
+	panic("reset of unset bit")
+}
+
+func (b *BitBlock) canSet(bi uint, bval byte) bool {
+	found := false
+	m := b.M
+	for j, row := range m {
+		if row[bi/8]&(1<<(7-bi&7)) == 0 {
+			continue
+		}
+		if !found {
+			found = true
+			if j != 0 {
+				m[0], m[j] = m[j], m[0]
+			}
+			continue
+		}
+		for k := range row {
+			row[k] ^= m[0][k]
+		}
+	}
+	if !found {
+		return false
+	}
+
+	targ := m[0]
+
+	// Subtract from saved-away rows too.
+	for _, row := range m[len(m):cap(m)] {
+		if row[bi/8]&(1<<(7-bi&7)) == 0 {
+			continue
+		}
+		for k := range row {
+			row[k] ^= targ[k]
+		}
+	}
+
+	// Found a row with bit #bi == 1 and cut that bit from all the others.
+	// Apply to data and remove from m.
+	if (b.B[bi/8]>>(7-bi&7))&1 != bval {
+		for j, v := range targ {
+			b.B[j] ^= v
+		}
+	}
+	b.check()
+	n := len(m) - 1
+	m[0], m[n] = m[n], m[0]
+	b.M = m[:n]
+
+	for _, row := range b.M {
+		if row[bi/8]&(1<<(7-bi&7)) != 0 {
+			panic("did not reduce")
+		}
+	}
+
+	return true
+}
+
+func (b *BitBlock) copyOut() {
+	b.check()
+	copy(b.bdata, b.B[:b.DataBytes])
+	copy(b.cdata, b.B[b.DataBytes:])
+}
+
+func showtable(w http.ResponseWriter, b *BitBlock, gray func(int) bool) {
+	nd := b.DataBytes
+	nc := b.CheckBytes
+
+	fmt.Fprintf(w, "<table class='matrix' cellspacing=0 cellpadding=0 border=0>\n")
+	line := func() {
+		fmt.Fprintf(w, "<tr height=1 bgcolor='#bbbbbb'><td colspan=%d>\n", (nd+nc)*8)
+	}
+	line()
+	dorow := func(row []byte) {
+		fmt.Fprintf(w, "<tr>\n")
+		for i := 0; i < (nd+nc)*8; i++ {
+			fmt.Fprintf(w, "<td")
+			v := row[i/8] >> uint(7-i&7) & 1
+			if gray(i) {
+				fmt.Fprintf(w, " class='gray'")
+			}
+			fmt.Fprintf(w, ">")
+			if v == 1 {
+				fmt.Fprintf(w, "1")
+			}
+		}
+		line()
+	}
+
+	m := b.M[len(b.M):cap(b.M)]
+	for i := len(m) - 1; i >= 0; i-- {
+		dorow(m[i])
+	}
+	m = b.M
+	for _, row := range b.M {
+		dorow(row)
+	}
+
+	fmt.Fprintf(w, "</table>\n")
+}
+
+func BitsTable(w http.ResponseWriter, req *http.Request) {
+	nd := 2
+	nc := 2
+	fmt.Fprintf(w, `<html>
+		<style type='text/css'>
+		.matrix {
+			font-family: sans-serif;
+			font-size: 0.8em;
+		}
+		table.matrix {
+			padding-left: 1em;
+			padding-right: 1em;
+			padding-top: 1em;
+			padding-bottom: 1em;
+		}
+		.matrix td {
+			padding-left: 0.3em;
+			padding-right: 0.3em;
+			border-left: 2px solid white;
+			border-right: 2px solid white;
+			text-align: center;
+			color: #aaa;
+		}
+		.matrix td.gray {
+			color: black;
+			background-color: #ddd;
+		}
+		</style>
+	`)
+	rs := gf256.NewRSEncoder(coding.Field, nc)
+	dat := make([]byte, nd+nc)
+	b := newBlock(nd, nc, rs, dat[:nd], dat[nd:])
+	for i := 0; i < nd*8; i++ {
+		b.canSet(uint(i), 0)
+	}
+	showtable(w, b, func(i int) bool { return i < nd*8 })
+
+	b = newBlock(nd, nc, rs, dat[:nd], dat[nd:])
+	for j := 0; j < (nd+nc)*8; j += 2 {
+		b.canSet(uint(j), 0)
+	}
+	showtable(w, b, func(i int) bool { return i%2 == 0 })
+
+}

+ 152 - 0
src/github.com/SKatiyar/qr/web/resize/resize.go

@@ -0,0 +1,152 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package resize
+
+import (
+	"image"
+	"image/color"
+)
+
+// average convert the sums to averages and returns the result.
+func average(sum []uint64, w, h int, n uint64) *image.RGBA {
+	ret := image.NewRGBA(image.Rect(0, 0, w, h))
+	for y := 0; y < h; y++ {
+		for x := 0; x < w; x++ {
+			index := 4 * (y*w + x)
+			pix := ret.Pix[y*ret.Stride+x*4:]
+			pix[0] = uint8(sum[index+0] / n)
+			pix[1] = uint8(sum[index+1] / n)
+			pix[2] = uint8(sum[index+2] / n)
+			pix[3] = uint8(sum[index+3] / n)
+		}
+	}
+	return ret
+}
+
+// ResizeRGBA returns a scaled copy of the RGBA image slice r of m.
+// The returned image has width w and height h.
+func ResizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) *image.RGBA {
+	ww, hh := uint64(w), uint64(h)
+	dx, dy := uint64(r.Dx()), uint64(r.Dy())
+	// See comment in Resize.
+	n, sum := dx*dy, make([]uint64, 4*w*h)
+	for y := r.Min.Y; y < r.Max.Y; y++ {
+		pix := m.Pix[(y-r.Min.Y)*m.Stride:]
+		for x := r.Min.X; x < r.Max.X; x++ {
+			// Get the source pixel.
+			p := pix[(x-r.Min.X)*4:]
+			r64 := uint64(p[0])
+			g64 := uint64(p[1])
+			b64 := uint64(p[2])
+			a64 := uint64(p[3])
+			// Spread the source pixel over 1 or more destination rows.
+			py := uint64(y) * hh
+			for remy := hh; remy > 0; {
+				qy := dy - (py % dy)
+				if qy > remy {
+					qy = remy
+				}
+				// Spread the source pixel over 1 or more destination columns.
+				px := uint64(x) * ww
+				index := 4 * ((py/dy)*ww + (px / dx))
+				for remx := ww; remx > 0; {
+					qx := dx - (px % dx)
+					if qx > remx {
+						qx = remx
+					}
+					qxy := qx * qy
+					sum[index+0] += r64 * qxy
+					sum[index+1] += g64 * qxy
+					sum[index+2] += b64 * qxy
+					sum[index+3] += a64 * qxy
+					index += 4
+					px += qx
+					remx -= qx
+				}
+				py += qy
+				remy -= qy
+			}
+		}
+	}
+	return average(sum, w, h, n)
+}
+
+// ResizeNRGBA returns a scaled copy of the RGBA image slice r of m.
+// The returned image has width w and height h.
+func ResizeNRGBA(m *image.NRGBA, r image.Rectangle, w, h int) *image.RGBA {
+	ww, hh := uint64(w), uint64(h)
+	dx, dy := uint64(r.Dx()), uint64(r.Dy())
+	// See comment in Resize.
+	n, sum := dx*dy, make([]uint64, 4*w*h)
+	for y := r.Min.Y; y < r.Max.Y; y++ {
+		pix := m.Pix[(y-r.Min.Y)*m.Stride:]
+		for x := r.Min.X; x < r.Max.X; x++ {
+			// Get the source pixel.
+			p := pix[(x-r.Min.X)*4:]
+			r64 := uint64(p[0])
+			g64 := uint64(p[1])
+			b64 := uint64(p[2])
+			a64 := uint64(p[3])
+			r64 = (r64 * a64) / 255
+			g64 = (g64 * a64) / 255
+			b64 = (b64 * a64) / 255
+			// Spread the source pixel over 1 or more destination rows.
+			py := uint64(y) * hh
+			for remy := hh; remy > 0; {
+				qy := dy - (py % dy)
+				if qy > remy {
+					qy = remy
+				}
+				// Spread the source pixel over 1 or more destination columns.
+				px := uint64(x) * ww
+				index := 4 * ((py/dy)*ww + (px / dx))
+				for remx := ww; remx > 0; {
+					qx := dx - (px % dx)
+					if qx > remx {
+						qx = remx
+					}
+					qxy := qx * qy
+					sum[index+0] += r64 * qxy
+					sum[index+1] += g64 * qxy
+					sum[index+2] += b64 * qxy
+					sum[index+3] += a64 * qxy
+					index += 4
+					px += qx
+					remx -= qx
+				}
+				py += qy
+				remy -= qy
+			}
+		}
+	}
+	return average(sum, w, h, n)
+}
+
+// Resample returns a resampled copy of the image slice r of m.
+// The returned image has width w and height h.
+func Resample(m image.Image, r image.Rectangle, w, h int) *image.RGBA {
+	if w < 0 || h < 0 {
+		return nil
+	}
+	if w == 0 || h == 0 || r.Dx() <= 0 || r.Dy() <= 0 {
+		return image.NewRGBA(image.Rect(0, 0, w, h))
+	}
+	curw, curh := r.Dx(), r.Dy()
+	img := image.NewRGBA(image.Rect(0, 0, w, h))
+	for y := 0; y < h; y++ {
+		for x := 0; x < w; x++ {
+			// Get a source pixel.
+			subx := x * curw / w
+			suby := y * curh / h
+			r32, g32, b32, a32 := m.At(subx, suby).RGBA()
+			r := uint8(r32 >> 8)
+			g := uint8(g32 >> 8)
+			b := uint8(b32 >> 8)
+			a := uint8(a32 >> 8)
+			img.SetRGBA(x, y, color.RGBA{r, g, b, a})
+		}
+	}
+	return img
+}

+ 29 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/.appveyor.yml

@@ -0,0 +1,29 @@
+
+    
+build: off
+
+clone_folder: c:\gopath\src\github.com\aliyun\alibaba-cloud-sdk-go
+
+environment:
+  GOPATH: c:\gopath
+  matrix:
+    - go: 1.10.x
+    - go: 1.11.x
+    - go: 1.12.x
+
+platform:
+  - x64
+
+install:
+  - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
+
+test_script:
+  - go vet ./sdk
+  - go vet ./services/...
+  - go build ./sdk
+  - go build ./services/...
+  - go test -race -coverprofile=coverage.txt -covermode=atomic ./sdk/...
+  - IF DEFINED ACCESS_KEY_ID (go test -v -timeout 120s ./integration/...)
+
+after_test:
+  - bash <(curl -s https://codecov.io/bash)

+ 24 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/.github/ISSUE_TEMPLATE/Bug_Report.md

@@ -0,0 +1,24 @@
+---
+name: "Bug Report"
+about: Create a report to help us improve
+---
+
+<!--
+Thank you for reporting a possible bug in Alibaba Cloud GO SDK
+Please fill in as much of the template below as you can.
+
+Product and API: the product and API you are working on when bug occurs
+Platform: output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
+How to Reproduce: If possible, please provide code that demonstrates the problem,
+keeping it as simple and free of external dependencies as you can.
+
+If crash, please provide the stack trace.
+
+If build error, please provide compiler information: compiler and version, etc
+-->
+
+* **Product and API**:
+* **Platform**:
+* **How to Reproduce**
+
+<!-- Please provide more details below this comment. -->

+ 23 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/.github/ISSUE_TEMPLATE/Bug_Report_zh.md

@@ -0,0 +1,23 @@
+---
+name: "缺陷问题反馈"
+about: 提交缺陷问题反馈
+
+---
+
+<!--
+感谢提交问题反馈。
+请提供尽量全面的信息协助问题定位修复。
+
+产品和接口:问题发生时工作所在的产品和调用的API
+最小代码: 如果可能,请提供一份最小问题复现代码
+平台:操作系统信息,类型,版本
+
+如果崩溃,请提供错误栈。
+如果编译出错,请提供 cmake 版本,编译器版本,编译命令等信息。
+-->
+
+* **产品和接口**:
+* **平台**:
+* **最小代码**:
+
+<!-- 请提供其他可能协助问题定位的信息 -->

+ 27 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/.travis.yml

@@ -0,0 +1,27 @@
+language: go
+
+go:
+  - 1.10.x
+  - 1.11.x
+  - 1.12.x
+
+branches: # build only on these branches
+  only:
+    - master
+
+notifications:
+    webhooks: https://oapi.dingtalk.com/robot/send?access_token=096ed387df243a6d60835aadeccc47165f3813bc7cb81cdd0cfeadfd28e3acc1
+    email: false
+    on_success: change
+    on_failure: always
+
+script:
+  - go vet ./sdk
+  - go vet ./services/...
+  - go build ./sdk
+  - go build ./services/...
+  - go test -race -coverprofile=coverage.txt -covermode=atomic ./sdk/...
+  - test -z $ACCESS_KEY_ID -a -z $ACCESS_KEY_SECRET || go test -v -timeout 120s ./integration/...
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)

+ 15 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/CONTRIBUTING.md

@@ -0,0 +1,15 @@
+# Contributing to the Alibaba Cloud SDK for Go
+
+We work hard to provide a high-quality and useful SDK for Alibaba Cloud, and we greatly value feedback and contributions from our community. Please submit your [issues][issues] or [pull requests][pull-requests] through GitHub.
+
+## Tips
+
+- The SDK is released under the [Apache license][license]. Any code you submit will be released under that license. For substantial contributions, we may ask you to sign a [Alibaba Documentation Corporate Contributor License Agreement (CLA)][cla].
+- We maintain a high percentage of code coverage in our unit tests. If you make changes to the code, please add, update, and/or remove tests as appropriate.
+- If your code does not conform to the standards, does not include adequate tests, we may ask you to update your pull requests before we accept them. We also reserve the right to deny any pull requests that do not align with our standards or goals.
+
+
+[issues]: https://github.com/aliyun/alibaba-cloud-sdk-go/issues
+[pull-requests]: https://github.com/aliyun/alibaba-cloud-sdk-go/pulls
+[license]: http://www.apache.org/licenses/LICENSE-2.0
+[cla]: https://alibaba-cla-2018.oss-cn-beijing.aliyuncs.com/Alibaba_Documentation_Open_Source_Corporate_CLA.pdf

+ 3969 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/ChangeLog.txt

@@ -0,0 +1,3969 @@
+2020-04-30 Version: v1.61.170
+- Generated 2019-10-23 for `Sls`.
+- App intefaces.
+
+2020-04-30 Version: v1.61.169
+- Generated 2018-06-13 for `Sls`.
+- App intefaces.
+
+2020-04-30 Version: v1.61.168
+- Generated 2019-08-08 for `ARMS`.
+- Add prometheus ListDashboards api.
+
+2020-04-29 Version: v1.61.167
+- Generated 2019-08-08 for `ARMS`.
+- Add prometheus ListDashboards api.
+
+2020-04-29 Version: v1.61.166
+- Generated 2019-08-08 for `ARMS`.
+- Build 20190808 release sdk.
+
+2020-04-29 Version: v1.61.165
+- Generated 2017-09-06 for `imm`.
+- Supported VideoProduce.
+
+2020-04-29 Version: v1.61.164
+- Generated 2017-09-06 for `imm`.
+- Supported VideoProduce.
+
+2020-04-29 Version: v1.61.163
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-29 Version: v1.61.162
+- Generated 2019-06-01 for `oos`.
+- Add ListTemplateVersions API.
+- GenerateExecutionPolicy supports TemplateVersion.
+- CreateTemplate and UpdateTemplate support VersionName.
+
+
+2020-04-29 Version: v1.61.161
+- Generated 2020-04-20 for `OnsMqtt`.
+- Support groupId operations.
+
+2020-04-29 Version: v1.61.160
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add ExportTas apis.
+
+2020-04-29 Version: v1.61.159
+- Generated 2018-10-12 for `alimt`.
+- Support Document Translation.
+- Support Lanuage Detection.
+
+2020-04-29 Version: v1.61.158
+- Generated 2018-01-15 for `dcdn`.
+- Sync cdn APIs.
+
+2020-04-29 Version: v1.61.157
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-29 Version: v1.61.156
+- Generated 2017-11-15 for `scdn`.
+- Add Scdn APIS.
+- Sync cdn APIS.
+
+2020-04-28 Version: v1.61.155
+- Generated 2019-03-07 for `Cloudauth`.
+- Add CompareFaceVerify API.
+
+2020-04-28 Version: v1.61.154
+- Generated 2019-03-15 for `fnf`.
+
+
+2020-04-28 Version: v1.61.153
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-27 Version: v1.61.152
+- Generated 2017-09-06 for `imm`.
+- Support watermark.
+
+2020-04-27 Version: v1.61.151
+- Generated 2019-01-01 for `HBase`.
+- Describe describeAvailableResource.
+
+2020-04-27 Version: v1.61.150
+- Generated 2019-03-07 for `Cloudauth`.
+- ContrastFaceVerify Return SubCode.
+
+2020-04-27 Version: v1.61.149
+- Generated 2019-10-10 for `LRG`.
+- New api.
+
+2020-04-27 Version: v1.61.148
+- Generated 2019-08-08 for `ARMS`.
+- Build 20190808 release sdk.
+
+2020-04-27 Version: v1.61.147
+- Generated 2019-08-08 for `ARMS`.
+- Build 20190808 release sdk.
+
+2020-04-27 Version: v1.61.146
+- Generated 2019-01-01 for `Cassandra`.
+
+
+2020-04-27 Version: v1.61.145
+- Generated 2020-03-31 for `ResourceManager`.
+- The first open version.
+
+2020-04-26 Version: v1.61.144
+- Generated 2019-10-23 for `Sls`.
+- App intefaces.
+
+2020-04-26 Version: v1.61.143
+- Generated 2020-03-26 for `Workorder`.
+- New api publish.
+
+2020-04-25 Version: v1.61.142
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Add RestoreTime for CreateInstance API.
+
+2020-04-24 Version: v1.61.141
+- Generated 2019-08-10 for `multimediaai`.
+- Template update.
+
+2020-04-24 Version: v1.61.140
+- Generated 2019-11-22 for `ledgerdb`.
+- Publish apis for ledger instances.
+- Publish apis for members.
+- Publish apis for time anchors.
+- Publish apis for endpoints.
+
+2020-04-24 Version: v1.61.139
+- Generated 2019-11-22 for `ledgerdb`.
+- Publish apis for ledger instances.
+- Publish apis for members.
+- Publish apis for time anchors.
+- Publish apis for endpoints.
+
+2020-04-23 Version: v1.61.138
+- Generated 2018-11-01 for `dms-enterprise`.
+- GetUser API return user execute query count information.
+
+2020-04-23 Version: v1.61.137
+- Generated 2016-08-01 for `Push`.
+- Fixed bugs for MassPush API.
+
+2020-04-23 Version: v1.61.136
+- Generated 2020-01-01 for `geoip`.
+- Add location info.
+
+2020-04-23 Version: v1.61.135
+- Generated 2020-04-20 for `OnsMqtt`.
+- Support groupId operations.
+
+2020-04-22 Version: v1.61.134
+- Generated 2018-11-01 for `dms-enterprise`.
+
+
+2020-04-22 Version: v1.61.133
+- Generated 2019-01-01 for `Cassandra`.
+
+
+2020-04-22 Version: v1.61.132
+- Generated 2019-01-01 for `Cassandra`.
+
+
+2020-04-21 Version: v1.61.131
+- Generated 2020-02-25 for `videosearch`.
+- VideoSearch deploy SDK.
+
+2020-04-21 Version: v1.61.130
+- Generated 2015-01-09 for `Alidns`.
+- Supported API for DescribeTags.
+- Supported API for ListTagResources.
+- Supported API for TagResources.
+- Supported API for UntagResources.
+
+2020-04-21 Version: v1.61.129
+- Generated 2019-06-25 for `ivpd`.
+- Supported PackageDesign Apis.
+- Supported Api ListPackageDesignModelTypes.
+- Supported Api PreviewModelForPackageDesign.
+- Supported Api RenderImageForPackageDesign.
+- Supported Api GetRenderResult.
+
+2020-04-21 Version: v1.61.128
+- Generated 2017-11-18 for `CSB`.
+- Add Statistics API.
+
+2020-04-20 Version: v1.61.127
+- Generated 2014-06-18 for `Mts`.
+- Add TemplateId.
+
+2020-04-20 Version: v1.61.126
+- Generated 2019-03-07 for `Cloudauth`.
+- Fix FaceContrastPicture.
+
+2020-04-20 Version: v1.61.125
+- Generated 2017-06-13 for `elasticsearch`.
+- Add kibana network white Ips.
+
+2020-04-19 Version: v1.61.124
+- Generated 2019-08-10 for `multimediaai`.
+- Multimedia poc modified.
+
+2020-04-17 Version: v1.61.123
+- Generated 2014-05-26 for `Ecs`.
+- DescribeInstanceTypes support TotalEniQueueQuantity.
+
+2020-04-16 Version: v1.61.122
+- Generated 2014-06-18 for `Mts`.
+- Modify `SubmitSmarttagJob`.
+
+2020-04-16 Version: v1.61.121
+- Generated 2017-06-13 for `elasticsearch`.
+- Add ModifyWhiteIps.
+
+2020-04-16 Version: v1.61.120
+- Generated 2014-05-26 for `Ecs`.
+- Add BatchOptimization param in Stop and Start Instances.
+- Add RemoveSymbols in GetInstanceConsoleOutput.
+- Add ImageFamily in ModifyImageAttribute.
+- Customize InstanceType on DedicatedHosts is supported.
+- Add StorageCapacityUnit interfaces.
+- Add param Tag in CreateAutoSnapshotPolicy.
+
+2020-04-16 Version: v1.61.119
+- Generated 2017-07-05 for `CCC`.
+
+
+2020-04-16 Version: v1.61.118
+- Generated 2017-07-05 for `CCC`.
+- Add features for open api.
+
+2020-04-16 Version: v1.61.117
+- Generated 2017-08-01 for `Edas`.
+- Increase the elastic expansion pop interface.
+- Increase the service method list pop interface.
+
+2020-04-15 Version: v1.61.116
+- Generated 2019-11-11 for `nlp-automl`.
+- Add pre train service api.
+
+2020-04-15 Version: v1.61.115
+- Generated 2015-12-01 for `Dds`.
+- CreateNode API add return NodeId.
+
+2020-04-15 Version: v1.61.114
+- Generated 2020-04-08 for `visionai-poc`.
+- Vision-poc response modified.
+
+2020-04-14 Version: v1.61.113
+- Generated 2019-06-25 for `ivpd`.
+- Supported CreateSegmentBodyJob.
+
+2020-04-13 Version: v1.61.112
+- Generated 2017-06-26 for `NAS`.
+
+2020-04-13 Version: v1.61.111
+- Generated 2019-06-01 for `oos`.
+- Fix ListTaskExecutions SDK error.
+
+2020-04-13 Version: v1.61.110
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-11 Version: v1.61.109
+- Generated 2019-08-08 for `ARMS`.
+
+
+2020-04-10 Version: v1.61.108
+- Generated 2017-09-06 for `imm`.
+- Support GetOfficeEditURL.
+- Support RefreshOfficeEditToken.
+
+2020-04-10 Version: v1.61.107
+- Generated 2020-01-01 for `geoip`.
+- Support DescribeIpv4Location.
+
+2020-04-09 Version: v1.61.106
+- Generated 2019-08-08 for `ARMS`.
+- Add prometheus api AddGrafana and AddIntegration.
+
+2020-04-09 Version: v1.61.105
+- Generated 2017-06-26 for `NAS`.
+
+2020-04-09 Version: v1.61.104
+- Generated 2017-08-01 for `polardb`.
+
+
+2020-04-09 Version: v1.61.103
+- Generated 2018-03-13 for `retailcloud`.
+
+
+2020-04-09 Version: v1.61.102
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-09 Version: v1.61.101
+- Generated 2015-12-01 for `Dds`.
+- Add DescribePrice API.
+
+2020-04-09 Version: v1.61.100
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Add DescribePrice API.
+
+2020-04-09 Version: v1.61.99
+- Generated 2019-09-10 for `waf-openapi`.
+- WAF OpenApi SDK Release.
+
+2020-04-09 Version: v1.61.98
+- Generated 2017-08-01 for `polardb`.
+- Add DescribeDBClusterAvailableResources.
+
+2020-04-08 Version: v1.61.97
+- Generated 2019-03-07 for `Cloudauth`.
+- Add SubCode for DescribeFaceVerify.
+
+2020-04-08 Version: v1.61.96
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-07 Version: v1.61.95
+- Generated 2020-02-06 for `acm`.
+- Support namespaces.
+
+2020-04-07 Version: v1.61.94
+- Generated 2020-03-20 for `imgsearch`.
+
+
+2020-04-07 Version: v1.61.93
+- Generated 2019-12-30 for `facebody`.
+
+
+2020-04-07 Version: v1.61.92
+- Generated 2020-02-06 for `acm`.
+- Support namespaces.
+
+2020-04-07 Version: v1.61.91
+- Generated 2019-08-08 for `ARMS`.
+- Update SearchAlertRules response struct.
+
+2020-04-03 Version: v1.61.90
+- Generated 2019-08-08 for `ARMS`.
+- Add trace api.
+- Add prometheus api.
+
+2020-04-03 Version: v1.61.89
+- Generated 2020-02-01 for `cloudesl`.
+- Support shelf type for planogram position.
+
+2020-04-03 Version: v1.61.88
+- Generated 2017-12-25 for `OpenSearch`.
+- Support sort script.
+
+2020-04-02 Version: v1.61.87
+- Generated 2017-11-18 for `CSB`.
+- Add slo metrics.
+
+2020-04-02 Version: v1.61.86
+- Generated 2019-03-07 for `Cloudauth`.
+- Add API ContrastFaceVerify.
+
+2020-04-02 Version: v1.61.85
+- Generated 2019-03-15 for `fnf`.
+- Support for WaitTimeSeconds for DescribeExecution.
+
+2020-04-01 Version: v1.61.84
+- Generated 2019-03-07 for `Cloudauth`.
+- DescribeVerifyToken API Add Parameters-UserIp and UserPhoneNumber and UserRegistTime.
+
+2020-04-01 Version: v1.61.83
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-01 Version: v1.61.82
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-04-01 Version: v1.61.81
+- Generated 2019-06-01 for `oos`.
+- Parameters is string in StartExecution Response.
+
+2020-04-01 Version: v1.61.80
+- Generated 2019-06-01 for `oos`.
+- Counters and Parameters are changed to Map in ListExections and StartExecution.
+
+2020-04-01 Version: v1.61.79
+- Generated 2018-08-01 for `dts`.
+
+2020-03-25 Version: v1.61.78
+- Generated 2018-01-11 for `rtc`.
+- Supported describe appkey for rtc application.
+- Supported set property for rtc channel.
+- Fixed the missing error code description for rtc removeterminal.
+
+2020-03-24 Version: v1.61.77
+- Generated 2014-05-26 for `Ecs`.
+- DescribeInstanceTypes Supports EniIpv6AddressQuantity.
+
+2020-03-24 Version: v1.61.76
+- Generated 2014-05-26 for `Ecs`.
+- DescribeInstanceTypes Supports EniIpv6AddressQuantity.
+
+2020-03-24 Version: v1.61.75
+- Generated 2019-09-01 for `ahas-openapi`.
+
+
+2020-03-24 Version: v1.61.74
+- Generated 2018-10-10 for `Yundun-bastionhost`.
+
+
+2020-03-24 Version: v1.61.73
+- Generated 2018-10-29 for `Yundun-dbaudit`.
+
+
+2020-03-23 Version: v1.61.72
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Add DescribeSecurityGroupConfiguration API.
+- Add ModifySecurityGroupConfiguration API.
+
+2020-03-23 Version: v1.61.71
+- Generated 2014-08-15 for `Rds`.
+- Generated 2014-08-15 for `Rds`.
+
+2020-03-22 Version: v1.61.70
+- Generated 2014-08-15 for `Rds`.
+
+
+2020-03-20 Version: v1.61.69
+- Generated 2018-05-22 for `eas`.
+- Add GetNetworkInterface interface.
+
+2020-03-20 Version: v1.61.68
+- Generated 2015-05-01 for `Ram`.
+- RAM role support MaxSessionDuration.
+
+2020-03-18 Version: v1.61.67
+- Generated 2015-01-09 for `Alidns`.
+- Supported API for DescribeInstanceDomains.
+
+2020-03-18 Version: v1.61.66
+- Generated 2020-02-06 for `acm`.
+- ACM POP SDK.
+
+2020-03-18 Version: v1.61.65
+- Generated 2019-11-18 for `address-purification`.
+- Support product addrp.
+
+2020-03-18 Version: v1.61.64
+- Generated 2017-08-24 for `faas`.
+- Support GO-SDK for 20170824 version.
+
+2020-03-18 Version: v1.61.63
+- Generated 2018-08-28 for `Tag`.
+
+
+2020-03-16 Version: v1.61.62
+- Generated 2019-03-07 for `Cloudauth`.
+- DescribeFaceVerify API Return DeviceToken Field.
+
+2020-03-16 Version: v1.61.61
+- Generated 2019-01-01 for `Cms`.
+- Add exporter related API.
+
+2020-03-16 Version: v1.61.60
+- Generated 2019-12-12 for `amqp-open`.
+- Release.
+- Add virtual host modification API.
+- Add exchange modification API.
+- Add queue modification API.
+- Add binding modification API.
+
+2020-03-16 Version: v1.61.59
+- Generated 2014-05-26 for `Ecs`.
+- Add ImageFamily paramters and apis, in Instance creation apis and Image query apis, and add DescribeImageFromFamily.
+- Add Instance batch operation apis RebootInstances StartInstances and StopInstances.
+- Add EncryptAlgorithm paramter in instance creati
+
+2020-03-13 Version: v1.61.58
+- Generated 2020-02-06 for `acms-open`.
+- ACM POP SDK.
+
+2020-03-12 Version: v1.61.57
+- Generated 2019-06-12 for `MaxCompute`.
+- Apply delete project api.
+- Apply get project api.
+
+2020-03-12 Version: v1.61.56
+- Generated 2014-08-28 for `Ess`.
+- Add ListTagKeys, ListTagValues etc.
+
+2020-03-12 Version: v1.61.55
+- Generated 2014-08-28 for `Ess`.
+- Add ListTagKeys, ListTagValues etc.
+
+2020-03-12 Version: v1.61.54
+- Generated 2014-08-28 for `Ess`.
+- Add ListTagKeys, ListTagValues etc.
+
+2020-03-12 Version: v1.61.53
+- Generated 2014-08-28 for `Ess`.
+- Add ListTagKeys, ListTagValues etc.
+
+2020-03-12 Version: v1.61.52
+- Generated 2019-06-25 for `ivpd`.
+- Add new Api SegmentBody.
+
+2020-03-11 Version: v1.61.51
+- Generated 2018-08-08 for `Eci`.
+
+2020-03-11 Version: v1.61.50
+- Generated 2018-08-08 for `Eci`.
+
+2020-03-11 Version: v1.61.49
+- Generated 2015-12-01 for `Dds`.
+- Update structure for DescribeAvailableResource without compatible.
+
+2020-03-11 Version: v1.61.48
+- Generated 2019-06-25 for `ivpd`.
+- Add new Api SegmentBody.
+
+2020-03-11 Version: v1.61.47
+- Generated 2018-10-12 for `alimt`.
+- Support certificate translate.
+
+2020-03-11 Version: v1.61.46
+- Generated 2018-10-12 for `alimt`.
+- Support certificate translate.
+
+2020-03-10 Version: v1.61.45
+- Generated 2020-02-06 for `acms-open`.
+- ACM POP SDK.
+
+2020-03-10 Version: v1.61.44
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add ExportTas apis.
+
+2020-03-10 Version: v1.61.43
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add ExportTas apis.
+
+2020-03-09 Version: v1.61.42
+- Generated 2019-03-06 for `Dbs`.
+- Add BackupStorageType.
+
+2020-03-09 Version: v1.61.41
+- Generated 2017-09-06 for `imm`.
+- Add EmotionConfidence to GetImage.
+
+2020-03-09 Version: v1.61.40
+- Generated 2019-11-11 for `nlp-automl`.
+- NlpAutoml update contract api.
+
+2020-03-09 Version: v1.61.39
+- Generated 2019-11-11 for `nlp-automl`.
+- NlpAutoml update contract api.
+
+2020-03-09 Version: v1.61.38
+- Generated 2019-11-11 for `nlp-automl`.
+- NlpAutoml update contract api.
+
+2020-03-06 Version: v1.61.37
+- Generated 2018-01-20 for `Iot`.
+- Add LinkIoTEdge CreateEdgeOssPreSignedAddress API.
+
+2020-03-06 Version: v1.61.36
+- Generated 2018-01-20 for `Iot`.
+- Add LinkIoTEdge CreateEdgeOssPreSignedAddress API.
+
+2020-03-06 Version: v1.61.35
+- Generated 2018-09-19 for `saf`.
+- Supported Saf for cn.
+
+2020-03-06 Version: v1.61.34
+- Generated 2018-09-19 for `saf`.
+- Supported Saf for cn.
+
+2020-03-06 Version: v1.61.33
+- Generated 2019-05-21 for `saf`.
+- Supported Saf for oversea.
+
+2020-03-06 Version: v1.61.32
+- Generated 2019-02-14 for `Ons`.
+- Add request parameter groupType for OnsGroupCreate.
+- Add request parameter groupType for OnsGroupList.
+
+2020-03-05 Version: v1.61.31
+- Generated 2017-09-12 for `Cbn`.
+- Support flow log.
+- Support route map.
+
+2020-03-05 Version: v1.61.30
+- Generated 2018-04-12 for `EHPC`.
+- Add param InstanceType for ListImages and ListCustomImages.
+- Fix error codes for SubmitJobs and so on.
+
+2020-03-05 Version: v1.61.29
+- Generated 2018-01-20 for `Iot`.
+- Add CreateEdgeDriver, DeleteEdgeDriver, BatchGetEdgeDriver, QueryEdgeDriver, CreateEdgeDriverVersion, DeleteEdgeDriverVersion, UpdateEdgeDriverVersion, GetEdgeDriverVersion, QueryEdgeDriverVersion API.
+- Add ResetThing API, support reset thing topo and 
+
+2020-03-05 Version: v1.61.28
+- Generated 2019-12-11 for `OnsMqtt`.
+- Update send message noPresistFlag param.
+
+2020-03-04 Version: v1.61.27
+- Generated 2017-08-01 for `Edas`.
+- Add UpdateK8sResource API.
+
+2020-03-04 Version: v1.61.26
+- Generated 2019-09-28 for `reid`.
+- Update.
+
+2020-03-04 Version: v1.61.25
+- Generated 2017-08-01 for `Edas`.
+- Add UpdateK8sResource API.
+
+2020-03-04 Version: v1.61.24
+- Generated 2019-10-30 for `aliyuncvc`.
+- Public beta version.
+- Add Api GetMeetingMebers.
+
+2020-03-03 Version: v1.61.23
+- Generated 2016-01-20 for `Kms`.
+- Supported secretmanager stable version for kms.
+
+2020-03-03 Version: v1.61.22
+- Generated 2016-01-20 for `Kms`.
+- Supported secretmanager stable version for kms.
+
+2020-03-03 Version: v1.61.21
+- Generated 2020-01-08 for `PartnerBill`.
+- Add first bill method of GetPartnerInstancesMonthBill.
+
+2020-03-03 Version: v1.61.20
+- Generated 2020-01-01 for `geoip`.
+- GeoIP Databases SDK initial release.
+
+2020-03-03 Version: v1.61.19
+- Generated 2016-01-20 for `Kms`.
+- Supported secretmanager for kms.
+
+2020-02-28 Version: v1.61.18
+- Generated 2017-05-25 for `Dyplsapi`.
+- Add QueryPhoneNoAByTrackNo  and AddAxnTrackNo.
+
+2020-02-28 Version: v1.61.17
+- Generated 2016-04-28 for `Vpc`.
+- Supported for eip operation idempotence.
+
+2020-02-28 Version: v1.61.16
+- Generated 2016-01-20 for `Kms`.
+- Supported secretmanager for kms.
+
+2020-02-28 Version: v1.61.15
+- Generated 2016-04-28 for `Vpc`.
+- Supported for eip operation idempotence.
+
+2020-02-28 Version: v1.61.14
+- Generated 2018-10-12 for `alimt`.
+- Add translate api.
+
+2020-02-27 Version: v1.61.13
+- Generated 2018-07-13 for `Ft`.
+- Release Ft SDK.
+
+2020-02-27 Version: v1.61.12
+- Generated 2018-07-13 for `Ft`.
+- Release Ft SDK.
+
+2020-02-26 Version: v1.61.11
+- Generated 2017-06-13 for `elasticsearch`.
+- Fix tag upper case parameters.
+- Fix patch.
+
+2020-02-26 Version: v1.61.10
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Add VpcCloudInstanceId for DescribeInstanceAttribute.
+
+2020-02-26 Version: v1.61.9
+- Generated 2019-03-07 for `Cloudauth`.
+- Add InitFaceVerify and DescribeFaceVerify API.
+
+2020-02-25 Version: v1.61.8
+- Generated 2018-11-01 for `dms-enterprise`.
+- Fix ListWorkFlowTemplates, ListWorkFlowNodes API Go SDK build error.
+
+2020-02-25 Version: v1.61.7
+- Generated 2019-03-06 for `Dbs`.
+- Update backupsetDownloadset fun.
+
+2020-02-24 Version: v1.61.6
+- Generated 2019-01-01 for `HBase`.
+- ImmediateDelete.
+- DescribeInstance CreateTimeUTC ExpireTimeUTC.
+- DescribeInstances CreateTimeUTC ExpireTimeUTC.
+- DescribeIpWhitelist Groups GroupName.
+- CreateCluster.
+
+2020-02-24 Version: v1.61.5
+- Generated 2018-07-13 for `Ft`.
+- Release Ft SDK.
+
+2020-02-24 Version: v1.61.4
+- Generated 2015-12-01 for `Dds`.
+- Update endpoints.
+
+2020-02-24 Version: v1.61.3
+- Generated 2015-12-01 for `Dds`.
+- Update endpoints.
+
+2020-02-24 Version: v1.61.2
+- Generated 2018-07-13 for `Ft`.
+- Release Ft SDK.
+
+2020-02-24 Version: v1.61.1
+- Add PATCH into requests.
+
+2020-02-24 Version: v1.60.392
+- Generated 2017-06-13 for `elasticsearch`.
+- Add tag of list instance parameters.
+
+2020-02-24 Version: v1.60.391
+- Generated 2017-06-13 for `elasticsearch`.
+- Add tag of list instance parameters.
+
+2020-02-24 Version: v1.60.390
+- Generated 2018-06-12 for `VoiceNavigator`.
+
+
+2020-02-24 Version: v1.60.389
+- Generated 2019-12-26 for `OutboundBot`.
+
+
+2020-02-24 Version: v1.60.388
+- Generated 2019-12-26 for `OutboundBot`.
+
+
+2020-02-21 Version: v1.60.387
+- Generated 2019-09-10 for `waf-openapi`.
+
+
+2020-02-21 Version: v1.60.386
+- Support TAG API.
+
+2020-02-20 Version: v1.60.385
+- Generated 2019-09-28 for `reid`.
+
+
+2020-02-20 Version: v1.60.384
+- Generated 2019-01-01 for `Cms`.
+- Fix the problem that the three APIs, disablesitemonitors, enablesitemonitors and modifysitemonitor, return result fields are not fully define.
+
+2020-02-20 Version: v1.60.383
+- Generated 2018-03-13 for `retailcloud`.
+- Add new api ListUsers.
+- Supported userRole setting for api, CreateApp, UpdateApp, DescribeAppDetail.
+
+2020-02-19 Version: v1.60.382
+- Generated 2017-09-12 for `Cbn`.
+- Support terraform.
+- Support flow log.
+- Support route map.
+
+2020-02-15 Version: v1.60.381
+- Generated 2018-11-01 for `dms-enterprise`.
+- Supported ListWorkFlowTemplates, ListWorkFlowNodes API.
+
+2020-02-15 Version: v1.60.380
+- Generated 2017-08-01 for `polardb`.
+- Add DescribeDBClusterSSL.
+- Add ModifyDBClusterSSL.
+
+2020-02-14 Version: v1.60.379
+- Generated 2015-01-09 for `Alidns`.
+- Update default endpoints.
+
+2020-02-14 Version: v1.60.378
+- Generated 2019-03-15 for `fnf`.
+- Add callback params to StartExecution API.
+- Support ListExecutions with Status API.
+
+2020-02-14 Version: v1.60.377
+- Generated 2015-04-01 for `Sts`.
+- Use standard endpoints.
+
+2020-02-14 Version: v1.60.376
+- Generated 2018-01-01 for `pvtz`.
+- Update default endpoints.
+
+2020-02-14 Version: v1.60.375
+- Generated 2019-01-01 for `Cms`.
+- Fix the field definition for the site monitoring option OptionJson.
+
+2020-02-13 Version: v1.60.374
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.373
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.372
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.371
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.370
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.369
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.368
+- Generated 2014-05-26 for `Ecs`.
+- ModifyNetworkInterfaceAttribute support modify QueueNumber.
+- DescribeNetworkInterfaces support QueueNumber.
+
+2020-02-13 Version: v1.60.367
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.366
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.365
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.364
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.363
+- Generated 2014-11-11, 2018-05-10 for `Cdn`.
+- Add Staging apis.
+
+2020-02-13 Version: v1.60.362
+- Generated 2018-11-01 for `dms-enterprise`.
+- Fix GetDataCorrectOrderDetail API return empty database information.
+
+2020-02-13 Version: v1.60.361
+- Init MQTT SDK .
+
+2020-02-12 Version: v1.60.360
+- Generated 2020-01-21 for `DemoCenter`.
+- Support to create, describe and expire demo access token.
+
+2020-02-12 Version: v1.60.359
+- Generated 2017-08-01 for `polardb`.
+- Return `VpcInstanceId ` for DescribeDBClusterEndpoints.
+- Add DescribeBackupLogs.
+
+2020-02-12 Version: v1.60.358
+- Generated 2019-03-06 for `Dbs`.
+- Add Full showStorageTyp.
+
+2020-02-12 Version: v1.60.357
+- Generated 2018-01-11 for `rtc`.
+- Support mau.
+
+2020-02-11 Version: v1.60.356
+- Generated 2018-01-20 for `Iot`.
+- Add Thing Model APIs, including CreateThingModel, UpdateThingModel, etc.
+- Add OTA API ListOTATaskByJob.
+- Update OTA API ListOTAJobByFirmware.
+- Update Thing Core Model API InvokeThingsService.
+
+2020-02-11 Version: v1.60.355
+- Generated 2018-01-01 for `pvtz`.
+- Update default endpoints.
+
+2020-02-10 Version: v1.60.354
+- Generated 2017-09-06 for `imm`.
+- Supported address for GetMediaMeta.
+
+2020-02-09 Version: v1.60.353
+- Generated 2019-12-30 for `facebody`.
+- Sdk version 104.
+
+2020-02-07 Version: v1.60.352
+- Generated 2018-01-01 for `pvtz`.
+- Fix bug for DescribeZoneInfo, delete return result of reionId.
+- Fix bug for DeleteZone, parameter zoneId change to compulsory.
+- Fix bug for SetProxyPattern, parameter zoneId change to compulsory.
+
+2020-02-05 Version: v1.60.351
+- Generated 2017-09-06 for `imm`.
+- Supported GetOfficePreviewURL.
+- Supported RefreshOfficePreviewToken.
+
+2020-02-04 Version: v1.60.350
+- Generated 2015-01-09 for `Alidns`.
+- Update default endpoints.
+
+2020-02-04 Version: v1.60.349
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Update endpoint for `R-kvstore`.
+
+2020-01-21 Version: v1.60.348
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Update endpoint for `R-kvstore`.
+
+2020-01-20 Version: v1.60.347
+- Generated 2018-01-20 for `Iot`.
+- Add Thing Model APIs, including QueryThingModel, PublishThingModel, etc.
+
+2020-01-20 Version: v1.60.346
+- Generated 2019-06-25 for `hiknoengine`.
+- Release on full language support.
+
+2020-01-17 Version: v1.60.345
+- Generated 2014-08-15 for `Rds`.
+- Supported Add the history event signature function.
+
+2020-01-17 Version: v1.60.344
+- Generated 2018-03-13 for `retailcloud`.
+- Supported AAA for BBB.
+- Fixed bugs for CCC.
+
+2020-01-16 Version: v1.60.343
+- Generated 2019-01-01 for `Cms`.
+- Fix the result value of DeleteSiteMonitors.
+
+2020-01-16 Version: v1.60.342
+- Generated 2015-01-01 for `R-kvstore`.
+- Generated 2015-01-01 for `R-kvstore`.
+- Update DescribeAvailableResource.
+
+2020-01-16 Version: v1.60.341
+- Generated 2019-01-23 for `Drds`.
+
+
+2020-01-16 Version: v1.60.340
+- Generated 2019-03-07 for `Cloudauth`.
+- Add fields faceComparisonScore for DescribeVerifyResult and VerifyMaterials interface.
+
+2020-01-16 Version: v1.60.339
+- Generated 2019-01-23 for `Drds`.
+
+
+2020-01-15 Version: v1.60.338
+- Generated 2018-11-01 for `dms-enterprise`.
+- Fix SignatureDoesNotMatch issue of GetOrderBaseInfo API.
+
+2020-01-15 Version: v1.60.337
+- Generated 2019-01-01 for `Cms`.
+- Fix parameter issues for label synchronization group.
+
+2020-01-15 Version: v1.60.336
+- Generated 2019-01-01 for `Cms`.
+- Fix parameter issues for label synchronization group.
+
+2020-01-15 Version: v1.60.335
+- Generated 2015-12-15 for `CS`.
+- Support A csk.
+
+2020-01-14 Version: v1.60.334
+- Generated 2019-09-16 for `alikafka`.
+- Support modity topic remark.
+- Support modify topic partitions.
+
+2020-01-13 Version: v1.60.333
+- Generated 2015-12-01 for `Dds`.
+- New API DescribeAvailableResource.
+
+2020-01-13 Version: v1.60.332
+- Generated 2014-05-26 for `Ecs`.
+- Solve conflict.
+
+2020-01-13 Version: v1.60.331
+- Generated 2017-08-23, 2018-05-09, 2016-12-22 for `Green`.
+- Fixed bugs for CreateKeywordResponse ValidKeywordList type.
+
+2020-01-13 Version: v1.60.330
+- Generated 2017-08-23, 2018-05-09, 2016-12-22 for `Green`.
+- Fixed bugs for CreateKeywordResponse ValidKeywordList type.
+
+2020-01-13 Version: v1.60.329
+- Generated 2019-01-01 for `Cms`.
+- Add Dynamic Tag API.
+
+2020-01-13 Version: v1.60.328
+- Generated 2014-05-26 for `Ecs`.
+- Supported InstanceMaintenanceAtrributes for maintening the attributes of instances.
+- Deleted MaintenanceProperty.
+- Supported RedeployDedicatedHost for redeploying dedicated host in under-assessment status.
+
+2020-01-10 Version: v1.60.327
+- Generated 2017-08-01 for `polardb`.
+- Return `ConsistentTime` for DescribeBackups.
+- Return `DBNodeIds` for CreateDBNodes.
+
+2020-01-09 Version: v1.60.326
+- Generated 2017-12-14 for `BssOpenApi`.
+- Support DAILY report in QueryInstanBill.
+
+2020-01-09 Version: v1.60.325
+- Generated 2017-11-10 for `Ens`.
+- Add New Interface DescribeInstanceTypes.
+- Deprecated New Interface DescribeReservedResource.
+- Add New Interface DescribeAvailableResourceInfo.
+- Add New Interface AttachEnsInstances.
+
+2020-01-08 Version: v1.60.324
+- Generated 2017-09-06 for `imm`.
+- Add TagLevel to ListSetTags.
+
+2020-01-08 Version: v1.60.323
+- Generated 2014-05-15 for `Slb`.
+- Supported upload Regional AlicloudCertificate.
+
+2020-01-08 Version: v1.60.322
+- Generated 2014-08-15 for `Rds`.
+
+
+2020-01-08 Version: v1.60.321
+- Generated 2014-08-15 for `Rds`.
+
+
+2020-01-08 Version: v1.60.320
+- Generated 2018-03-13 for `retailcloud`.
+- Supported AAA for BBB.
+- Fixed bugs for CCC.
+
+2020-01-07 Version: v1.60.319
+- Generated 2017-06-13 for `elasticsearch`.
+- Modify desribeInstance return dict item.
+
+2020-01-07 Version: v1.60.318
+- Generated 2014-08-15 for `Rds`.
+
+
+2020-01-03 Version: v1.60.317
+- Generated 2018-04-12 for `EHPC`.
+- Add GWS serie apis.
+- Add QueryReport.
+- Fix ListJobs.
+- Etc.
+
+2020-01-03 Version: v1.60.316
+- Generated 2015-12-01 for `Dds`.
+- New API DescribeAvailableResource.
+
+2020-01-03 Version: v1.60.315
+- Generated 2019-01-01 for `HBase`.
+- ImmediateDelete.
+- DescribeInstance CreateTimeUTC ExpireTimeUTC.
+- DescribeInstances CreateTimeUTC ExpireTimeUTC.
+- DescribeIpWhitelist Groups GroupName.
+
+2019-01-02 Version: v1.60.314
+- Remove r-kvstore.
+
+2019-12-31 Version: v1.60.313
+- Generated 2018-01-20 for `Iot`.
+- Add OTA APIs, including GenerateOTAUploadURL, CreateOTAFirmware, etc.
+- Add UpdateProductFilterConfig interface, support setting product filter config.
+
+2019-12-31 Version: v1.60.312
+- Generated 2019-12-30 for `ocr`.
+- First version.
+
+2019-12-31 Version: v1.60.311
+- Generated 2014-08-15 for `Rds`.
+- Supported SQL SERVER support delete backup set according to time range.
+
+2019-12-31 Version: v1.60.310
+- Generated 2016-01-20 for `Kms`.
+- Support asymmetric keys.
+
+2019-12-31 Version: v1.60.309
+- Generated 2019-02-14 for `Ons`.
+- Add request parameter groupType for OnsGroupCreate.
+
+2019-12-31 Version: v1.60.308
+- Generated 2017-09-06 for `imm`.
+- Supported image for process.
+
+2019-12-31 Version: v1.60.307
+- Generated 2019-09-28 for `reid`.
+
+
+2019-12-31 Version: v1.60.306
+- Generated 2019-03-15 for `adb`.
+- Support DeleteDBCluster.
+- Support ModifyDBCluster.
+- Support DescribeAvailableResource.
+
+2019-12-30 Version: v1.60.305
+- Generated 2018-01-20 for `Iot`.
+- Supported 2018-01-20 for Golang.
+
+2019-12-26 Version: v1.60.304
+- Generated 2019-11-11 for `nlp-automl`.
+- NlpAutoml add async api.
+
+2019-12-26 Version: v1.60.303
+- Generated 2019-03-06 for `Dbs`.
+- Modify CreateRestoreTask.
+
+2019-12-23 Version: v1.60.302
+- Generated 2016-04-08 for `Emr`.
+- Bugfix.
+
+2019-12-20 Version: v1.60.301
+- Generated 2018-11-01 for `dms-enterprise`.
+- Supported Authorization API, includes GrantUserPermission, RevokeUserPermission, ListDatabaseUserPermssions, ListUserPermissions.
+- Supported Database or Table Metadata synchronization API, includes SyncInstanceMeta, SyncDatabaseMeta.
+- Supported Database or Table Owner set up API.
+- Supported GetUser API to get information of one DMS user.
+- Supported set Dingding mobile number with RegisterUser API.
+
+2019-12-20 Version: v1.60.300
+- Generated 2019-03-08 for `ivision`.
+- Supported Search stream predict task list use modelId for IVISION.
+
+2019-12-20 Version: v1.60.299
+- Generated 2019-02-14 for `Ons`.
+- Add result value for OnsConsumerStatus.
+
+2019-12-19 Version: v1.60.298
+- Generated 2017-08-01 for `polardb`.
+- Add DescribeSlowLogRecords,DescribeSlowLogs for SlowLog.
+
+2019-12-19 Version: v1.60.297
+- Generated 2019-09-28 for `reid`.
+
+
+2019-12-19 Version: v1.60.296
+- Generated 2014-05-26 for `Ecs`.
+- DescribeNetworkInterfaces Supports NextToken.
+
+2019-12-18 Version: v1.60.295
+- Generated 2014-08-15 for `Rds`.
+
+
+2019-12-18 Version: v1.60.294
+- Generated 2019-09-16 for `alikafka`.
+- Support sasl and acl.
+
+2019-12-16 Version: v1.60.293
+- DescribeTransferDomains add query param, TargetUserId, FromUserId, DomainName.
+
+2019-12-16 Version: v1.60.292
+- Update tag related api to publically visable.
+
+2019-12-16 Version: v1.60.291
+- Update tag related api to publically visable.
+
+2019-12-14 Version: v1.60.290
+- Generated 2015-01-01 for `R-kvstore`.
+
+2019-12-14 Version: v1.60.289
+- Update DescribeAvailableResource.
+
+2019-12-14 Version: v1.60.288
+- Update DescribeAvailableResource.
+
+2019-12-11 Version: v1.60.287
+- Add tag API.
+- Add group process monitoring API.
+- Add API for batch setting alarm rules.
+
+2019-12-11 Version: v1.60.286
+- Support GroupDeletionProtection.
+
+2019-12-05 Version: v1.60.285
+- Add api QueryMetric.
+
+2019-12-04 Version: v1.60.284
+- Delete invalid Api.
+
+2019-12-04 Version: v1.60.283
+- Generated 2019-01-01 for `HBase`.
+
+2019-12-04 Version: v1.60.282
+- Supported ModifyDBInstanceSpec for Direction param.
+
+2019-12-03 Version: v1.60.281
+- Supported face library operation.
+- Supported task template and words.
+
+2019-12-03 Version: v1.60.280
+- Bugfix for DescribeTrails.
+
+2019-12-02 Version: v1.60.279
+- Generated 2019-01-23 for `Drds`.
+
+2019-12-02 Version: v1.60.278
+- Update go sdk.
+
+2019-12-02 Version: v1.60.277
+- Remove QuerySmsProdStatus OpenAPI.
+
+2019-11-29 Version: v1.60.276
+- Add RenewInstance.
+- Add RenewLogstash.
+- Add UpdateInstanceChargeType.
+- Add UpdateLogstashChargeType.
+
+2019-11-29 Version: v1.60.275
+- Add RWMutex to AddEndpointMapping and GetEndpointFromMap.
+
+2019-11-28 Version: v1.60.274
+- Supported API GetTxtRecordForVerify.
+- Supported API RetrieveDomain.
+
+2019-11-28 Version: v1.60.273
+- Supported dash for compress.
+
+2019-11-28 Version: v1.60.272
+- Eni Supports Multi-SecurityGroup.
+
+2019-11-28 Version: v1.60.271
+- Support Order Basis API, include CreateOrder, CloseOrder, ListOrders, GetOrderBaseInfo.
+- Support Order Approval API, include SubmitOrderApproval, ApproveOrder, GetApprovalDetail.
+- Support DataCorrect Order Operation API, include ExecuteDataCorrect, GetDataCorrectOrderDetail, GetDataCorrectBackupFiles.
+- Support DDL Order Operation API, include CreatePublishGroupTask.
+- Support DataExport Operation API, include ExecuteDataExport, GetDataExportOrderDetail, GetDataExportDownloadURL.
+
+2019-11-27 Version: v1.60.270
+- Add Tag API.
+
+2019-11-27 Version: v1.60.269
+- CreateReplicationJob API supports the specified instance type.
+
+2019-11-27 Version: v1.60.268
+- Add enhanced natgateway sdk.
+
+2019-11-26 Version: v1.60.267
+- Modify QueryBill, add SubOrderId in item of response.
+
+2019-11-26 Version: v1.60.266
+- Generated 2015-12-01 for `Dds`.
+
+2019-11-26 Version: v1.60.265
+- Supported API BindInstanceDomains.
+- Supported API UnbindInstanceDomains.
+- Supported API AddCustomLine.
+- Supported API UpdateCustomLine.
+- Supported API DeleteCustomLines.
+- Supported API DescribeCustomLine.
+- Supported API DescribeCustomLines.
+
+2019-11-26 Version: v1.60.264
+- Generated 2015-12-01 for `Dds`.
+
+2019-11-26 Version: v1.60.263
+- Refresh endpoint.
+
+2019-11-26 Version: v1.60.262
+- Generated 2014-08-15 for `Rds`.
+
+2019-11-25 Version: v1.60.261
+- Add v to release version.
+
+2019-11-25 Version: 1.60.260
+- One or more people can be subscribed when creating tasks.
+
+2019-11-22 Version: 1.60.259
+- Update Api Request And Response structure.
+
+2019-11-22 Version: 1.60.258
+- First version.
+
+2019-11-21 Version: 1.60.257
+- Add ConvertPayType.
+- Add ConvertLogstashPayType.
+
+2019-11-21 Version: 1.60.256
+- Update Nlp Automl SDK.
+
+2019-11-21 Version: 1.60.255
+- Add OpenAPI DescribeAvailableResourceInfo.
+- Deprecated OpenApi DescribeAvailableResource.
+
+2019-11-21 Version: 1.60.254
+- First version.
+
+2019-11-20 Version: 1.60.253
+- Add new pop api.
+
+2019-11-20 Version: 1.60.252
+- Supported ecsDemand CreateDemand, ModifyDemand, DeleteDemand API.
+- Modify ecsDemand DescribesDemands API.
+
+2019-11-20 Version: 1.60.251
+- Generated 2019-01-01 for `HBase`.
+
+2019-11-20 Version: 1.60.250
+- Add API ReportVoipProblems.
+- Add API QueryVoipNumberBindINfos.
+
+2019-11-19 Version: 1.60.249
+- Generated 2018-01-20 for `Iot`.
+
+2019-11-15 Version: 1.60.248
+- Nlp Automl SDK.
+
+2019-11-15 Version: 1.60.247
+- Support tags.
+- Support create pre paid alikafka instance.
+
+2019-11-15 Version: 1.60.246
+- Sync cdn APIs.
+
+2019-11-15 Version: 1.60.245
+- Fixed bugs.
+
+2019-11-15 Version: 1.60.244
+- Add Scdn APIS.
+- Sync cdn APIS.
+
+2019-11-15 Version: 1.60.243
+- Modify QueryAccountBill, support group by product.
+
+2019-11-15 Version: 1.60.242
+- Modify DescribeRestoreRangeInfo.
+
+2019-11-15 Version: 1.60.241
+- Add DescribeRestoreRangeInfo.
+
+2019-11-14 Version: 1.60.240
+- Released dysms OpenAPI.
+
+2019-11-14 Version: 1.60.239
+- Support Tag OpenAPI.
+- Fix endpoint problem.
+
+2019-11-14 Version: 1.60.238
+- Initial construction.
+- Public beta version.
+- Supported AliyunController for Interface.
+- Supported ConsoleController for Interface.
+- Supported AccountController for Interface.
+
+2019-11-13 Version: 1.60.237
+- Supported Stream analyse.
+
+2019-11-13 Version: 1.60.236
+- Fix ResourceId type from Long to String on QueryCostUnitResource.
+
+2019-11-13 Version: 1.60.235
+- Fix ResourceId type from Long to String on QueryCostUnitResource.
+
+2019-11-13 Version: 1.60.234
+- Add new API QueryBillToOSSSubscription.
+
+2019-11-13 Version: 1.60.233
+- Supported API UpdateDomainRemark for Update Domain Remark.
+- Supported API UpdateDomainRecordRemark for Update Record Remark.
+- Unsupported API CheckDomainRecord.
+
+2019-11-13 Version: 1.60.232
+- Support Go SDK.
+- Support C SDK.
+- Support PHP SDK.
+
+2019-11-12 Version: 1.60.231
+- Generated 2018-08-21, 2018-08-16 for `Rdc`.
+
+2019-11-12 Version: 1.60.230
+- Generated 2018-08-21 for `Rdc`.
+
+2019-11-08 Version: 1.60.229
+- Add DescribeJobErrorCode.
+
+2019-11-05 Version: 1.60.228
+- Add OperateBlackNo.
+
+2019-11-04 Version: 1.60.227
+- Supported Qos for smartag.
+
+2019-11-04 Version: 1.60.226
+- Support console api for cloud bastionhost.
+
+2019-11-02 Version: 1.60.225
+- Generated 2017-05-25 for `Dyvmsapi`.
+
+2019-10-31 Version: 1.60.224
+- Initial construction.
+- Public beta version.
+- Supported AliyunController for Interface.
+- Supported ConsoleController for Interface.
+- Supported AccountController for Interface.
+
+2019-10-31 Version: 1.60.223
+- Initial construction.
+- Public beta version.
+- Supported AliyunController for Interface.
+- Supported ConsoleController for Interface.
+- Supported AccountController for Interface.
+
+2019-10-31 Version: 1.60.222
+- Support ListTagResources for tag fuzzy filter.
+
+2019-10-30 Version: 1.60.221
+- Update interface params of the cloudauth.
+
+2019-10-29 Version: 1.60.220
+- Add a new Alibaba Cloud Bill API named QueryAccountBill.
+
+2019-10-28 Version: 1.60.219
+- FIX RefundInstance.
+
+2019-10-25 Version: 1.60.218
+- ADD RefundInstance.
+
+2019-10-25 Version: 1.60.217
+- Initial construction.
+- Public beta version.
+- Supported AliyunController for Interface.
+- Supported ConsoleController for Interface.
+- Supported AccountController for Interface.
+
+2019-10-25 Version: 1.60.216
+- Supported query push records.
+
+2019-10-25 Version: 1.60.215
+- Add DescribeDBClusterPerformance, DescribeDBNodePerformance for performance.
+
+2019-10-24 Version: 1.60.214
+- Optimize instance relevant interface.
+
+2019-10-24 Version: 1.60.213
+- Generated 2017-12-14 for `BssOpenApi`.
+
+2019-10-24 Version: 1.60.212
+- Generated 2017-12-14 for `BssOpenApi`.
+
+2019-10-23 Version: 1.60.211
+- Add a new api named SubmitDynamicImageJob capture a certain part of the video as a dynamic image.
+- Add a new api named SubmitWorkflowJob to initiate the VoD workflow processing for audio and video.
+- Add a new field AuditStatus in the structure ImageInfo to GetImageInfo api response, which is used to identify the audit status of the image.
+- Modify the data type of return field StorageLocation to String.
+- Add a new field named MediaType to SubmitAIMediaAuditJob api request.
+
+2019-10-23 Version: 1.60.210
+- Add a new api named SubmitDynamicImageJob capture a certain part of the video as a dynamic image.
+- Add a new api named SubmitWorkflowJob to initiate the VoD workflow processing for audio and video.
+- Add a new field AuditStatus in the structure ImageInfo to GetImageInfo api response, which is used to identify the audit status of the image.
+- Modify the data type of return field StorageLocation to String.
+- Add a new field named MediaType to SubmitAIMediaAuditJob api request.
+
+2019-10-23 Version: 1.60.209
+- Add a new api named SubmitDynamicImageJob capture a certain part of the video as a dynamic image.
+- Add a new api named SubmitWorkflowJob to initiate the VoD workflow processing for audio and video.
+- Add a new field AuditStatus in the structure ImageInfo to GetImageInfo api response, which is used to identify the audit status of the image.
+- Modify the data type of return field StorageLocation to String.
+- Add a new field named MediaType to SubmitAIMediaAuditJob api request.
+
+2019-10-22 Version: 1.60.208
+- Supported Sync predict for Image.
+- Supported Set Frame Frequency for Stream Predict.
+- Fixed Errore Code For APIs.
+
+2019-10-22 Version: 1.60.207
+- RunInstances support AutoSnapshotPolicyId.
+
+2019-10-22 Version: 1.60.206
+- Fix ListDatabases CatalogName dataType integer to string.
+- Remove GetLogicDatabase invalid output param named TotalCount.
+- Remove ListIndexes invalid output param named ColumnList.
+- Support SearchTable API to filter searchTarget.
+
+2019-10-22 Version: 1.60.205
+- Supported ExternalId IMM.
+
+2019-10-21 Version: 1.60.204
+- Release first version.
+
+2019-10-21 Version: 1.60.203
+- Init the standard baas sdk.
+
+2019-10-18 Version: 1.60.202
+- Generated 2019-09-10 for `DnsKnocker`.
+
+2019-10-18 Version: 1.60.201
+- Modify Yundun-dbaudit DescribeInstanceAttribute InstanceStatus Type.
+
+2019-10-17 Version: 1.60.200
+- Support instance manage API, includes UpdateInstance and DeleteInstance.
+- Support user manage API, includes ListUsers and UpdateUser.
+- Support meta data information API, includes ListInstances, ListDatabases, ListLogicDatabases, ListTables, ListLogicTables, ListColumns, ListIndexes.
+- Support meta data search API, inclues SearchDatabase, SearcTable.
+- Support RegisterInstance API to set datalinkName and useDsql option.
+
+2019-10-16 Version: 1.60.199
+- Optimize instance relevant interface.
+
+2019-10-16 Version: 1.60.198
+- Add ListTagResources OpenApi.
+- Add TagResources OpenApi.
+- Add UntagResources OpenApi.
+- Add ModifyDBInstanceAutoUpgradeMinorVersion OpenApi.
+
+2019-10-14 Version: 1.60.197
+- Optimize instance relevant interface.
+
+2019-10-14 Version: 1.60.196
+- Support continuous pushing.
+
+2019-10-13 Version: 1.60.195
+- Add instance relevant interface.
+
+2019-10-12 Version: 1.60.194
+- Add new api.
+
+2019-10-11 Version: 1.60.193
+- Add response paramete for DescribeDomainExtensionAttribute.
+
+2019-10-11 Version: 1.60.192
+- Add new interface DescribeDomainExtensionAttribute.
+
+2019-10-10 Version: 1.60.191
+- Generated 2014-05-15 for `Slb`.
+
+2019-10-10 Version: 1.60.190
+- Generated 2015-04-01 for `Sts`.
+
+2019-10-09 Version: 1.60.189
+- Add VideoCancelScan Api.
+
+2019-10-09 Version: 1.60.188
+- Support API RecognizeImageColor.
+- Support API DetectImageElements.
+- Support API RecolorImage.
+- Support API SegmentImage.
+- Support API ChangeImageSize.
+- Support API ExtendImageStyle.
+- Support API RecognizeImageStyle.
+- Support API MakeSuperResolution.
+
+2019-10-09 Version: 1.60.187
+- Generated 2018-11-11 for `PTS`.
+
+2019-10-09 Version: 1.60.186
+- DescribeIntance status type modification.
+
+2019-10-08 Version: 1.60.185
+- Supported Grab Frame IMM.
+
+2019-10-08 Version: 1.60.184
+- Supported API MassPush for Push Message or Notice.
+
+2019-10-08 Version: 1.60.183
+- Generated 2016-01-20 for `Kms`.
+
+2019-09-29 Version: 1.60.182
+- Generated 2015-01-01 for `R-kvstore`.
+
+2019-09-29 Version: 1.60.181
+- Add new api.
+
+2019-09-26 Version: 1.60.180
+- Update AHAS GetSentinelAppSumMetric API.
+
+2019-09-26 Version: 1.60.179
+- Supported RingConfig for BindAxb,BindAxn,BindAxg,BindAxnExtension.
+- Add QuerySubsId.
+
+2019-09-25 Version: 1.60.178
+- Add new api.
+
+2019-09-25 Version: 1.60.177
+- Update AHAS GetSentinelAppSumMetric API.
+
+2019-09-25 Version: 1.60.176
+- Fix the capitalization problem of Describe Site Monitor Data.
+
+2019-09-24 Version: 1.60.175
+- Generated 2019-08-10 for `multimediaai`.
+
+2019-09-23 Version: 1.60.174
+- Generated 2019-06-12 for `MaxCompute`.
+
+2019-09-23 Version: 1.60.173
+- Add param, DescribePrice supprot ReservedInstance.
+
+2019-09-20 Version: 1.60.172
+- Generated 2017-12-14 for `BssOpenApi`.
+
+2019-09-20 Version: 1.60.171
+- Generated 2018-02-01, 2018-07-20 for `ddosbgp`.
+
+2019-09-19 Version: 1.60.170
+- Supported Video Abstract for IMM.
+
+2019-09-19 Version: 1.60.169
+- Supported Video Abstract for IMM.
+
+2019-09-19 Version: 1.60.168
+- Add bizSubCode and so on.
+
+2019-09-18 Version: 1.60.167
+- Support to update layout in task for MPU.
+
+2019-09-18 Version: 1.60.166
+- Optimize error code.
+
+2019-09-18 Version: 1.60.165
+- Supported Video Abstract for IMM.
+
+2019-09-18 Version: 1.60.164
+- Generated 2019-09-10 for `waf-openapi`.
+
+2019-09-18 Version: 1.60.163
+- Generated 2016-11-01 for `live`.
+
+2019-09-18 Version: 1.60.162
+- Add AHAS OpenAPI version 2019-09-01.
+
+2019-09-16 Version: 1.60.161
+- Generated 2019-09-10 for `waf-openapi`.
+
+2019-09-16 Version: 1.60.160
+- Add Region List.
+
+2019-09-16 Version: 1.60.159
+- Fixed go struct file.
+
+2019-09-16 Version: 1.60.158
+- Generated 2019-09-10 for `waf-openapi`.
+
+2019-09-16 Version: 1.60.157
+- Generated 2019-09-10 for `waf-openapi`.
+
+2019-09-12 Version: 1.60.156
+- Add DescribePrePaidInstanceStock.
+
+2019-09-12 Version: 1.60.155
+- Support RDS.
+
+2019-09-09 Version: 1.60.154
+- CreateInstance add toen.
+- UpdateInstance add toen.
+
+2019-09-09 Version: 1.60.153
+- Generated 2017-05-25 for `Dysmsapi`.
+
+2019-09-06 Version: 1.60.152
+- Generated 2018-01-17 for `waf-openapi`.
+
+2019-09-06 Version: 1.60.151
+- Supported specifying vswitch when creating vpngateway.
+
+2019-09-06 Version: 1.60.150
+- Update endpoint data.
+
+2019-09-06 Version: 1.60.149
+- Add ReleasePostPaidInstance.
+- Add ReleasePrePaidInstance.
+
+2019-09-06 Version: 1.60.148
+- Generated 2016-04-28 for `Vpc`.
+
+2019-09-06 Version: 1.60.147
+- Generated 2014-05-15 for `Slb`.
+
+2019-09-05 Version: 1.60.146
+- Supported for setEndpoint method.
+
+2019-09-05 Version: 1.60.145
+- Generated 2014-05-15 for `Slb`.
+
+2019-09-05 Version: 1.60.144
+- Generated 2017-07-05 for `CCC`.
+
+2019-09-05 Version: 1.60.143
+- Generated 2018-11-11 for `foas`.
+
+2019-09-05 Version: 1.60.142
+- Add DescribeInstanceTypes.
+- Add DescribeReservedResource.
+- Add DescribeInstances.
+
+2019-09-05 Version: 1.60.141
+- Generated 2019-05-24 for `cusanalytic_sc_online`.
+
+2019-09-05 Version: 1.60.140
+- Generated 2015-11-01 for `Market`.
+
+2019-09-05 Version: 1.60.139
+- Generated 2016-07-14 for `CloudAPI`.
+
+2019-09-05 Version: 1.60.138
+- Generated 2017-08-01 for `polardb`.
+
+2019-09-05 Version: 1.60.137
+- Generated 2017-12-14 for `BssOpenApi`.
+
+2019-09-05 Version: 1.60.136
+- Generated 2014-05-26 for `Ecs`.
+
+2019-09-03 Version: 1.60.135
+- Generated 2018-02-01, 2018-07-20 for `ddosbgp`.
+
+2019-09-03 Version: 1.60.134
+- Generated 2018-02-01 for `ddosbgp`.
+
+2019-09-03 Version: 1.60.133
+- Generated 2014-08-15 for `Rds`.
+
+2019-08-30 Version: 1.60.132
+- Add describeRegions.
+- Add toen.
+
+2019-08-30 Version: 1.60.131
+- Revert to 2015-09-01.
+
+2019-08-30 Version: 1.60.130
+- Release Apis of Version 2019-09-10.
+
+2019-08-30 Version: 1.60.129
+- Support latest DRDS Open API for version 2019-01-23.
+
+2019-08-30 Version: 1.60.128
+- Supported Meida complex for IMM.
+
+2019-08-30 Version: 1.60.127
+- Add api for getTrace and searchTracelist.
+
+2019-08-29 Version: 1.60.126
+- Move StopExecution params to body.
+
+2019-08-29 Version: 1.60.125
+- Bugfix.
+
+2019-08-29 Version: 1.60.124
+- For publish.
+
+2019-08-28 Version: 1.60.123
+- Update emr go api.
+
+2019-08-28 Version: 1.60.122
+- Add DBS API UpgradeBackupPlan.
+- Add DBS API DescribePreCheckProgressList.
+
+2019-08-28 Version: 1.60.121
+- Add NamespaceId for listApplication.
+
+2019-08-27 Version: 1.60.120
+- Add ModifyDBInstanceAutoUpgradeMinorVersion Openapi.
+
+2019-08-26 Version: 1.60.119
+- QueryInstanceBillResponse change ownerId type from Long to String.
+- QueryInstanceBillResponse change usage type from float to String.
+- QueryInstanceBillResponse change listPrice type from float to String.
+- QueryInstanceBillResponse change deductedByResourcePackage type from float to String.
+
+2019-08-22 Version: 1.60.118
+- Support API RecognizeImageColor.
+- Support API DetectImageElements.
+- Support API RecolorImage.
+- Support API SegmentImage.
+- Support API ChangeImageSize.
+- Support API ExtendImageStyle.
+- Support API RecognizeImageStyle.
+- Support API MakeSuperResolutionImage.
+
+2019-08-22 Version: 1.60.117
+- Api release 2019-08-08 public sdk 2-5-2 sdk release 20190822 shichun-fsc.
+
+2019-08-22 Version: 1.60.116
+- Api getTopicList add a new return value.
+
+2019-08-21 Version: 1.60.115
+- Supported for CPFS new feature of client evict.
+
+2019-08-21 Version: 1.60.114
+- Move StartExecution params to body.
+
+2019-08-21 Version: 1.60.113
+- Return backup job id when create backup.
+- Return backup set size when describe backups.
+
+2019-08-21 Version: 1.60.112
+- Modify getTopicList and getConsumerList response.
+
+2019-08-21 Version: 1.60.111
+- Modify getInstanceList response.
+
+2019-08-20 Version: 1.60.110
+- API TaobaoFilmGetSchedules retrun col add hallId.
+
+2019-08-19 Version: 1.60.109
+- Optimize topic create parameters.
+
+2019-08-19 Version: 1.60.108
+- Optimize topic create parameters.
+
+2019-08-18 Version: 1.60.107
+- Support go sdk.
+
+2019-08-16 Version: 1.60.106
+- Generated 2018-02-01 for `ddosbgp`.
+
+2019-08-15 Version: 1.60.105
+- FnF public version.
+- Add Report task api.
+
+2019-08-15 Version: 1.60.104
+- Add API BatchStartCdnDomain, BatchStopCdnDomain, DescribeTagResources, DescribeUserTags, TagResources, UntagResources.
+
+2019-08-15 Version: 1.60.103
+- QueryInstanceBillResponse add ServicePeriod.
+
+2019-08-14 Version: 1.60.102
+- Expose the interface to the yundun-console.
+- Update interface definitions of the cloudauth.
+
+2019-08-13 Version: 1.60.101
+- Optimize return code.
+- Edit QueryDataset.
+
+2019-08-13 Version: 1.60.100
+- Supported for openapi new version.
+
+2019-08-13 Version: 1.60.99
+- Support latest DRDS Open API for version 2019-01-23.
+
+2019-08-13 Version: 1.60.98
+- Generated 2018-07-20 for `ddosbgp`.
+
+2019-08-12 Version: 1.60.97
+- Supported create routeEntry with routeEntryName.
+
+2019-08-12 Version: 1.60.96
+- Support Defect Face API.
+
+2019-08-08 Version: 1.60.95
+- Supported DryRun for Vpc and VSwitch.
+
+2019-08-08 Version: 1.60.94
+- Add CreateStorageSet api to support storageSet.
+- Add DeleteStorageSet api to support storageSet.
+- Add ModifyStorageSetAttribute api to support storageSet.
+- Add DescribeStorageSets api to support storageSet.
+- Add DescribeStorageSetDetails api to support storageSet.
+- Add parameter StorageSetId,StorageSetPartitionNumber to api CreateDisk,RunInstances,CreateInstance support storageSet.
+- Add StorageSetId,StorageSetPartitionNumber with response of api DescribeDisks.
+- Add DescribeNetworkInterfaces to support filter by PrivateIpAddress.
+
+2019-08-08 Version: 1.60.93
+- Group, Plugin support tag authentication.
+
+2019-08-07 Version: 1.60.92
+- Generated 2019-03-20 for `WebPlus`.
+
+2019-08-06 Version: 1.60.91
+- Supported GetMediaMeta for IMM.
+
+2019-08-06 Version: 1.60.90
+- Supported GetMediaMeta for IMM.
+
+2019-08-06 Version: 1.60.89
+- Supported GetMediaMeta for IMM.
+
+2019-08-06 Version: 1.60.88
+- Supported GetMediaMeta for IMM.
+
+2019-08-06 Version: 1.60.87
+- Supported GetMediaMeta for IMM.
+
+2019-08-06 Version: 1.60.86
+- Enhencement for CostOptimized scaling group.
+
+2019-08-05 Version: 1.60.85
+- Add response parameter LoadBalancerId for DescribeMasterSlaveServerGroupAttribute interface.
+
+2019-08-05 Version: 1.60.84
+- GetOrderDetail add originalConfig param.
+
+2019-08-05 Version: 1.60.83
+- GetOrderDetail add originalConfig param.
+
+2019-08-05 Version: 1.60.82
+- Modify DBS API DescribeFullBackupList.
+
+2019-08-02 Version: 1.60.81
+- Add a lot of new API.
+
+2019-08-02 Version: 1.60.80
+- SubscribeBillToOSSRequest add multAccountRelSubscribe, bucketOwnerId.
+- UnsubscribeBillToOSSRequest add multAccountRelSubscribe.
+
+2019-07-31 Version: 1.60.79
+- Endpoint auto route.
+
+2019-07-31 Version: 1.60.78
+- Generated 2014-06-18 for `Mts`.
+
+2019-07-30 Version: 1.60.77
+- Generated 2014-06-18 for `Mts`.
+
+2019-07-26 Version: 1.60.76
+- Generated 2019-05-24 for `cusanalytic_sc_online`.
+
+2019-07-26 Version: 1.60.75
+- Generated 2019-05-24 for `cusanalytic_sc_online`.
+
+2019-07-25 Version: 1.60.74
+- Api createKey add optional parameter `ProtectionLevel`.
+- Api describeKey add a field `ProtectionLevel` in the response.
+- Add Api `DescribeService`.
+
+2019-07-25 Version: 1.60.73
+- Generated 2016-07-14 for `CloudAPI`.
+
+2019-07-25 Version: 1.60.72
+- App-related actions support tag authentication.
+
+2019-07-23 Version: 1.60.71
+- Supported CreationOption of CreateDBCluster with `CloneFromPolarDB `,`CloneFromRDS`,`MigrationFromRDS`.
+
+2019-07-19 Version: 1.60.70
+- QueryMonthlyBillResponse add roundDownDiscount.
+- QueryBillResponse add roundDownDiscount.
+- QueryInstanceBillResponse add item.
+
+2019-07-18 Version: 1.60.69
+- Fix resolve wrong tagname IOS to iOS.
+
+2019-07-18 Version: 1.60.68
+- Add a new field named Input to SubmitAIJob api request to set the input file of AI job.
+- Change the field MediaId of SubmitAIJob api to non-mandatory.
+
+2019-07-18 Version: 1.60.67
+- Supported open api for dbaudit management.
+
+2019-07-15 Version: 1.60.66
+- Add filed `internalDomain` for api `get token`.
+
+2019-07-12 Version: 1.60.65
+- Public api AddLivePullStreamInfoConfig.
+
+2019-07-11 Version: 1.60.64
+- Modify CreateBackupPlan.
+- Modify ConfigureBackupPlan.
+- Modify DescribeFullBackupList.
+- Modify DescribeRestoreTaskList.
+- Add ModifyBackupSourceEndpoint.
+- Add ModifyBackupStrategy.
+- Add ModifyBackupPlanName.
+
+2019-07-08 Version: 1.60.63
+- ActionName change to OOSActionName from ListActions API param and response.
+
+2019-07-05 Version: 1.60.62
+- Add TaskCancelStatus for QueryTaskList api.
+
+2019-07-05 Version: 1.60.61
+- Generated 2015-04-01 for `Sts`.
+
+2019-07-05 Version: 1.60.60
+- Generated 2015-04-01 for `Sts`.
+
+2019-07-04 Version: 1.60.59
+- Supported API DescribeRecordStatisticsy for Query Volume.
+- Supported API DescribeDomainStatistics for Query Volume.
+
+2019-07-03 Version: 1.60.58
+- Supported API DescribeRecordStatisticsSummary for Query Volume.
+- Supported API DescribeDomainStatisticsSummary for Query Volume.
+- Supported API DescribeRecordStatisticsHistory for Query Volume.
+- Supported API DescribeDomainDnsStatistics for Query Volume.
+
+2019-07-02 Version: 1.60.57
+- FnF public version.
+
+2019-07-01 Version: 1.60.56
+- Support cloud_essd disk category for API CreateDisk, CreateInstance and RunInstances, and support configurating PerformanceLevel when choose cloud_essd.
+- Add ModifyDiskSpec API to support cloud_essd PerformanceLevel modification.
+- Add AutoProvisioningGroup interfaces, provide AutoProvisioningGroup function.
+- Add RetentionDays to snapshot creating.
+
+2019-06-27 Version: 1.60.55
+- Added setting of crop_mode parameter.
+
+2019-06-25 Version: 1.60.54
+- Enhencement for CostOptimized scaling group.
+
+2019-06-24 Version: 1.60.53
+- Add some new apis to manage VoD domain, such as AddVodDomain, UpdateVodDomain, DeleteVodDomain, BatchStartVodDomain, BatchStopVodDomain, DescribeVodUserDomains, DescribeVodDomainDetail.
+- Add some new apis to manage VoD domain config, such as BatchSetVodDomainConfigs, DescribeVodDomainConfigs, DeleteVodSpecificConfig, SetVodDomainCertificate, DescribeVodCertificateList, DescribeVodDomainCertificateInfo.
+- Add a new field named AppId to some apis supporting the VoD App feature, such as AddWorkFlow, GetWorkFlow, ListWorkFlow, AddVodTemplate, GetVodTemplate, ListVodTemplate, AddTranscodeTemplateGroup, GetTranscodeTemplateGroup, ListTranscodeTemplateGroup, AddWatermark, GetWatermark, ListWatermark, UploadMediaByURL.
+- Add a new field named UserData to SubmitTranscodeJobs api request to support user-defined extension fields, which can be used for transparent return when callbacks.
+
+2019-06-24 Version: 1.60.52
+- Add some new apis to manage VoD domain, such as AddVodDomain, UpdateVodDomain, DeleteVodDomain, BatchStartVodDomain, BatchStopVodDomain, DescribeVodUserDomains, DescribeVodDomainDetail.
+- Add some new apis to manage VoD domain config, such as BatchSetVodDomainConfigs, DescribeVodDomainConfigs, DeleteVodSpecificConfig, SetVodDomainCertificate, DescribeVodCertificateList, DescribeVodDomainCertificateInfo.
+- Add a new field named AppId to some apis supporting the VoD App feature, such as AddWorkFlow, GetWorkFlow, ListWorkFlow, AddVodTemplate, GetVodTemplate, ListVodTemplate, AddTranscodeTemplateGroup, GetTranscodeTemplateGroup, ListTranscodeTemplateGroup, AddWatermark, GetWatermark, ListWatermark, UploadMediaByURL.
+- Add a new field named UserData to SubmitTranscodeJobs api request to support user-defined extension fields, which can be used for transparent return when callbacks.
+
+2019-06-21 Version: 1.60.51
+- Support DeleteProtection api.
+
+2019-06-20 Version: 1.60.50
+- OOS SDK first release for managing of templates, executions and actions.
+
+2019-06-19 Version: 1.60.49
+1, Add DefaultPolicyVersion as return field to GetPolicy interface, Facilitating to get policy document from this interface.
+2, Add RotateStrategy as input field to CreatePolicyVersion interface for rotating policy version when reaching policy version limit.
+
+2019-06-18 Version: 1.60.48
+1. Supported the related recommend.
+2. Supported exposure time controll and exposure filter by scene.
+
+2019-06-17 Version: 1.60.47
+companyreg release
+
+2019-06-14 Version: 1.60.46
+- Generated 2018-01-20 for `Iot`
+
+2019-06-14 Version: 1.60.45
+1. govendor rds sdk code problem.
+
+2019-06-13 Version: 1.60.44
+1, fixed DescribeAvailableResource OpenApi AvailableZones value problem.
+
+2019-06-13 Version: 1.60.43
+- Generated 2015-01-01 for `R-kvstore`
+
+2019-06-13 Version: 1.60.42
+Add Network Assistant openapi sdk
+
+2019-06-13 Version: 1.60.41
+1, add DescribeAvailableResource OpenApi.
+2, upgrade version to 2.3.8 
+
+2019-06-12 Version: 1.60.40
+1, Add RenewBackupPlan DBS interface.
+
+2019-06-12 Version: 1.60.39
+1.Fix bug
+
+2019-06-12 Version: 1.60.38
+1, Add InvokeDataAPIService interface, support invoke service of data api to get sql query result.
+2, Add GetDataAPIServiceDetail interface, support get data api's detail information.
+3, Add CreateDataAPIService interface, support create data api with sql statement.
+
+2019-06-06 Version: 1.60.37
+1, Add RuleId in DescribeRuleAttribute.
+
+2019-06-05 Version: 1.60.36
+1, Update DescribeDataLimitDetail interface, add password for response
+
+2019-06-04 Version: 1.60.35
+1, Add new API: UpdateLivePullStreamInfoConfig
+
+2019-06-03 Version: 1.60.34
+1, Add openapi of ACL.
+
+2019-06-03 Version: 1.60.33
+1, Order info add relatedOrderId.
+
+2019-05-31 Version: 1.60.32
+1, add api 'search traces count' 
+2, add page index for api 'searchTraces'
+
+2019-05-31 Version: 1.60.31
+1, Add authority in GetMaterials API.
+
+2019-05-30 Version: 1.60.30
+Fix: Handle the repeatlist parameter correctly
+
+2019-05-30 Version: 1.60.29
+1.Fix: Handle the repeatlist parameter correctly
+
+2019-05-30 Version: 1.60.28
+1. Re-generate for STS-2015-04-01.
+
+2019-05-30 Version: 1.60.27
+1. Re-generate for STS 2015-04-01.
+
+2019-05-29 Version: 1.60.26
+1, Fix ServiceCode typo in request
+
+2019-05-29 Version: 1.60.25
+1, Modify DescribeRestoreTaskList DBS interface.
+2, Modify DescribeFullBackupList DBS interface.
+
+2019-05-28 Version: 1.60.24
+1, CreateDBInstance now support autoRenew
+
+2019-05-27 Version: 1.60.23
+1, Support video transcode.
+
+2019-05-27 Version: 1.60.22
+1, Initial release of api, including data management api, rule configuration api and system configuration api.
+
+2019-05-24 Version: 1.60.21
+1, The list of site monitoring probes adds ISP and city fields in English.
+2, Fixed the capitalization problem of Describe MetricData and Describe Site Monitor Statistics API metricName.
+
+2019-05-23 Version: 1.60.20
+1, Fixed bug of the JSON list in GO sdk.
+
+2019-05-22 Version: 1.60.19
+1, Add EndTime parameter to api ModifyPrepayInstanceSpec
+2, Add RebootTime parameter to api ModifyPrepayInstanceSpec
+
+2019-05-20 Version: 1.60.18
+1, Added the cross zones supported.
+
+2019-05-20 Version: 1.60.17
+1, Add both api, ListNotaryInfos
+
+2019-05-20 Version: 1.60.16
+1, Initial release of api, including data management api, rule configuration api and system configuration api.
+
+2019-05-20 Version: 1.60.15
+1,  DescribeRouteTableList add route table status
+2,  DescribeRouteTables add route table  status
+
+2019-05-18 Version: 1.60.14
+1, Add ModifyBackupObjects DBS interface.
+
+2019-05-16 Version: 1.60.13
+1, Update SDK Version
+2, Change api UpgradeClusterAddons, UpgradeClusterComponents, DescribeClusterAddonsVersion to public
+
+
+2019-05-16 Version: 1.60.12
+1, Add some new apis to fetch the usage data from VoD, such as DescribeVodDomainUsageData, DescribeVodStorageData, DescribeVodTranscodeData, DescribeVodAIData.
+2, Add a new api named GetUploadDetails to describe the upload detail, such as uploading time and uploading source.
+3, Add a new api named GetAIVideoTagResult to describe the result of AI tag.
+4, Add new field Icon and OnlineStatus to some apis to manage short video materials, such as SearchMedia, GetAttachedMediaInfo, CreateUploadAttachedMedia.
+5, Add a new field RegionId to the response of GetAttachedMediaInfo and GetImageInfo.
+6, Add the field named UserData to SubmitSnapshotJob api request.
+
+2019-05-16 Version: 1.60.11
+1, Update SDK Version
+2, Change api UpgradeClusterAddons, UpgradeClusterComponents, DescribeClusterAddonsVersion to public
+
+
+2019-05-16 Version: 1.60.10
+1, Update SDK version
+2, Add api DescribeClusterAddonsVersion
+
+
+2019-05-16 Version: 1.60.9
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-05-16 Version: 1.60.8
+1, Scaling configuration add security group ids 
+
+2019-05-15 Version: 1.60.7
+1, Add InstanceId parameter to api CreateDisk
+2, Add parameter Affinity and Tenancy in ECS instance DedicatedHost related apis.
+3, Add SecurityGroupIds parameter to Instance creation apis.
+
+2019-05-14 Version: 1.60.6
+1, Support body detect.
+
+2019-05-14 Version: 1.60.5
+1, support body detect
+
+2019-05-14 Version: 1.60.4
+1, Support body detect.
+
+2019-05-14 Version: 1.60.3
+1, Support body detect.
+
+2019-05-14 Version: 1.60.2
+1, Support body detect.
+
+2019-05-14 Version: 1.60.1
+1, Support body detect.
+
+2019-05-13 Version: 1.60.0
+1, Add three new apis, SaveSingleTaskForSaveArtExtension, QueryArtExtension and CancelTask.
+
+2019-05-13 Version: 1.59.0
+1, Add three new apis, SaveSingleTaskForSaveArtExtension, QueryArtExtension and CancelTask.
+
+2019-05-13 Version: 1.58.11
+1, Support for creating instances and joining multiple security groups at the same time.
+
+2019-05-13 Version: 1.58.10
+1, Add Vpc openapi interface,Support for new fields.
+2, Support for nacl openapi.
+3, Fixed some problems of the vpc openapi.
+
+2019-05-13 Version: 1.58.9
+1, Modify CreateRestoreTaskRequest DBS interface.
+
+2019-05-05 Version: 1.58.8
+1, CreateInstance add ClientToken parameter.
+
+2019-04-29 Version: 1.58.7
+1, add new api
+
+2019-04-28 Version: 1.58.6
+1, Publish DescribeAvailableResource.
+
+2019-04-26 Version: 1.58.5
+1, Add resource describe api.
+
+2019-04-26 Version: 1.58.4
+1, add new openapi.
+
+2019-04-25 Version: 1.58.3
+1, The alarm contact interface increases paging parameters
+
+2019-04-25 Version: 1.58.2
+1, upgrade sdk version.
+
+2019-04-23 Version: 1.58.1
+1, Support mounting multiple volumes using different protocol
+2, Support scaling out cross AZ
+
+2019-04-23 Version: 1.58.0
+1, Add a new api, QueryDomainByDomainName.
+
+2019-04-22 Version: 1.57.1
+1, Add interface ModifyDBNodeClass.
+2, Add interface ModifyAutoRenewAttribute and DescribeAutoRenewAttribute.
+3, Param AutoRenew is supported in interface CreateDBCluster.
+
+2019-04-22 Version: 1.57.0
+1, Add a new api, QueryDomainByDomainName.
+
+2019-04-22 Version: 1.56.14
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-04-22 Version: 1.56.13
+1, Add CreateDBCluster interface;
+2, Add CreateDBNodes interface;
+
+2019-04-18 Version: 1.56.12
+1, New service
+
+2019-04-18 Version: 1.56.11
+1, CreateCacheAnalysisTask prop modify
+
+2019-04-18 Version: 1.56.10
+1, add ReservedInstance API.
+
+2019-04-17 Version: 1.56.9
+1, Add DescribeRestoreTaskList DBS interface.
+2, Add DescribeNodeCidrList DBS interface.
+
+2019-04-16 Version: 1.56.8
+1, Fixed DescribeAccounts response type error.
+
+2019-04-16 Version: 1.56.7
+1, Add api about pushing security check commands
+
+2019-04-16 Version: 1.56.6
+1, Fixed describeAccounts response type error
+
+2019-04-16 Version: 1.56.5
+1, Mark CreateDiagnosticReport as public api.
+2, ModifySecurityIps support modifyMode parameter.
+
+2019-04-16 Version: 1.56.4
+1, Add group api.
+
+2019-04-16 Version: 1.56.3
+1, Add group api.
+
+2019-04-16 Version: 1.56.2
+1, Add group api.
+
+2019-04-16 Version: 1.56.1
+1, Add group api.
+
+2019-04-16 Version: 1.56.0
+1, Add the go version of sdk.
+
+2019-04-15 Version: 1.55.7
+1, Add SDK for goLang version.
+
+2019-04-15 Version: 1.55.6
+1, Add CreateRestoreTask DBS interface.
+2, Add StartRestoreTask DBS interface.
+
+2019-04-15 Version: 1.55.5
+1, add arns parameter to CreateDisk/CreateInstance API in order to support disk encryption by performing a sts role play.
+
+2019-04-15 Version: 1.55.4
+1, Add apis, DescribeScreenHostStatistics、DescribeScreenSummaryInfo、DescribeScreenAttackAnalysisData、DescribeScreenAttackAnalysisData、DescribeScreenAttackAnalysisData
+
+2019-04-15 Version: 1.55.3
+1, Add Group Api.
+
+
+2019-04-11 Version: 1.55.2
+1, The ‘CreateProject’ interface adds the ‘ModelId’ parameter
+
+2019-04-11 Version: 1.55.1
+1, Add some new apis to manage VoD's App, such as CreateAppInfo, GetAppInfos, ListAppInfos, UpdateAppInfo, DeleteAppInfo, AttachAppPolicyToIdentity, DetachAppPolicyFromIdentity, ListAppPoliciesForIdentity and MoveAppResource.
+2, Add new apis SetMessageCallback, GetMessageCallback and DeleteMessageCallback to manage message callback configuration.
+3, Add new apis GetAttachedMedia, UpdateAttachedMediaInfos and DeleteAttachedMedia to manage attached media information.
+4, Add a new api named DeleteMultipartUpload to clean up the fragmented files generated during the upload process.
+5, Add a new field named AppId to some apis supporting the VoD App feature, such as CreateUploadVideo, SearchMedia, GetVideoInfo, GetVideoInfos; Add new fields named NonExistVideoIds and ForbiddenVideoIds to DeleteVideo api response; Add a new field named ForbiddenVideoIds to UpdateVideoInfos api response.
+6, The Editing supports multi-region and multi-materials. Add new fields named StorageLocation and RegionId in Project struct to GetEditingProject api response; Add new fields named StorageLocation, RegionId and Duration in ProjectList struct to SearchEditingProject api response; Add a new field named MaterialType to GetEditingProjectMaterials api request.
+7, Add new fields named TranscodeFileRegular, Clip and Rotate to GetTranscodeTemplateGroup api response to support the custom file path feature.
+8, Add a new field named AttachedMedia to api response to support the attached media feature.
+9, Add a new field named SubTotal to GetCategories response to show the number of sub category.
+10, Add a new field named AdditionType to GetVideoInfo and GetVideoInfos api request to fetch the custom media info and the corresponding field is CustomMediaInfo.
+
+2019-04-11 Version: 1.55.0
+1, Release AddImage/SearchImage/DeleteImage OpenApi.
+2, Add 'SearchByName' feature to SearchImage
+
+2019-04-10 Version: 1.54.1
+1, Modify DescribeBackupGatewayList DBS interface.
+
+2019-04-10 Version: 1.54.0
+1, New APIs for visual service (VNC).
+2, New APIs for software management.
+3, New APIs for CPFS filesystem.
+
+2019-04-09 Version: 1.53.93
+1, Add APIs: TagResource, UntagResource, ListResourceTags.
+2, Remove the optional parameter 'STSToken'  and replace it with 'SecurityToken'.
+
+2019-04-09 Version: 1.53.92
+1, support service account management networkInterface function.
+
+2019-04-09 Version: 1.53.91
+1, Add Apis for offline VerifySDK.
+
+2019-04-08 Version: 1.53.90
+1, Modify DescribeBackupPlanList DBS interface.
+2, Modify DescribeFullBackupList DBS interface.
+3, Modify DescribeIncrementBackupList DBS interface.
+4, Modify DescribeBackupGatewayList DBS interface.
+
+2019-04-08 Version: 1.53.89
+1, Add StopBackupPlan DBS interface.
+2, Add DescribeBackupPlanList DBS interface.
+3, Add DescribeFullBackupList DBS interface.
+4, Add DescribeIncrementBackupList DBS interface.
+5, Add DescribeBackupGatewayList DBS interface.
+
+2019-04-04 Version: 1.53.88
+1, ScalingConf systemDisk add DiskName and Description  parameters.
+2, ScalingConf dataDisk add DiskName & Description & Encrypted & KMSKeyId  parameters.
+
+2019-04-03 Version: 1.53.87
+1, Support DNAT/ENI/Online user count features.
+
+
+2019-04-03 Version: 1.53.86
+1, Add interfaces related to vulnerability whitelist
+2, Add interfaces related to vulnerability auto-del configuration
+3, Add interfaces related to vulnerability fix advise
+4, Add interfaces searching and operating vulnerability
+
+2019-03-29 Version: 1.53.85
+1, fix MNS Query params
+
+2019-03-29 Version: 1.53.84
+1, Fix error version
+
+2019-03-29 Version: 1.53.83
+1, Add DLQ message openAPI.
+2, Add Query the subscription relational openAPI.
+3, Remove white list restrictions.
+
+2019-03-28 Version: 1.53.82
+1, EDAS serverless support Pandora application deployment in war/jar mode
+
+2019-03-28 Version: 1.53.81
+1, Add multiple language support in DescribeZones
+2, Add gray parameter in CreateDisk
+
+2019-03-28 Version: 1.53.80
+1, Add instance remark modify api.
+
+2019-03-28 Version: 1.53.79
+1, Add logo detect.
+
+2019-03-27 Version: 1.53.78
+1, Add Project interface.
+2, Add Tag interface.
+3, Add TrainData interface.
+4, Add Train interface.
+5, Add Iteration interface.
+6, Add Predict interface.
+7, Add PredictData interface.
+
+2019-03-27 Version: 1.53.77
+1, Add Project interface.
+2, Add Tag interface.
+3, Add TrainData interface.
+4, Add Train interface.
+5, Add Iteration interface.
+6, Add Predict interface.
+7, Add PredictData interface.
+
+2019-03-27 Version: 1.53.76
+1, Add DLQ message openAPI.
+2, Add Query the subscription relational openAPI.
+3, Remove white list restrictions.
+
+2019-03-27 Version: 1.53.75
+1, Add DLQ message openAPI.
+2, Add Query the subscription relational openAPI.
+3, Remove white list restrictions.
+
+2019-03-21 Version: 1.53.74
+1, Update Dependency
+
+2019-03-21 Version: 1.53.73
+1,  Update Dependency
+
+2019-03-20 Version: 1.53.72
+1, Update the Dependency
+
+2019-03-20 Version: 1.53.71
+1, Update the Dependency
+
+2019-03-20 Version: 1.53.70
+1, Update the Dependency
+
+2019-03-20 Version: 1.53.69
+1, Update the Dependency
+
+2019-03-20 Version: 1.53.68
+1, Update Dependency
+
+2019-03-19 Version: 1.53.67
+1, Support ' fc ' Source DomainName
+
+2019-03-19 Version: 1.53.66
+1, move fields: tag, associate_person, cid
+
+2019-03-19 Version: 1.53.65
+1, format fields: tags, associatePerson
+
+2019-03-19 Version: 1.53.63
+1, Update Dependency
+
+2019-03-19 Version: 1.53.62
+1, Update Dependency
+
+2019-03-19 Version: 1.53.61
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-03-18 Version: 1.53.60
+1, Signature Document SDK.
+2, First version publish.
+3, Beta Version SDK
+
+2019-03-18 Version: 1.53.59
+1, Update Dependency
+
+2019-03-18 Version: 1.53.58
+1, move AssociatePerson\Tag field
+2, Optimize RealName field
+
+2019-03-18 Version: 1.53.57
+1, Update Dependency
+
+2019-03-18 Version: 1.53.56
+1, Update Dependency
+
+2019-03-18 Version: 1.53.55
+1, Update Dependency
+
+2019-03-18 Version: 1.53.54
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.53
+1,  update api info
+
+2019-03-15 Version: 1.53.52
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.51
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.50
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.49
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.48
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.47
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.46
+1, update RemoveInstances, support  to delete 50 instances at a time. 
+
+2019-03-15 Version: 1.53.45
+1, Update Dependency
+
+2019-03-15 Version: 1.53.44
+1,  Update Dependency
+
+2019-03-15 Version: 1.53.43
+1, Update Dependency
+
+2019-03-15 Version: 1.53.42
+1, Update Dependency
+
+2019-03-14 Version: 1.53.41
+1, Update Dependency
+
+2019-03-14 Version: 1.53.40
+1, Update Dependency
+
+2019-03-14 Version: 1.53.39
+1, Update Dependency
+
+2019-03-14 Version: 1.53.38
+1, Update Dependency
+
+2019-03-14 Version: 1.53.37
+1,  Update Dependency
+
+2019-03-14 Version: 1.53.36
+1, Update Dependency
+
+2019-03-14 Version: 1.53.35
+1, Update Dependency
+
+2019-03-14 Version: 1.53.34
+1, Update Dependency
+
+2019-03-14 Version: 1.53.33
+1, Update Dependency
+
+2019-03-13 Version: 1.53.32
+1, Update Dependency
+
+2019-03-13 Version: 1.53.31
+1, Update Dependency
+
+2019-03-13 Version: 1.53.30
+1, Update Dependency
+
+2019-03-13 Version: 1.53.29
+1, Distinguish between system and service parameters
+
+2019-03-13 Version: 1.53.28
+1, add DescribeDemands interface
+
+2019-03-13 Version: 1.53.27
+1, Distinguish between system and service parameters
+
+2019-03-13 Version: 1.53.26
+1, Distinguish between system and service parameters
+
+2019-03-12 Version: 1.53.25
+1, Add application name support for config center APIs.
+2, Add GetCluster API.
+3, Support debug switch in user region APIs.
+
+2019-03-12 Version: 1.53.24
+1, Add Vpc openapi interface,Support for new fields.
+2, Support for ipv6 gateway openapi.
+3, Fixed some problems of the vpc openapi.
+
+2019-03-12 Version: 1.53.23
+1,  add agency infomation
+2,  update sdk core resources
+
+2019-03-11 Version: 1.53.22
+1,  add agency infomation
+2,  update sdk core resources
+
+2019-03-11 Version: 1.53.21
+1, Add face grouping feature .
+2, Add head pose and face quality.
+
+
+2019-03-11 Version: 1.53.20
+1, Update aliyun-java-sdk-core version.
+
+2019-03-08 Version: 1.53.19
+1, Publish instance spec describe api.
+
+2019-03-08 Version: 1.53.18
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-03-07 Version: 1.53.17
+1, Fix go api miss service_code problem
+
+2019-03-07 Version: 1.53.16
+1, Change service code to acr
+
+2019-03-07 Version: 1.53.15
+1, Publish instance describe and release apis.
+
+2019-03-06 Version: 1.53.14
+1, Release ActionTrail Go API.
+
+2019-03-06 Version: 1.53.13
+1, Update Service Code.
+
+2019-03-05 Version: 1.53.12
+1, Update the SDK version to 1.2.4
+
+2019-03-04 Version: 1.53.11
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-03-01 Version: 1.53.10
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-03-01 Version: 1.53.9
+1, Support Build Function Compute Backend By SDK
+
+2019-03-01 Version: 1.53.8
+1, Add API QueryRegionConfig
+2, Add API SynchronizeResource
+
+2019-02-28 Version: 1.53.7
+1, Add new apis named ListTranscodeTask, GetTranscodeTask, GetTranscodeSummary.
+2, Add the new field named TranscodeTemplateIds and ForceDelGroup to DeleteTranscodeTemplateGroup api request, and add a new field named NonExistTranscodeTemplateIds to the api response.
+3, Add a new field named Rotate in VideoStream of Mezzanine struct to GetMezzanineInfo api response .
+4, Add a new field named Status in ImageInfo to GetImageInfo api response.
+5, Add a new field named CustomMediaInfo to UpdateVideoInfo, GetVideoInfo and SearchMedia api to support the custom mediaInfo feature.
+6, Add a new filed named PlayInfoList and some Audit fields to SearchMedia api response.
+7, Clean up an api named DeleteTranscodeTemplates, which is replaced with the api named DeleteTranscodeTemplateGroup.
+
+2019-02-27 Version: 1.53.6
+1, Add three APIs for tag. APIs : TagResources, UntagResources, ListTagResources.
+
+2019-02-27 Version: 1.53.5
+1, Update Config API.
+
+2019-02-26 Version: 1.53.4
+1, Industry brain v1.3.7 release
+2, Add asynchronous APIs
+3, version 1.3.7
+
+2019-02-26 Version: 1.53.3
+1, Add interface of tag.
+2, Add tag info in DescribeDBClusters and DescribeDBClusterAttribute.
+
+2019-02-25 Version: 1.53.2
+1, add opensearch to GO sdk
+
+2019-02-22 Version: 1.53.1
+1, Fixed QueryAvailableInstances response unmarshal problem
+
+2019-02-21 Version: 1.53.0
+1, New DeleteDeviceGroup interface.
+2, New DeleteDeviceAllGroup interface.
+3, New GroupId fields for QuerySyncPicSchedule input parameter.
+4, New GroupId fields for QueryAddUserInfo input parameter.
+
+2019-02-20 Version: 1.52.2
+1, Add code and message to inovkeService API
+2, User could check code and message to get more specific information
+3, 1.0.0 alpha release
+
+2019-02-18 Version: 1.52.1
+1, Initialization release SDK. EDAS now supports SDK implemented by current programming language.
+
+2019-02-18 Version: 1.52.0
+1, Instantiation: adding the property of instance.
+2, Replace the "ProducerId" and "ConsumerId" properties with "GROUP_ID".
+3, Remove parameter OnsRegionId to simplified usage.
+
+2019-02-18 Version: 1.51.28
+1, industrial-brain release.
+2, publish SDK to public package stores.
+3, 1.0.0 including all APIs in doc.
+
+2019-02-18 Version: 1.51.27
+1, Add DryRun into StartInstance, StopInstance and RebootInstance.
+2, Add snapshot operations: ExportSnapshot and ImportSnapshot
+
+2019-02-17 Version: 1.51.26
+1,  API definition support 'ForceNonceCheck', 'DisableInternet' parameter settings
+
+
+2019-02-15 Version: 1.51.25
+1, Modify DescribeDBInstances LastDowngradeTime DataType
+
+2019-02-14 Version: 1.51.24
+1, Update DescribeCdnTypes.
+
+2019-02-14 Version: 1.51.23
+1, Update DescribeTopDomainsByFlow.
+
+2019-01-30 Version: 1.51.22
+1, Add a new api called GetURLUploadInfos to query the upload task of UploadMediaByURL.
+2, Clean up the old api related to the CDN of VoD that is going offline, such as DescribeDomainBpsData, is replaced with the new CDN api.
+3, Clean up the old AI related apis of VoD, such as SubmitAIASRJob, is replaced with the new AI api.
+4, Clean up an api called GetVideoPlayInfo, which is used by the old player SDKs that are no longer supported.
+5, Clean up some apis that used only by the VoD console to avoid misuse, such as OpenVodService.
+
+2019-01-30 Version: 1.51.21
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-30 Version: 1.51.20
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-30 Version: 1.51.19
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-30 Version: 1.51.18
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-30 Version: 1.51.17
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-29 Version: 1.51.16
+1, Add a new pop interface to support deleting item by skuId which is defined by users to distinguish items
+
+2019-01-29 Version: 1.51.15
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2019-01-29 Version: 1.51.14
+1, Make obsolete api offline.
+2, Fix some bug.
+
+
+2019-01-28 Version: 1.51.13
+1, Release 3 new Alibaba Cloud Bill API(QueryBillOverview、QueryBill、QueryInstanceBill). 
+2, Kindly suggest replace to new APIs who is using the old ones(QueryMonthlyBill、QuerySettlementBill、QueryMonthlyInstanceConsumption).
+
+2019-01-28 Version: 1.51.12
+1, Support modify vSwitch of scalingGroup.
+2, Support new target tracking scaling rule.
+
+2019-01-28 Version: 1.51.11
+1, modify DescribeSlowLogs OpenApi.
+
+
+2019-01-23 Version: 1.51.10
+1, release new version
+
+2019-01-23 Version: 1.51.9
+1, release new version
+
+2019-01-23 Version: 1.51.8
+1, new go version
+
+2019-01-23 Version: 1.51.7
+1, Add model relevant parameter
+1, Add plugin relevant parameter
+
+2019-01-23 Version: 1.51.6
+1, Add model relevant parameter
+
+2019-01-23 Version: 1.51.5
+1, support plugin
+2, support model
+
+2019-01-23 Version: 1.51.4
+1, Add model relevant parameter
+
+2019-01-22 Version: 1.51.3
+1, Add model relevant parameters
+
+2019-01-22 Version: 1.51.2
+1, support Stack Policy setting .
+
+2019-01-21 Version: 1.51.1
+1, Interface DescribePluginApis returns the modification
+
+
+2019-01-17 Version: 1.51.0
+1, Add api AcceptInquiredSystemEvent.
+2, Add ExtendedAttribute to response of api DescribeInstanceHistoryEvents.
+3, Add ExtendedAttribute to response of api DescribeInstancesFullStatus.
+
+2019-01-16 Version: 1.50.10
+1, The plugin feature is released and supports a variety of plugins, as follows:trafficControl、backendSignature、ipControl、jwtAuth、cors、caching
+
+2019-01-15 Version: 1.50.9
+1, Add SubmitVerification API for RPMin solution.
+2, Return authority comparison score in GetStatus, SubmitMaterials and SubmitVerification.
+
+2019-01-15 Version: 1.50.8
+1, Go Lang API release
+2, Support UploadCertificate,CertficateList function;
+
+2019-01-15 Version: 1.50.7
+1, Add new apis called AddTranscodeTemplateGroup, UpdateTranscodeTemplateGroup, ListTranscodeTemplateGroup, GetTranscodeTemplateGroup, SetDefaultTranscodeTemplateGroup ,DeleteTranscodeTemplateGroup and DeleteTranscodeTemplates which support transcode template feature.
+2, Add new apis called AddAITemplate, DeleteAITemplate, UpdateAITemplate, GetAITemplate, ListAITemplate, GetDefaultAITemplate and SetDefaultAITemplate which support AI template feature.
+3, Add new apis called SubmitAIMediaAuditJob, GetAIMediaAuditJob, GetMediaAuditResult, GetMediaAuditResultTimeline which support AIMediaAudit feature.
+4, Add the field named Priority to SubmitTranscodeJobs api request.
+5, Add the field named UserData to UploadMediaByURL api request.
+6, Add the field named UserData to CreateUploadImage api request.
+7, Add the field named UserData to CreateUploadAttachedMedia api request.
+
+2019-01-14 Version: 1.50.6
+1, Release Go SDK for CR
+
+2019-01-11 Version: 1.50.5
+1, CreateOfficeConversionTask, ConvertOfficeFormat support Hidecomments
+
+2019-01-10 Version: 1.50.4
+1, regenerated for ECS/2014-05-26
+
+2019-01-10 Version: 1.50.3
+1, Add fields for QueryBrokerDemand api.
+
+2019-01-09 Version: 1.50.2
+1, Add user role support.
+
+2019-01-09 Version: 1.50.1
+1, Add user role support.
+2, Add new APIs for IMM.
+
+2019-01-07 Version: 1.50.0
+1, Airec SDK Release Version 1.0.0.
+
+2019-01-04 Version: 1.49.4
+1, modify DescribeSlowLogs, support query param SQLHASH, remove SQLID query param.
+2, modify DescribeSlowLogRecords, response values add SQLHASH, remove SQLID.
+3, upgrade rds sdk version 2.3.1.
+
+2019-01-04 Version: 1.49.3
+1, Change the DescribeInstance response esConfig List => Map
+
+2018-12-29 Version: 1.49.2
+1, re-generate go sdk for sts.
+
+2018-12-29 Version: 1.49.1
+1, re-generate Go SDK for RAM
+
+2018-12-29 Version: 1.49.0
+1, Add EnableUser interface, Support admin user to enable another user.
+2, Add DisableUser Interface, Support admin user to disable another user.
+3, Add DeleteUser Interface, Support admin user to delete another user.
+
+2018-12-29 Version: 1.48.8
+1, Add new set image video api
+
+2018-12-28 Version: 1.48.7
+1, Add new set image video api
+
+2018-12-28 Version: 1.48.6
+1, Add new set image video api
+
+2018-12-28 Version: 1.48.5
+1, Add new set image video api
+
+2018-12-28 Version: 1.48.4
+1, Add new set image video api.
+
+
+2018-12-28 Version: 1.48.3
+1, Add new set image video api.
+2, Add video async task api.
+
+2018-12-27 Version: 1.48.2
+1, fix php sdk
+
+2018-12-27 Version: 1.48.1
+1, Add DescribeZones Api.
+
+2018-12-27 Version: 1.48.0
+1, Elasticsearch GO sdk release.
+
+2018-12-25 Version: 1.47.3
+1, Add interface of endpoint.
+2, Add interface of cluster parameter.
+
+2018-12-21 Version: 1.47.2
+1, ARMS SDK 2018-12-19 go sdk upload
+
+2018-12-20 Version: 1.47.1
+1, Sync CDN API.
+
+2018-12-20 Version: 1.47.0
+1, Add Dns Sec apis.
+2, Add coupon and promotion fields for order apis.
+
+2018-12-20 Version: 1.46.0
+1, Add Dns Sec apis.
+2, Add coupon and promotion fields for order apis.
+
+2018-12-17 Version: 1.45.3
+1, Add EnableBillGeneration interface.
+2, Authorization for Virtual Network Operator.
+
+
+2018-12-17 Version: 1.45.2
+1, First Release
+
+2018-12-16 Version: 1.45.1
+1, Add a new api called AddMediaSequences to add media sequences of vod videos with in/out or live streams with start time/end time.
+
+2018-12-15 Version: 1.45.0
+1, first release
+2, this SDK is for control purposes, programmatic interface of HTTPDNS console: https://httpdns.console.aliyun.com
+
+2018-12-14 Version: 1.44.2
+1, fixed sdk unit loute .
+2, upgrade rds sdk version 2.2.0.
+
+2018-12-14 Version: 1.44.1
+1, fix service code to rds
+
+2018-12-13 Version: 1.44.0
+1, Add SystemDiskSize in CreateCluster and AddNodes, add more parameters in RecoverCluster
+
+2018-12-12 Version: 1.43.15
+1, Add interface GrantAccountPrivilege;
+2, Add interface RevokeAccountPrivilege;
+
+2018-12-12 Version: 1.43.14
+1, Sync CDN API.
+
+2018-12-12 Version: 1.43.13
+1, Fix a bug.
+
+2018-12-12 Version: 1.43.12
+1, Sync CDN API.
+
+2018-12-11 Version: 1.43.11
+1, Sync CDN API.
+
+2018-12-11 Version: 1.43.10
+1, Sync CDN API.
+
+2018-12-11 Version: 1.43.9
+1, Release Location Go SDK
+
+2018-12-11 Version: 1.43.8
+1, DescribeAccount support OwnerAccount params.
+2, Upgrade Rds SDK Version to 2.3.0
+
+2018-12-11 Version: 1.43.7
+1, Modify DescribeDBInstances OpenApi lastDowngradeTime dataType. 
+2, Upgrade SDK Version to 2.0.3.
+
+2018-12-11 Version: 1.43.6
+1, Sync CDN API.
+
+2018-12-11 Version: 1.43.5
+1, Add ModifyInstanceVpcAuthMode OpenApi.
+2, Upgrade SDK Version to 2.0.4.
+
+2018-12-06 Version: 1.43.4
+1, Add api RedeployInstance
+
+2018-12-06 Version: 1.43.3
+1, Add DescribeLiveDomainRealTimeBpsData, DescribeLiveDomainRealTimeHttpCodeData,DescribeLiveDomainRealTimeTrafficData.
+2, Update DescirbeCasterChannels and DescribeCasterStreamUrl.
+
+
+2018-12-05 Version: 1.43.2
+1, Scaling group support vServerGroup.
+
+2018-12-05 Version: 1.43.1
+1, Add image async job.
+2, Fix group bug.
+
+2018-12-04 Version: 1.43.0
+1, The first release of Alibaba Cloud BaaS SDK
+
+2018-12-03 Version: 1.42.10
+1, Sync CDN API.
+
+2018-12-03 Version: 1.42.9
+1, Sync CDN API.
+
+2018-12-03 Version: 1.42.8
+1, Update 2014-11-11 API.
+
+2018-12-03 Version: 1.42.7
+1,  Add a parameter to RemoveInstances.
+
+2018-11-30 Version: 1.42.6
+1, Add new apis called AddVodTemplate, UpdateVodTemplate, DeleteVodTemplate, ListVodTemplate, GetVodTemplate and SetDefaultVodTemplate which support vodtemplate feature.
+2, Add a new api called CreateUploadAttachedMedia to get upload auth for attached media
+3, Add new apis called AddWorkFlow, UpdateWorkFlow, DeleteWorkFlow, ListWorkFlow, GetWorkFlow  which support workflow feature.
+
+2018-11-29 Version: 1.42.5
+1, This is the first version of Uis service.
+
+2018-11-28 Version: 1.42.4
+1, Add RedeployInstance interface, and support to migrate ecs instance with specified maintenance events actively
+
+2018-11-27 Version: 1.42.3
+1, ConvertOfficeFormat support TgtFilePrefix, TgtFileSuffix, TgtFilePages, FitToPagesTall, FitToPagesWide
+
+2018-11-27 Version: 1.42.2
+1, add doc index api
+
+2018-11-27 Version: 1.42.1
+1, api for custom keyword lib、similartext lib、voice keyword lib
+2, api for custom image lib
+3, support client file detect for image detection scenes、voice detection scenes、video detection scenes、file detection scenes
+
+2018-11-26 Version: 1.42.0
+1, Add RegisterInstance interface, Support admin or DBA user to register new db instance.
+2, Add RegisterUser Interface, Support admin user to register new user.
+3, Add GetOpLog Interface, Support admin user to get operation log.
+
+2018-11-22 Version: 1.41.2
+1, Increase the scene field
+
+2018-11-22 Version: 1.41.1
+1, Provide attribute info for DescribePricingModule api.
+
+2018-11-22 Version: 1.41.0
+1, Add ScrollDomainList api.
+2, Add email filter for QueryRegistrantProfile.
+
+2018-11-22 Version: 1.40.4
+1, Add parameter DeletionProtection when creating instance and modifying instance attribute
+
+
+2018-11-21 Version: 1.40.3
+1, Add new apis called AddWatermark, UpdateWatermark, DeleteWatermark, ListWatermarks, GetWatermark and SetDefaultWatermark which support watermark feature.
+2, Add a new api called RegisterMedia which supports registration of audio and video media files that already exist in the OSS bucket.
+3, Add the field named OverrideParams to SubmitTranscodeJobs api request.
+
+2018-11-20 Version: 1.40.2
+1, Fix Compatibility Problems
+
+2018-11-20 Version: 1.40.1
+1, Add MNS STS Token Query API
+2, Add MNS Minimal Package
+
+2018-11-19 Version: 1.40.0
+1, Add field BargainSellerPrice and BargainSellerMobile
+
+2018-11-19 Version: 1.39.3
+1, Fixed some problems.
+
+2018-11-19 Version: 1.39.2
+1, Add query support for redeem
+
+2018-11-16 Version: 1.39.1
+1, update version
+
+2018-11-15 Version: 1.39.0
+1, ECS support ipv6Address
+
+2018-11-15 Version: 1.38.3
+1, update version
+
+2018-11-15 Version: 1.38.2
+1, update version.
+
+2018-11-15 Version: 1.38.1
+1, Remove FaceCompare, FaceRegist, FaceSearch api.
+2, Add DetectLogo api.
+3, CreateOfficeConversionTask support IdempotentToken.
+
+2018-11-14 Version: 1.38.0
+1, Better support for hybrid cluster.
+
+2018-11-13 Version: 1.37.0
+1, Add FuzzyMatchDomainSensitiveWord interface,Support fuzzy matching sensitive words.
+2, Add BatchFuzzyMatchDomainSensitiveWord interface,Support batch fuzzy matching sensitive words.
+3, Add DynamicCheck properties for results of CheckDomain interface.
+
+2018-11-13 Version: 1.36.0
+1, Add FuzzyMatchDomainSensitiveWord interface,Support fuzzy matching sensitive words.
+2, Add BatchFuzzyMatchDomainSensitiveWord interface,Support batch fuzzy matching sensitive words.
+3, Add DynamicCheck properties for results of CheckDomain interface.
+
+2018-11-10 Version: 1.35.4
+1, Update SetWaitingRoomConfig.
+
+2018-11-08 Version: 1.35.3
+1, Update Version.
+
+
+2018-11-07 Version: 1.35.2
+1, Add API CreateTemplate,DeleteTemplate,GetAllTemplate,GetTemplateInfo.
+2, Add API  GetTaskStatus,StartTask,StopTask.
+3, Add API GetTaskParam,UpdateTaskParam.
+
+
+2018-11-02 Version: 1.35.1
+1, deviceName and productKey can be used instead of iotId.
+2, New productKey fields for queryauthentication output parameter.
+3, New deviceName fields for queryauthentication output parameter.
+
+2018-11-02 Version: 1.35.0
+1, first version
+
+2018-10-31 Version: 1.34.13
+1, update
+
+2018-10-31 Version: 1.34.12
+1, Update SetWaitingRoomConfig.
+
+2018-10-31 Version: 1.34.11
+1, Add SetWaitingRoomConfig.
+
+2018-10-31 Version: 1.34.10
+1, Add SetWaitingRoomConfig.
+
+2018-10-31 Version: 1.34.9
+1, The DescribeDBInstanceAttribute add replicaSets response value.
+2, The DescribeDBInstances support engine query.
+
+2018-10-30 Version: 1.34.8
+1, Add API DescribeLiveDomainBpsData,DescribeLiveDomainTrafficData.
+
+
+2018-10-29 Version: 1.34.7
+1, Add ots trigger api interface.
+
+2018-10-29 Version: 1.34.6
+1, update
+
+2018-10-26 Version: 1.34.5
+1, Remove useless parameters QueryDomainAdminDivision api.
+
+2018-10-26 Version: 1.34.4
+1, Add apis for trademark domains.
+2, Retry publish SDK.
+
+2018-10-26 Version: 1.34.3
+1, Add apis for trademark domains.
+
+2018-10-26 Version: 1.34.2
+1, Return AuditConclusions in SubmitMaterials API.
+
+2018-10-25 Version: 1.34.1
+1, Add apis for trademark domains.
+
+2018-10-25 Version: 1.34.0
+1, Add apis for trademark domains.
+
+2018-10-25 Version: 1.33.0
+1, Add apis for trademark domains.
+2, Add QueryDomainAdminDivision api.
+
+2018-10-16 Version: 1.32.3
+1, This version add MetricQuery interface to support retcode and apm metric query.
+
+2018-10-16 Version: 1.32.2
+1, This version add MetricQuery interface to support retcode and apm metric query.
+
+2018-10-16 Version: 1.32.1
+1, This version add MetricQuery interface to support retcode and apm metric query.
+
+2018-10-16 Version: 1.32.0
+1, Delete deprecated and unusable apis : AddIpRange, UnbindIpRange, BindIpRange, DescribeIntranetAttributeKb, DescribeIpRanges, ModifyIntranetBandwidthKb, DescribeEventDetail, CheckAutoSnapshotPolicy, CheckDiskEnableAutoSnapshotValidation, DescribeAutoSnapshotPolicy
+2, Add instance topology api DescribeInstanceTopology
+3, Add mount point in DescribeDisksFullStatus
+
+
+2018-10-12 Version: 1.31.8
+1, ModifyCenAttribute supports ProtectionLevel.
+2, New APIs: PublishRouteEntries/WithdrawPublishedRouteEntries/DescribePublishedRouteEntries
+
+2018-10-11 Version: 1.31.7
+1, Add a new api called DeleteMezzanines to clear mezzanine infos and storages.
+2, Add the field called PlayConfig to GetVideoPlayAuth and GetPlayInfo api request.
+3, Add a new api called UpdateImageInfos to update image information.
+
+2018-10-10 Version: 1.31.6
+1, add UpdateProject api, support update CU, ServiceRole
+2, PutProject not support edit
+3, CreateOfficeConversionTask api now supports FitToPagesTall, FitToPagesWide
+4, Remove paramater Engines, Indexers from project apis
+
+2018-10-09 Version: 1.31.5
+1, v1.0.0-->v1.0.1
+2, provider new region:beijing/shenzhen/zhangjiakou
+3, provider service for VPC user;
+
+2018-09-30 Version: 1.31.4
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-09-29 Version: 1.31.3
+1, Sync cdn api.
+
+2018-09-29 Version: 1.31.2
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-09-29 Version: 1.31.1
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-09-27 Version: 1.31.0
+1, Add new API ListQueues, modify API SetAutoScaleConfig to support queue related features.
+
+2018-09-27 Version: 1.30.8
+1, modify getFaces Api
+
+2018-09-27 Version: 1.30.7
+1, modify getFaces Api
+
+2018-09-25 Version: 1.30.6
+1, Add GetHotlineRecord and QueryHotlineRecord interface, version 1.0.0
+
+2018-09-25 Version: 1.30.5
+1, Fix bug in GetVerifyToken and SubmitMaterials API.
+
+2018-09-22 Version: 1.30.4
+1, update
+
+2018-09-20 Version: 1.30.3
+1, Fix publish failure for Java, Python and C# SDK.
+
+2018-09-20 Version: 1.30.2
+1, BssOpenApi first deploy.
+
+2018-09-20 Version: 1.30.1
+1, BssOpenApi first deploy.
+
+2018-09-19 Version: 1.30.0
+1, Add ens api, include SaveSingleTaskForDisassociatingEns, SaveSingleTaskForAssociatingEns, QueryLocalEnsAssociation and QueryEnsAssociation.
+
+2018-09-17 Version: 1.29.10
+1, Add field for AddCasterVideoResource, DescribeCasterVideoResources, ModifyCasterVideoResource.
+
+
+2018-09-17 Version: 1.29.9
+1, describeRegions modify.
+
+2018-09-17 Version: 1.29.8
+1, ModifySecurityIps support WhitelistNetworkType 
+
+2018-09-16 Version: 1.29.7
+1, add CheckInstanceExist OpenApi.
+
+2018-09-14 Version: 1.29.6
+1, Add DedicatedHost Feature
+
+2018-09-13 Version: 1.29.5
+1, modify CheckDBInstance OpenApi
+
+2018-09-13 Version: 1.29.4
+1, Add CreateChannelToken.
+
+
+2018-09-11 Version: 1.29.3
+1, add CheckDBInstance OpenApi.
+
+2018-09-06 Version: 1.29.2
+1, fixed DescirbeRegions zoneId date type.
+
+2018-09-06 Version: 1.29.1
+1, AutoScaling support launchTemplate.
+
+2018-09-05 Version: 1.29.0
+1, Add new APIs with control policy for querying price
+
+2018-09-03 Version: 1.28.3
+1, voice identify interface
+2, client upload credentials interface
+
+2018-09-03 Version: 1.28.2
+1, Add new service API:FindServiceStatisticalData, which can support query service statistical data.
+
+2018-08-31 Version: 1.28.1
+1, The CreateInstance supported VPC IpAddress.
+
+2018-08-28 Version: 1.28.0
+1, Add new APIs for profiling application performance: GetCloudMetricProfiling, etc.
+2, Add new APIs to support Shifter container applications: AddContainerApp, etc.
+
+2018-08-28 Version: 1.27.6
+1, Add param for DescribeRegions,support AcceptLanguage,RegionEndpoint.
+
+2018-08-27 Version: 1.27.5
+1, add Ess alarm task api, CreateAlarm, DeleteAlarm, DescribeAlarms, DeleteAlarm, EnableAlarm, DisableAlarm
+
+
+2018-08-27 Version: 1.27.4
+1, createInstance supported IpAddress param.
+
+2018-08-24 Version: 1.27.3
+1, Add api: DetectQRCode
+
+2018-08-23 Version: 1.27.2
+1, RunInstance add privateIpAddress.
+
+2018-08-23 Version: 1.27.1
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-08-23 Version: 1.27.0
+1, Add CreateVpnGateway API.
+2, Add product Ipv6Translator.
+
+
+2018-08-22 Version: 1.26.2
+1, upgrade mongodb sdk.
+
+2018-08-22 Version: 1.26.0
+1, Add api CreateSimulatedSystemEvents, support creating one or more simulated system events.
+2, Add api CancelSimulatedSystemEvents, support cancelling one or more simulated system events.
+
+2018-08-21 Version: 1.25.8
+1, DeleteFaceSearchImageByIdRequest add field: srcUri(String).
+2, CreateFaceSetResponse  add field: faces(Long).
+3, GetFaceSetResponse add field: faces(Long).
+4, Fix class Blurness in IndexFaceResponse.
+
+2018-08-21 Version: 1.25.7
+1, Repair describeLaunchTemplateVersions securityEnhancementStrategy type
+
+2018-08-17 Version: 1.25.6
+1, Add a new api called DeleteImage to clear the image resource.
+2, Add the field called AdditionType and OutputType to GetMezzanineInfo api request.
+3, Add the field called OutputType to GetMezzanineInfo api response.
+4, Add the field called CreationTime and ModificationTime to GetPlayInfo api response.
+
+2018-08-17 Version: 1.25.5
+1, New apkpubkey fields for queryauthentication output parameter.
+2, New packagename fields for queryauthentication output parameter.
+3, New clientId fields for queryauthentication output parameter.
+
+2018-08-16 Version: 1.25.4
+1, ModifyScalingConfiguration add imageName.
+2, CreateScalingConfiguration add imageName.
+
+2018-08-15 Version: 1.25.3
+1, Fix bug in GetMaterials API.
+2, GetVerifyToken added a new param VerifyConfigs
+
+2018-08-15 Version: 1.25.2
+1, Add domain operation api AddLiveDomain,DeleteLiveDomain,DescribeLiveDomainDetail,StartLiveDomain,StopLiveDomain.
+2, Add certificate operation api DescribeLiveCertificateDetail,DescribeLiveCertificateList,SetLiveDomainCertificate.
+3, Add domain config api BatchSetLiveDomainConfigs,BatchDeleteLiveDomainConfigs,DeleteLiveSpecificConfig,DescribeLiveDomainConfigs.
+
+2018-08-15 Version: 1.25.1
+1, Update ecs tag to 20 maximum
+
+
+2018-08-13 Version: 1.25.0
+1, New userinfo fields for registerface input parameter.
+2, New userinfo fields for updateface input parameter.
+3, New userinfo fields for queryface output parameter.
+
+2018-08-13 Version: 1.24.7
+1, Add cross domain support.
+2, Add parameter SerialNumber to interface RebootSmartAccessGateway.
+
+2018-08-08 Version: 1.24.6
+1, The official release 4.0.0
+
+2018-08-08 Version: 1.24.5
+1, Update FC apis.
+
+2018-08-08 Version: 1.24.4
+1, First release.
+
+2018-08-05 Version: 1.24.3
+1, Add Interface SubmitSubtitleJob
+2, Support convert ttml、stl subtitle to vtt
+
+2018-08-04 Version: 1.24.2
+1, Add a new api called SetAuditSecurityIp to set audit security ip.
+2, Add a new api called ListAuditSecurityIp to query audit security ip list.
+3, Add a new api called UploadMediaByURL to bulk upload media based on urls.
+4, Add the field called StorageLocation and TemplateGroupId to GetVideoInfo api response.
+5, Add the field called StorageLocation and TemplateGroupId to GetVideoInfos api response.
+6, Add the field called OutputType and Status to GetPlayInfo api response.
+
+2018-08-04 Version: 1.24.1
+1, CreateOfficeConversionTask add TgtFilePages field to specify the final uploaded page
+
+2018-08-03 Version: 1.24.0
+1, Add new API AddLocalNodes, for adding local machine to a hybrid cluster
+
+2018-08-02 Version: 1.23.7
+1, Add parameter(GroupId) for CreateNotifyPolicy,GetNotifyPolicy,DeleteNotifyPolicy,ListNotifyPolicy.
+
+2018-08-01 Version: 1.23.6
+1, Add parameter(DryRun) for CreateAlarm and UpdateAlarm.
+
+2018-08-01 Version: 1.23.5
+1, Add api: MoPenQueryCanvas, MoPenDoRecognize, MoPenSendMqttMessage, MoPenFindGroup
+
+2018-08-01 Version: 1.23.4
+1, Support describePrice for market image, add return detailInfo in interface describePrice
+
+2018-07-26 Version: 1.23.3
+1, Add RemoveTerminals API.
+
+2018-07-26 Version: 1.23.2
+1, Add new API: SendDryRunSystemEvent,PutMetricAlarm,DescribeAlarmsForDimensions,DescribeAlarms.
+
+2018-07-26 Version: 1.23.1
+1, Add BatchGetJobMetricInfo API.
+2, Add BatchGetPluginConfigInfo API.
+3, Add GetJobTopology API.
+
+2018-07-13 Version: 1.23.0
+1, New APIs for batch executing commands in cluster: InvokeShellCommand, ListCommands, etc.
+2, New APIs for HybridCluster (link E-HPC with cluster in local IDC): CreateHybridCluster, etc.
+3, New APIs for Container applications: AddContainerApp, etc.
+
+2018-07-13 Version: 1.22.6
+1, Release 2016-06-20 TableStore Open API
+
+2018-07-11 Version: 1.22.5
+1, new function: Attach and Detach Rds instance of scalingGroup.
+
+
+2018-07-11 Version: 1.22.4
+1, Add DetectFaceAttributesService.
+2, Add cloudauthPageUrl to GetVerifyTokenResponse.
+
+2018-07-10 Version: 1.22.3
+1, Add new API: EnableEventRule,DisableEventRule,DeleteEventTargets,DeleteEventRule,ListEventRules.
+2, Modify the structured parameters for API: PutEventTargets,PutEventRule,DescribeEventRule.
+
+2018-07-09 Version: 1.22.2
+1, Add new API: DescribeEventRule,ListEventTargetsByRule,PutEventRule,PutEventTargets.
+2, Add return value(Id) for NodeProcessCreate.
+
+2018-07-05 Version: 1.22.1
+1, new function, attach/detach load balancer of scalingGroup
+
+2018-07-05 Version: 1.22.0
+1, A new optional parameter 'Perspectives' is introduced to the 'Chat' API. By filling this parameter when calling 'Chat', you'll get the knowledge base content within the specified perspectives.
+
+2018-06-28 Version: 1.21.1
+1, ScalingConfiguration support hostName and passwordInherit
+2, ScalingConfiguration support modify
+
+2018-06-27 Version: 1.21.0
+1, Modify QueryDomainRealNameVerificationInfo Api, add a return value IdentityCredentialUrl, which is domain real name verification image, you can download it via a HTTP get request,It has validity for 30 seconds.
+2, Modify QueryRegistrantProfileRealNameVerificationInfo Api, add a return value IdentityCredentialUrl, which is domain real name verification image, you can download it via a HTTP get request,It has validity for 30 seconds
+
+
+2018-06-27 Version: 1.20.6
+1, DescribeNetworkInterfaces support query with vpcId
+
+2018-06-22 Version: 1.20.5
+1, rtc openapi
+
+
+2018-06-22 Version: 1.20.4
+1, Add new API: DeleteGroupDynamicRule,ListGroupDynamicRule,ApplyTemplate,PutGroupDynamicRule.
+2, Add parameter(AlertIds) for CreateTask and ModifyTask.
+
+2018-06-21 Version: 1.20.3
+1, This is add InstanceList.
+
+2018-06-21 Version: 1.20.2
+1, This is add InstanceList.
+
+2018-06-20 Version: 1.20.1
+1, Add InstanceList in product security information query.
+
+2018-06-20 Version: 1.20.0
+1, This is the first version of jarvis-public.
+
+2018-06-20 Version: 1.19.6
+1, Add InstanceList in product security information query.
+
+2018-06-14 Version: 1.19.5
+1, Add passwordInherit.
+
+2018-06-13 Version: 1.19.4
+1, Add API AddCasterEpisodeGroupContent,CreateCaster.
+2, Add Parameter fillMode for AddCasterLayout,ModifyCasterLayout.
+3, Add return value fillMode DescribeCasterLayouts.
+4, Update ErrorCode for StartCaster,StartCasterScene,DescribeCasters,CopyCaster.
+5, Update ErrorCode for ModifyCasterLayout,DeleteCasterLayout,DeleteLiveAppRecordConfig.
+6, Update ForbidLiveStream,DescribeLiveStreamsPublishList,DescribeLiveStreamsOnlineList.
+
+2018-06-13 Version: 1.19.3
+1, Add lifecycleHook.
+
+2018-06-08 Version: 1.19.2
+1, Add scdn interface,support scdn.
+
+2018-06-06 Version: 1.19.1
+1, Change the type of srcUid to int.
+
+2018-06-06 Version: 1.19.0
+1, Add interface DescribePhoneInfo, DescribeDdosDefenseInfo, DescribeRiskListDetail, DescribePunishList.
+
+2018-05-30 Version: 1.18.1
+1, Add new API: DescribeContact and GetContacts.
+2, Add return value: alertRule for CreateTask.
+
+2018-05-29 Version: 1.18.0
+1, Add new interface DescribePhoneInfo to SDK.
+
+2018-05-28 Version: 1.17.0
+1, Add face 1-N,1-1 scan interface.
+
+2018-05-28 Version: 1.16.0
+1, ValidateSecurityGroup API
+
+2018-05-28 Version: 1.15.1
+1, add new openapi .
+
+2018-05-28 Version: 1.15.0
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-05-26 Version: 1.14.2
+1, Add new interface InstallCloudAssistant, support Cloud Assistant client installation.
+2, Add new interface DescribeCloudAssistantStatus, support Cloud Assistant client status detection.
+
+2018-05-25 Version: 1.14.1
+1, CreateAlarm,DeleteAlarm,UpdateAlarm,DisableAlarm,EnableAlarm,ListAlarm,ListAlarmHistory,ListContactGroup,PutMetricData,QueryMetricData,QueryMetricLast,QueryMetricList,QueryMetricTop remove useless parameter: callby_cms_owner. 
+2, QueryMetricList remove useless parameter: Page.
+3, UpdateMyGroupMembers add parameter: Readers.
+4, StopTasks,StartTasks,DeleteTasks add return value: Data.
+5, ModifyTask remove invalid parameter: TaskType.
+6, CreateTask fix bug: TaskName maybe duplicated.
+7, Add lost return value for CreateTask.
+8, DescribeISPAreaCity add parameter: isp and city.
+9, remove api: PutMetricData.
+
+2018-05-23 Version: 1.14.0
+1, Add CreateDrdsAccount API, to support creating account for all databases of a DRDS instance.
+
+2018-05-23 Version: 1.13.1
+1, Add Function Compute interface,support Function Compute.
+
+2018-05-23 Version: 1.13.0
+1, Update API version to 2018-04-12. 
+2, New APIs in this new version: StartCluster, StopCluster, RecoverCluster, StartNodes, StopNodes, ListCustomImages. Support stopping a post-paid cluster or partial nodes of a cluster to save cost.
+3, New parameters for CreateCluster API: EcsChargeType. Support creating a pre-paid cluster.
+4, New parameters for CreateCluster API: DeploymentMode. Support creating a cluster with fewer manager nodes.
+
+2018-05-23 Version: 1.12.5
+1, This is an example of release-log.
+2, Please strictly follow this format to edit in English.
+3, Format:Number + , + Space + Description
+
+2018-05-22 Version: 1.12.4
+1, add DescribeMigrateTasks,DescribeOssDownloads,CheckRecoveryConditions.
+2, modify DescribeDBInstanceAttribute.
+
+2018-05-17 Version: 1.12.3
+1, Supporting ethnicGroup and idCardStartDate fields in GetMaterials api
+
+2018-05-17 Version: 1.12.2
+1, the first version of private dns SDK
+
+2018-05-11 Version: 1.12.1
+1, Add Function Compute interface,support Function Compute.
+
+2018-05-09 Version: 1.12.0
+1, Add apis for domain broker.
+
+2018-05-08 Version: 1.11.3
+1, Add instance params.
+
+2018-05-08 Version: 1.11.2
+1, Remove DescribeAccountAttributes.
+
+2018-05-03 Version: 1.11.1
+1, Publish DRDS go sdk for the first time.
+2, Support for creation/modification/deletion/description operation for DRDS instances and databases.
+
+2018-05-02 Version: 1.11.0
+1, Add new interface InstallCloudAssistant, support Cloud Assistant client installation.
+2, Add new interface DescribeCloudAssistantStatus, support Cloud Assistant client status detection.
+
+2018-04-28 Version: 1.10.3
+1, Add api: DescribeTasks、StartTasks、StopTasks、DeleteTasks、ModifyTask、CreateTask、DescribeTaskDetail、DescribeISPAreaCity.
+
+2018-04-28 Version: 1.10.2
+1, Add dcdn domain interface,Support add、modify、delete、query dcdn domain.
+2, Add dcdn config interface,Support set、delete、query domain config.
+
+2018-04-27 Version: 1.10.1
+1, release hsm open api
+2, hsm open api includes DescribeRegions, DescribeInstances, ModityInstance, ConfigNetwork, ConfigWhiteList
+
+2018-04-26 Version: 1.10.0
+1, Add apis for domain group.
+3, Add fields for QueryDomainList.
+
+2018-04-25 Version: 1.9.6
+1, CreateMyGroups add parameter "options"
+
+2018-04-24 Version: 1.9.5
+1, Add api:UpdateMyGroupMembers
+
+2018-04-24 Version: 1.9.4
+1, add interface FetchPhotos
+2, add xflush log param unixTimestamp
+
+2018-04-23 Version: 1.9.3
+1, DescribeInstanceHistoryEvents adds parameter instanceEventTypes and instanceEventCycleStatuss.
+2, InstanceId parameter is not necessary for DescribeInstanceHistoryEvents now.
+3, DescribeInstancesFullStatus adds parameter instanceEventTypes.
+
+2018-04-23 Version: 1.9.2
+1, Add notificationConfiguration.
+2, Add standby status.
+
+2018-04-20 Version: 1.9.1
+1, Interface DescribeAlarmHistory/ListAlarmHistory Add return value instanceName.
+
+2018-04-17 Version: 1.8.3
+1, EditPhotos add input param TakenAt
+2, add FetchMomentPhotos
+3, ListAlbums add return param Remark
+
+2018-04-16 Version: 1.8.2
+1, Release go sdk
+
+
+2018-04-11 Version: 1.8.1
+1, GetLibrary/FetchLibraries add return ctime
+2, Editphotos add input param takenAt
+
+2018-04-10 Version: 1.8.0
+1, Add three interfaces CreateNetworkInterfacePermission DeleteNetworkInterfacePermission DescribeNetworkInterfacePermissions.
+
+2018-04-10 Version: 1.7.3
+1, publish Project API.
+2, publish Service API.
+3, publish Service order API.
+4, publish Credential API.
+5, publish CAS API.
+
+2018-04-09 Version: 1.7.2
+1, Add api: QueryMetricData, QueryMetricTop, TaskConfigCreate, TaskConfigDelete, TaskConfigEnable, TaskConfigList, TaskConfigModify, TaskConfigUnhealthy
+2, Remove api: PutSystemEvent 
+
+2018-04-08 Version: 1.7.0
+1, Add voice asynchronous scan interface.
+
+2018-04-04 Version: 1.6.1
+1, add tablestore trigger pop api and sample code
+
+2018-04-04 Version: 1.6.0
+1, Add APIs for domain transfer in and transfer out.
+2, Add APIs for poll and acknowledge domain task.
+
+2018-04-03 Version: 1.5.0
+1, Add APIs for domain transfer in and transfer out.
+2, Add APIs for poll and acknowledge domain task.
+3, Add API for query domain group list.
+
+2018-03-30 Version: 1.4.11
+1, API QueryCustomerSaleInfo arguments update.
+
+2018-03-29 Version: 1.4.10
+1, edit FetchLibraries return param format
+
+2018-03-29 Version: 1.4.9
+1, Add APIs: CreateAlias, UpdateAlias, DeleteAlias, ListAliases, ListAliasesByKeyId.
+2, Add APIs: GetParametersForImport, ImportKeyMaterial, DeleteKeyMaterial.
+3, Update KeyMetadata for CreateKey and DescribeKey.
+
+2018-03-29 Version: 1.4.8
+1, Cloud Enterprise Network SDK initial release.
+
+2018-03-29 Version: 1.4.7
+1, Cloud Enterprise Network SDK initial release.
+
+2018-03-27 Version: 1.4.6
+1, Rename QueryCustomerSaleInfo to RegionName.
+
+2018-03-27 Version: 1.4.5
+1, publish Project API.
+2, publish Service API.
+3, publish Service order API.
+4, publish Credential API.
+5, publish CAS API.
+
+2018-03-27 Version: 1.4.4
+1, add interface FetchLibraries
+
+2018-03-23 Version: 1.4.3
+1, interface DescribeInstanceTypes output InstancePpsRx InstancePpsTx
+
+2018-03-23 Version: 1.4.2
+1, interface DescribeInstanceTypes output InstancePpsRx InstancePpsTx
+
+2018-03-23 Version: 1.4.1
+1, ModifyPrepayInstanceSpec support migrateAcrossZone.
+
+2018-03-23 Version: 1.4.0
+1, Add 'QueryDevicesByAccount' and 'QueryDevicesByAlias'  Api.
+2, Remove 'QueryPushDetail' Api.
+
+2018-03-20 Version: 1.2.7
+1, add FetchAlbumTagPhotos
+2, CreatePhoto add optional param TakenAt
+
+2018-03-16 Version: 1.2.6
+1, Update GetQuotaInstance API.
+
+2018-03-16 Version: 1.2.5
+1, Synchronize to the latest api list
+
+
+2018-03-15 Version: 1.2.4
+1, Add QueryCustomerSaleInfo API.
+
+
+2018-03-09 Version:1.2.3
+1, fix bug:modify error message when roleArn is wrong
+
+2018-03-13 Version: 1.2.2
+1, API GetQuotaHistoryInfo fix field error.
+
+2018-03-09 Version: 1.2.1
+1, complete api comments
+
+2018-03-09 Version: 1.2.0
+1, improve endpoint resolver
+
+2018-03-07 Version: 1.1.7
+1, add GetAlbumsByNames
+2, add ListEvent/CreateEvent/EditEvent/DeleteEvent/GetEvent
+3, add return IdStr for string
+
+2018-03-06 Version: 1.1.6
+1, add GetAlbumsByNames
+2, add ListEvent/CreateEvent/EditEvent/DeleteEvent/GetEvent
+3, add return IdStr for string
+
+
+2018-03-06 Version: 1.1.5
+1, add GetAlbumsByNames
+2, add ListEvent/CreateEvent/EditEvent/DeleteEvent/GetEvent
+3, add return IdStr for string
+
+2018-02-28 Version: 1.1.3
+1, Add query resource inventory API.
+2, Add query topology API.
+
+2018-02-28 Version: 1.1.2
+1, Add query topology API.
+
+2018-02-27 Version: 1.1.1
+1, Add Action API.
+
+2018-02-27 Version: 1.1.0
+1, add new api DescribeClusters,DescribeClusterLogs,DescribeClusterNodes,UpgradeClusterComponents
+
+2018-02-12 Version: 1.0.0
+1. Optimize the nested structs in all response, this will bring the following incompatibilities:
+    * Api response struct changes, conclude:
+        * ecs.DescribeNetworkInterfaces
+        * ecs.DescribeDeploymentSetTopology
+        * rds.DescribeDBInstanceIPArrayList
+        * rds.DescribeDBInstancesAsCsv
+        * slb.DescribeLoadBalancersRelatedEcs
+        * vpc.DescribeVpcAttribute
+        * vpc.DescribeVirtualBorderRouters
+        * mts.RegisterMediaDetailPerson
+        * mts.QueryEditingJobList
+        * mts.SubmitEditingJobs
+        * mts.QueryMediaListByURL
+        * mts.ListJob
+        * mts.QueryMediaList
+        * mts.QueryJobList
+        * mts.SubmitJobs
+        * ehpc.DescribeCluster
+        * aegis.GetEntityList
+        * aegis.DescribeStratety
+
+
+2018-02-09 Version: 0.12.0
+1, Aliyun E-HPC service SDK, initial version.
+2, Include APIs of E-HPC clusters, nodes, users, jobs, job templates etc.
+
+2018-02-07 Version: 0.11.4
+1, add the access interface of logs such as vulnerabilities, baselines, exceptions, etc.
+
+2018-02-06 Version: 0.11.3
+1, ModifyInstanceChargeType add instanceChargeType param, support prepay instance to postpay instance.
+2, ModifyPrepayInstanceSpec add operatorType param, support downgrade prepay ecs.
+
+2018-02-02 Version: 0.11.2
+1, this version is new of captcha management and nvc analyze
+
+2018-01-31 Version: 0.11.1
+1. Fixed bugs that could cause signature errors while a ROA request contains lots of special symbols
+2. Optimization of the serverError.Error() interface
+3. When there is an error of "SignatureDoseNotMatch", it is now possible to tell user if the AccessKeySecret is wrong or caused by a sdk bug
+
+2018-01-29 Version: 0.11.0
+1, Support dep
+2, Add 4 usage of credentials for each services
+3, Standard interface naming
+
+2018-01-29 Version: 0.10.0
+1, Add 'notificationChannel' parameter to Push API
+
+2018-01-24 Version: 0.9.3
+1,  Fixed a problem that the ActiveAlert interface name is not standard
+2,  Update ActiveAlert interface : ListProductOfActiveAlert / DisableActiveAlert / EnableActiveAlert
+
+2018-01-24 Version: 0.9.2
+1, Add ActiveAlert API : ListActiveAlertRuleRequest/ListProductOfActiceAlertRequest/EnableActiceAlertRequest/DisableActiceAlertRequest
+
+2018-01-24 Version: 0.9.1
+1. improve: asyncWithChan avoid panic of "write on closed channel"
+2. improve: asyncWithChan support select case usage
+3. bug fix: repeated signature nonce while retry
+4. bug fix: calling request.GetUrl() ahead of client.DoAction() will cause problems
+
+2018-01-24 Version: 0.9.0
+1, Publish domain config interface, allow users to create or delete a domain protect config under DDoS Pro.
+2, Publish domain config query interface, allow users to query a list of domain config or a single domain config.
+3, Publish domain proxy modify interface, allow users to set domain transmit proxy.
+4, Publish domain transmit rule config interface, including modify and delete type.
+5, Publish domain black and white list config interface.
+6, Publish domain cname auto status config interface.
+7, Publish domain certificate upload interface.
+8, Publish domain qps graph query interface.
+9, Publish ip attack events and traffic graph query interface.
+
+
+2018-01-23 Version: 0.8.3
+1, Tesla Dam API release.
+2, Add ActionDiskCheck, ActionDiskMask, ActionDiskRma, HostGets.
+
+
+2018-01-18 Version: 0.8.2
+1. Set default timeout to 10s
+2. Make integration tests more stable
+3. Fix the problem of LocationResolver in concurrency
+4. Modifying the naming specification
+
+2018-01-18 Version: 0.8.1
+1, Update ECS go SDK to catch up with other langs.
+2, DescribeImageSupportInstanceTypes add new param Filter and ActionType
+
+2018-01-16 Version: 0.8.0
+1,  Add TriggerMode param in AddMediaWorkflow.
+2, Add TriggerMode param in QueryMediaWorkflowList/SearchMediaWorkflow/UpdateMediaWorkflow.
+3, Add interface UpdateMediaWorkflowTriggerMode.
+
+2018-01-11 Version: 0.7.8
+1. complete partly missed service codes
+2. add default http method for roa requests
+
+2018-01-11 Version: 0.7.7
+1, Bug fix: Replenish user interface
+
+2018-01-11 Version: 0.7.6
+1, replace photo tag 2000 upgrade to 5000
+2, add TrashQuota
+
+2018-01-11 Version: 0.7.5
+1, Make the json.Unmarshal more robust
+
+2018-01-09 Version: 0.7.4
+1, Add integration tests
+
+2018-01-04 Version: 0.7.3
+1, Remove get entity info API, replaced by get entity instance.
+
+2018-01-03 Version: 0.7.2
+1, Bug fix: fix the ecs unmarshal bug
+
+2018-01-03 Version: 0.7.1
+1, Bug fix: fix the unmarshal bug
+
+2017-12-29 Version: 0.7.0
+1, First release for Domain-intl.
+2, Add interfaces for domain name registration and management.
+
+2017-12-29 Version: 0.6.8
+1, Allow the setting of AcceptFormat in the Request.
+
+2017-12-26 Version: 0.6.5
+1, Add video AI service interface.
+
+2017-12-26 Version: 0.6.4
+1, Add video AI service interface.
+
+2017-12-20 Version: 0.6.3
+1, support tile dataset
+2, use dimension value type to replace isDrillDown
+
+2017-12-20 Version: 0.6.2
+1, support tile dataset
+2, use dimension value type to replace isDrillDown
+
+
+2017-12-19 Version: 0.6.1
+1, InactivatePhotos support customer inactiveTime
+2, support customer tags
+3, ListPhotoTags、ListTags add param lang
+
+2017-12-18 Version: 0.6.0
+1, Add Scdn API.
+
+2017-12-18 Version: 0.5.7
+1, This is the first release of CCC Open API.
+
+2017-12-15 Version: 0.5.6
+1, Update SetReqAuthConfig.
+2, Add Scdn API.
+
+2017-12-15 Version: 0.5.5
+1, fix test cases
+
+2017-12-15 Version: 0.5.4
+1, Add scdn API.
+2, Update SetReqAuthConfig.
+
+2017-12-12 Version: 0.5.1
+1, remove unused fmt.print
+
+2017-12-08 Version: 0.5.0
+1, Add ARMSQueryDataSet, WhereInDimQuery interface.
+
+2017-12-08 Version: 0.4.0
+1, support endpoint.xml resolver
+
+2017-12-08 Version: 0.3.1
+1, Add ARMSQueryDataSet, WhereInDimQuery interface.
+
+2017-12-01 Version: 0.2.2
+1, Fix ineffassign problems
+
+2017-12-01 Version: 0.2.1
+1, Fix capitalizes the first letter in each word of header
+
+2017-11-28 Version: 0.2.0
+1, AK2.0 & StsToken
+
+2017-11-27 Version: 0.1.3
+1, format ft code with gofmt
+
+2017-11-27 Version: 0.1.2
+1, format code with gofmt
+
+2017-11-27 Version: 0.1.1
+1, add glide files
+

+ 95 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/Gopkg.lock

@@ -0,0 +1,95 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
+  name = "github.com/davecgh/go-spew"
+  packages = ["spew"]
+  pruneopts = "UT"
+  revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
+  version = "v1.1.1"
+
+[[projects]]
+  branch = "master"
+  digest = "1:205b3c069003f9d1458e4304427ab3652d8d7bac3bde1568a2d5486451e453bd"
+  name = "github.com/goji/httpauth"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "2da839ab0f4df05a6db5eb277995589dadbd4fb9"
+
+[[projects]]
+  digest = "1:b87714e57a511d88f307aba7d5b63522da12bed0a050889c81272fc50f71100e"
+  name = "github.com/jmespath/go-jmespath"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "3433f3ea46d9f8019119e7dd41274e112a2359a9"
+  version = "0.2.2"
+
+[[projects]]
+  digest = "1:709cd2a2c29cc9b89732f6c24846bbb9d6270f28ef5ef2128cc73bd0d6d7bff9"
+  name = "github.com/json-iterator/go"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "27518f6661eba504be5a7a9a9f6d9460d892ade3"
+  version = "v1.1.7"
+
+[[projects]]
+  digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563"
+  name = "github.com/modern-go/concurrent"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
+  version = "1.0.3"
+
+[[projects]]
+  digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855"
+  name = "github.com/modern-go/reflect2"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd"
+  version = "1.0.1"
+
+[[projects]]
+  digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
+  name = "github.com/pmezard/go-difflib"
+  packages = ["difflib"]
+  pruneopts = "UT"
+  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+  version = "v1.0.0"
+
+[[projects]]
+  digest = "1:8548c309c65a85933a625be5e7d52b6ac927ca30c56869fae58123b8a77a75e1"
+  name = "github.com/stretchr/testify"
+  packages = ["assert"]
+  pruneopts = "UT"
+  revision = "221dbe5ed46703ee255b1da0dec05086f5035f62"
+  version = "v1.4.0"
+
+[[projects]]
+  digest = "1:6c56c50b13fd3cb33b692b264727c1c89198274f5dcabaa077e3b2472037e0f9"
+  name = "gopkg.in/ini.v1"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "d4cae42d398bc0095297fc3315669590d29166ea"
+  version = "v1.46.0"
+
+[[projects]]
+  digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
+  name = "gopkg.in/yaml.v2"
+  packages = ["."]
+  pruneopts = "UT"
+  revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
+  version = "v2.2.2"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  input-imports = [
+    "github.com/goji/httpauth",
+    "github.com/jmespath/go-jmespath",
+    "github.com/json-iterator/go",
+    "github.com/stretchr/testify/assert",
+    "gopkg.in/ini.v1",
+  ]
+  solver-name = "gps-cdcl"
+  solver-version = 1

+ 50 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/Gopkg.toml

@@ -0,0 +1,50 @@
+# Gopkg.toml example
+#
+# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#   name = "github.com/x/y"
+#   version = "2.4.0"
+#
+# [prune]
+#   non-go = false
+#   go-tests = true
+#   unused-packages = true
+
+
+[[constraint]]
+  branch = "master"
+  name = "github.com/goji/httpauth"
+
+[[constraint]]
+  name = "github.com/jmespath/go-jmespath"
+  version = "0.2.2"
+
+[[constraint]]
+  name = "github.com/json-iterator/go"
+  version = "1.1.7"
+
+[[constraint]]
+  name = "github.com/stretchr/testify"
+  version = "1.4.0"
+
+[[constraint]]
+  name = "gopkg.in/ini.v1"
+  version = "1.46.0"
+
+[prune]
+  go-tests = true
+  unused-packages = true

+ 201 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 9 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/Makefile

@@ -0,0 +1,9 @@
+
+all:
+
+fmt:
+	go fmt ./sdk ./integration ./services/...
+
+test:
+	go test -race -coverprofile=coverage.txt -covermode=atomic ./sdk/...
+	go tool cover -html=coverage.txt -o coverage.html

+ 142 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/README-CN.md

@@ -0,0 +1,142 @@
+[English](./README.md) | 简体中文
+
+<p align="center">
+<a href=" https://www.alibabacloud.com"><img src="https://aliyunsdk-pages.alicdn.com/icons/Aliyun.svg"></a>
+</p>
+
+<h1 align="center">Alibaba Cloud SDK for Go</h1>
+
+<p align="center">
+<a href="https://badge.fury.io/gh/aliyun%2Falibaba-cloud-sdk-go"><img src="https://badge.fury.io/gh/aliyun%2Falibaba-cloud-sdk-go.svg" alt="Latest Stable Version"></a>
+<a href="https://app.fossa.io/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go?ref=badge_shield"><img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go.svg?type=shield" alt="License"></a>
+<br/>
+<a href="https://codecov.io/gh/aliyun/alibaba-cloud-sdk-go"><img src="https://codecov.io/gh/aliyun/alibaba-cloud-sdk-go/branch/master/graph/badge.svg" alt="codecov"></a>
+<a href="https://travis-ci.org/aliyun/alibaba-cloud-sdk-go"><img src="https://travis-ci.org/aliyun/alibaba-cloud-sdk-go.svg?branch=master" alt="Travis Build Status"></a>
+<a href="https://ci.appveyor.com/project/aliyun/alibaba-cloud-sdk-go/branch/master"><img src="https://ci.appveyor.com/api/projects/status/gn17u48i53ktblfp/branch/master?svg=true" alt="Appveyor Build Status"></a>
+<a href="https://app.codacy.com/app/aliyun/alibaba-cloud-sdk-go?utm_source=github.com&utm_medium=referral&utm_content=aliyun/alibaba-cloud-sdk-go&utm_campaign=Badge_Grade_Dashboard"><img src="https://api.codacy.com/project/badge/Grade/291a39e242364b04ad442f0cce0e30d5" alt="Codacy Badge"></a>
+<a href="https://goreportcard.com/report/github.com/aliyun/alibaba-cloud-sdk-go"><img src="https://goreportcard.com/badge/github.com/aliyun/alibaba-cloud-sdk-go" alt="Go Report Card"></a>
+</p>
+
+欢迎使用 Alibaba Cloud SDK for Go。Alibaba Cloud SDK for Go 让您不用复杂编程即可访问云服务器、云监控等多个阿里云服务。
+这里向您介绍如何获取 [Alibaba Cloud SDK for Go][SDK] 并开始调用。
+
+## 在线示例
+[API Explorer][open-api] 提供在线调用阿里云产品,并动态生成 SDK 代码和快速检索接口等能力,能显著降低使用云 API 的难度。
+
+
+## 环境要求
+- 您的系统需要达到 [环境要求][Requirements], 例如,安装了不低于 1.10.x 版本的 Go 环境。
+
+## 安装
+使用 `go get` 下载安装 SDK
+
+```sh
+$ go get -u github.com/aliyun/alibaba-cloud-sdk-go/sdk
+```
+
+如果您使用了 glide 管理依赖,您也可以使用 glide 来安装 Alibaba Cloud SDK for Go
+
+```sh
+$ glide get github.com/aliyun/alibaba-cloud-sdk-go
+```
+
+另外,Alibaba Cloud SDK for Go 也会发布在 https://develop.aliyun.com/tools/sdk#/go 这个地址。
+
+## 快速使用
+在您开始之前,您需要注册阿里云帐户并获取您的[凭证](https://usercenter.console.aliyun.com/#/manage/ak)。
+
+### 创建客户端
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+
+func main() {
+
+	client, err := sdk.NewClientWithAccessKey("REGION_ID", "ACCESS_KEY_ID", "ACCESS_KEY_SECRET")
+	if err != nil {
+		// Handle exceptions
+		panic(err)
+	}
+}
+```
+
+### ROA 请求
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+
+func main() {
+	request := requests.NewCommonRequest()        // 构造一个公共请求
+	request.Method = "GET"                        // 设置请求方式
+	request.Product = "CS"                        // 指定产品
+	request.Domain = "cs.aliyuncs.com"            // 指定域名则不会寻址,如认证方式为 Bearer Token 的服务则需要指定
+	request.Version = "2015-12-15"                // 指定产品版本
+	request.PathPattern = "/clusters/[ClusterId]" // 指定ROA风格路径规则
+	request.ApiName = "DescribeCluster"           // 指定接口名
+	request.QueryParams["ClusterId"] = "123456"   // 设置参数值
+	request.QueryParams["RegionId"] = "region_id" // 指定请求的区域,不指定则使用客户端区域、默认区域
+	request.TransToAcsRequest()                   // 把公共请求转化为acs请求
+}
+```
+
+### RPC 请求
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+
+func main() {
+	request := requests.NewCommonRequest()                // 构造一个公共请求
+	request.Method = "POST"                               // 设置请求方式
+	request.Product = "Ecs"                               // 指定产品
+	request.Domain = "ecs.aliyuncs.com"                   // 指定域名则不会寻址,如认证方式为 Bearer Token 的服务则需要指定
+	request.Version = "2014-05-26"                        // 指定产品版本
+	request.ApiName = "CreateInstance"                    // 指定接口名
+	request.QueryParams["InstanceType"] = "ecs.g5.large"  // 设置参数值
+	request.QueryParams["RegionId"] = "region_id"         // 指定请求的区域,不指定则使用客户端区域、默认区域
+	request.TransToAcsRequest()                           // 把公共请求转化为acs请求
+}
+```
+
+
+## 文档
+* [Requirements](docs/0-Requirements-CN.md)
+* [Installation](docs/1-Installation-CN.md)
+* [Client](docs/2-Client-CN.md)
+* [SSL Verify](docs/3-Verify-CN.md)
+* [Proxy](docs/4-Proxy-CN.md)
+* [Timeout](docs/5-Timeout-CN.md)
+* [Debug](docs/6-Debug-CN.md)
+* [Logger](docs/7-Logger-CN.md)
+* [Concurrent](docs/8-Concurrent-CN.md)
+* [Asynchronous Call](docs/9-Asynchronous-CN.md)
+* [Package Management](docs/10-Package-Management-CN.md)
+
+
+## 问题
+[提交 Issue][issue] 不符合指南的问题可能会立即关闭。
+
+
+## 贡献
+提交 Pull Request 之前请阅读[贡献指南](CONTRIBUTING.md)。
+
+## 相关
+* [阿里云服务 Regions & Endpoints][endpoints]
+* [OpenAPI Explorer][open-api]
+* [Go][go]
+* [最新发行版本][latest-release]
+
+
+## 许可证
+[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go?ref=badge_large)
+
+[SDK]: https://github.com/aliyun/alibaba-cloud-sdk-go
+[apache]: http://www.apache.org/licenses/LICENSE-2.0
+[issue]: https://github.com/aliyun/alibaba-cloud-sdk-go/issues/new
+[open-api]: https://api.aliyun.com/
+[latest-release]: https://github.com/aliyun/alibaba-cloud-sdk-go/releases
+[go]: https://golang.org/dl/
+[endpoints]: https://developer.aliyun.com/endpoints
+[Requirements]: docs/0-Requirements-CN.md

+ 145 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/README.md

@@ -0,0 +1,145 @@
+English | [简体中文](README-CN.md)
+
+
+<p align="center">
+<a href=" https://www.alibabacloud.com"><img src="https://aliyunsdk-pages.alicdn.com/icons/AlibabaCloud.svg"></a>
+</p>
+
+<h1 align="center">Alibaba Cloud SDK for Go</h1>
+
+<p align="center">
+<a href="https://badge.fury.io/gh/aliyun%2Falibaba-cloud-sdk-go"><img src="https://badge.fury.io/gh/aliyun%2Falibaba-cloud-sdk-go.svg" alt="Latest Stable Version"></a>
+<a href="https://app.fossa.io/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go?ref=badge_shield"><img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go.svg?type=shield" alt="License"></a>
+<br/>
+<a href="https://codecov.io/gh/aliyun/alibaba-cloud-sdk-go"><img src="https://codecov.io/gh/aliyun/alibaba-cloud-sdk-go/branch/master/graph/badge.svg" alt="codecov"></a>
+<a href="https://travis-ci.org/aliyun/alibaba-cloud-sdk-go"><img src="https://travis-ci.org/aliyun/alibaba-cloud-sdk-go.svg?branch=master" alt="Travis Build Status"></a>
+<a href="https://ci.appveyor.com/project/aliyun/alibaba-cloud-sdk-go/branch/master"><img src="https://ci.appveyor.com/api/projects/status/gn17u48i53ktblfp/branch/master?svg=true" alt="Appveyor Build Status"></a>
+<a href="https://app.codacy.com/app/aliyun/alibaba-cloud-sdk-go?utm_source=github.com&utm_medium=referral&utm_content=aliyun/alibaba-cloud-sdk-go&utm_campaign=Badge_Grade_Dashboard"><img src="https://api.codacy.com/project/badge/Grade/291a39e242364b04ad442f0cce0e30d5" alt="Codacy Badge"></a>
+<a href="https://goreportcard.com/report/github.com/aliyun/alibaba-cloud-sdk-go"><img src="https://goreportcard.com/badge/github.com/aliyun/alibaba-cloud-sdk-go" alt="Go Report Card"></a>
+</p>
+
+
+Alibaba Cloud SDK for Go allows you to access Alibaba Cloud services such as Elastic Compute Service (ECS), Server Load Balancer (SLB), and CloudMonitor. You can access Alibaba Cloud services without the need to handle API related tasks, such as signing and constructing your requests.
+                         
+This document introduces how to obtain and call [Alibaba Cloud SDK for Go][SDK].
+
+## Online Demo
+[API Explorer][open-api] provides the ability to call the cloud product OpenAPI online, and dynamically generate SDK Example code and quick retrieval interface, which can significantly reduce the difficulty of using the cloud API.
+
+
+## Requirements
+- It's necessary for you to make sure your system meet the [Requirements][Requirements], such as installing a Go environment which is new than 1.10.x.
+
+## Installation
+Use `go get` to install SDK:
+
+```sh
+$ go get -u github.com/aliyun/alibaba-cloud-sdk-go/sdk
+```
+
+If you have used glide to manage dependence,you can also use glide to install Alibaba Cloud SDK for Go:
+
+```sh
+$ glide get github.com/aliyun/alibaba-cloud-sdk-go
+```
+
+## Quick Examples
+Before you begin, you need to sign up for an Alibaba Cloud account and retrieve your [Credentials](https://usercenter.console.aliyun.com/#/manage/ak).
+
+### Create Client
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+
+func main() {
+
+	client, err := sdk.NewClientWithAccessKey("REGION_ID", "ACCESS_KEY_ID", "ACCESS_KEY_SECRET")
+	if err != nil {
+		// Handle exceptions
+		panic(err)
+	}
+}
+```
+
+### ROA Request
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+
+func main() {
+	request := requests.NewCommonRequest()        // Make a common request
+	request.Method = "GET"                        // Set request method
+	request.Product = "CS"                        // Specify product
+	request.Domain = "cs.aliyuncs.com"            // Location Service will not be enabled if the host is specified. For example, service with a Certification type-Bearer Token should be specified
+	request.Version = "2015-12-15"                // Specify product version
+	request.PathPattern = "/clusters/[ClusterId]" // Specify path rule with ROA-style
+	request.Scheme = "https"                      // Set request scheme. Default: http
+	request.ApiName = "DescribeCluster"           // Specify product interface
+	request.QueryParams["ClusterId"] = "123456"   // Assign values to parameters in the path
+	request.QueryParams["RegionId"] = "region_id" // Specify the requested regionId, if not specified, use the client regionId, then default regionId
+	request.TransToAcsRequest()                   // Trans commonrequest to acsRequest, which is used by client.
+}
+```
+
+### RPC Request
+```go
+package main
+
+import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+
+func main() {
+	request := requests.NewCommonRequest()                // Make a common request
+	request.Method = "POST"                               // Set request method
+	request.Product = "Ecs"                               // Specify product
+	request.Domain = "ecs.aliyuncs.com"                   // Location Service will not be enabled if the host is specified. For example, service with a Certification type-Bearer Token should be specified
+	request.Version = "2014-05-26"                        // Specify product version
+	request.Scheme = "https"                              // Set request scheme. Default: http
+	request.ApiName = "CreateInstance"                    // Specify product interface
+	request.QueryParams["InstanceType"] = "ecs.g5.large"  // Assign values to parameters in the path
+	request.QueryParams["RegionId"] = "region_id"         // Specify the requested regionId, if not specified, use the client regionId, then default regionId
+	request.TransToAcsRequest()                           // Trans commonrequest to acsRequest, which is used by client.
+}
+```
+
+
+## Documentation
+* [Requirements](docs/0-Requirements-EN.md)
+* [Installation](docs/1-Installation-EN.md)
+* [Client](docs/2-Client-EN.md)
+* [SSL Verify](docs/3-Verify-EN.md)
+* [Proxy](docs/4-Proxy-EN.md)
+* [Timeout](docs/5-Timeout-EN.md)
+* [Debug](docs/6-Debug-EN.md)
+* [Logger](docs/7-Logger-EN.md)
+* [Concurrent](docs/8-Concurrent-EN.md)
+* [Asynchronous Call](docs/9-Asynchronous-EN.md)
+* [Package Management](docs/10-Package-Management-EN.md)
+
+
+## Issues
+[Opening an Issue][issue], Issues not conforming to the guidelines may be closed immediately.
+
+
+## Contribution
+Please make sure to read the [Contributing Guide](CONTRIBUTING.md) before making a pull request.
+
+
+## References
+* [Alibaba Cloud Regions & Endpoints][endpoints]
+* [OpenAPI Explorer][open-api]
+* [Go][go]
+* [Latest Release][latest-release]
+
+
+## License
+[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Faliyun%2Falibaba-cloud-sdk-go?ref=badge_large)
+
+[SDK]: https://github.com/aliyun/alibaba-cloud-sdk-go
+[issue]: https://github.com/aliyun/alibaba-cloud-sdk-go/issues/new
+[open-api]: https://api.aliyun.com/#/
+[latest-release]: https://github.com/aliyun/alibaba-cloud-sdk-go/releases
+[go]: https://golang.org/dl/
+[endpoints]: https://developer.aliyun.com/endpoints
+[Requirements]: docs/0-Requirements-EN.md

+ 2 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/doc.go

@@ -0,0 +1,2 @@
+// This file is created for depping ensure.
+package doc

+ 11 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/0-Requirements-CN.md

@@ -0,0 +1,11 @@
+[← 首页](../README-CN.md) | 环境要求[(English)](0-Requirements-EN.md) | [安装 →](1-Installation-CN.md)
+***
+
+## 要求
+- Go 环境版本必须不低于 1.10.x.
+
+## 建议
+- 请确保您的项目中没有其他依赖跟 Alibaba Cloud SDK for Go 存在冲突。你可以在[Gopkg.toml](../Gopkg.toml)查看相关的约束。
+
+***
+[← 首页](../README-CN.md) | 环境要求[(English)](0-Requirements-EN.md) | [安装 →](1-Installation-CN.md)

+ 11 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/0-Requirements-EN.md

@@ -0,0 +1,11 @@
+[← Home](../README.md) | Requirements[(中文)](0-Requirements-CN.md) | [Installation →](1-Installation-EN.md)
+***
+
+## Requirements
+- You must use Go 1.10.x or later.
+
+## Recommendations
+- Please make sure there will be no conflict between Alibaba Cloud SDK for Go and other. You can see specific constraints in [Gopkg.toml](../Gopkg.toml).
+
+***
+[← Home](../README.md) | Requirements[(中文)](0-Requirements-CN.md) | [Installation →](1-Installation-EN.md)

+ 24 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/1-Installation-CN.md

@@ -0,0 +1,24 @@
+[← 环境要求](0-Requirements-CN.md) | 安装[(English)](1-Installation-EN.md) | [客户端 →](2-Client-CN.md)
+***
+
+## 通过命令安装
+使用 `go get` 下载安装 SDK
+
+```sh
+$ go get -u github.com/aliyun/alibaba-cloud-sdk-go/sdk
+```
+
+如果您使用了 glide 管理依赖,您也可以使用 glide 来安装 Alibaba Cloud SDK for Go
+
+```sh
+$ glide get github.com/aliyun/alibaba-cloud-sdk-go
+```
+
+## 通过压缩文件安装
+Alibaba Cloud SDK for Go 中的一个 ZIP 文件包含运行开发工具包所需的所有类和依赖项。
+下载这个 [文件][Go-release], 然后在项目中的选定位置进行解压缩。
+
+***
+[← 环境要求](0-Requirements-CN.md) | 安装[(English)](1-Installation-EN.md) | [客户端 →](2-Client-CN.md)
+
+[Go-release]: https://github.com/aliyun/alibaba-cloud-sdk-go/releases

+ 25 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/1-Installation-EN.md

@@ -0,0 +1,25 @@
+[← Requirements](0-Requirements-EN.md) | Installation[(中文)](1-Installation-CN.md) | [Client →](2-Client-EN.md)
+***
+
+## Installation by Using command
+Use `go get` to install SDK:
+
+```sh
+$ go get -u github.com/aliyun/alibaba-cloud-sdk-go/sdk
+```
+
+If you have used glide to manage dependence,you can also use glide to install Alibaba Cloud SDK for Go:
+
+```sh
+$ glide get github.com/aliyun/alibaba-cloud-sdk-go
+```
+
+## Installing by Using the ZIP file
+The Alibaba Cloud SDK for Go includes a ZIP file containing all the classes and dependencies you need to run.
+
+Download the [file][Go-release], and then extract it into your project at a location you choose.
+
+***
+[← Requirements](0-Requirements-EN.md) | Installation[(中文)](1-Installation-CN.md) | [Client →](2-Client-EN.md)
+
+[Go-release]: https://github.com/aliyun/alibaba-cloud-sdk-go/releases

+ 24 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/10-Package-Management-CN.md

@@ -0,0 +1,24 @@
+[← 异步调用](9-Asynchronous-CN.md) | 包管理[(English)](10-Package-Management-EN.md) | [首页 →](../README-CN.md)
+***
+## 包管理
+
+Alibaba Cloud SDK for Go 支持两种方式的包管理.
+
+### dep
+
+在 alibaba-cloud-sdk-go 目录下执行以下命令:
+```bash
+# 当存在 Gopkg.lock 及 Gopkg.toml 时, 该指令会去拉取依赖包并放入 vendor 目录下.
+dep ensure
+```
+
+### go modules
+
+在 alibaba-cloud-sdk-go 目录下执行以下命令:
+```bash
+# 当存在 go.mod 及 go.sum 时, 该指令会去拉取依赖包并放入 $GOPATH/pkg/mod 目录下.
+go mod tidy
+```
+
+***
+[← 异步调用](9-Asynchronous-CN.md) | 包管理[(English)](10-Package-Management-EN.md) | [首页 →](../README-CN.md)

+ 25 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/10-Package-Management-EN.md

@@ -0,0 +1,25 @@
+[← Asynchronous Call](9-Asynchronous-EN.md) | Package Management[(中文)](10-Package-Management-CN.md) | [Home →](../README.md)
+***
+## Package Management
+
+Alibaba Cloud SDK for Go supports two ways for package management.
+
+### dep
+
+Execute the following command in the alibaba-cloud-sdk-go directory:
+```bash
+# When gopkg.lock and gopkg.toml exist, this instruction will pull the dependency package and put it into the vendor directory.
+dep ensure 
+```
+
+### go modules
+
+Execute the following command in the alibaba-cloud-sdk-go directory:
+```bash
+# When go.mod and go.sum exist, the command will pull the dependent package and put it into the $GOPATH/pkg/mod directory.
+go mod tidy
+```
+
+***
+[← Asynchronous Call](9-Asynchronous-EN.md) | Package Management[(中文)](10-Package-Management-CN.md) | [Home →](../README.md)
+ 

+ 116 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/2-Client-CN.md

@@ -0,0 +1,116 @@
+[← 安装](1-Installation-CN.md) | 客户端[(English)](2-Client-EN.md) | [SSL 验证 →](3-Verify-CN.md)
+***
+
+# 客户端
+您可以同时创建多个不同的客户端,每个客户端都可以有独立的配置,每一个请求都可以指定发送的客户端,如果不指定则使用默认客户端。客户端可以通过配置文件自动加载创建,也可以手动创建、管理。不同类型的客户端需要不同的凭证 `Credential`,内部也选取不同的签名算法 `Signature`,您也可以自定义客户端:即传入自定义的凭证和签名。
+## 客户端类型
+
+### AccessKey 客户端
+通过[用户信息管理][ak]设置AccessKey,它们具有该账户完全的权限,请妥善保管。有时出于安全考虑,您不能把具有完全访问权限的主账户 AccessKey 交于一个项目的开发者使用,您可以[创建RAM子账户][ram]并为子账户[授权][permissions],使用RAM子用户的 AccessKey 来进行API调用。 
+
+```go
+client, err := sdk.NewClientWithAccessKey("regionId", "accessKeyId", "accessKeySecret")
+
+```
+
+### STS 客户端
+通过安全令牌服务(Security Token Service,简称 STS),申请临时安全凭证(Temporary Security Credentials,简称 TSC),创建临时安全客户端。
+
+```go
+client, err := sdk.NewClientWithStsToken("regionId", "subaccessKeyId", "subaccessKeySecret", "stsToken")
+```
+
+
+### RamRoleArn 客户端
+通过指定[RAM角色][RAM Role],让客户端在发起请求前自动申请维护 STS Token,自动转变为一个有时限性的STS客户端。您也可以自行申请维护 STS Token,再创建 `STS客户端`。  
+> 示例代码:创建一个 RamRoleArn 方式认证的客户端。
+
+```go
+client, err := sdk.NewClientWithRamRoleArn("regionId", "subaccessKeyId", "subaccessKeySecret", "roleArn", "roleSession")
+```
+
+如果你想限制生成的 STS Token 的权限([构建Policy][policy]), 你可以使用如下方式创建客户端:
+```go
+client, err := sdk.NewClientWithRamRoleArnAndPolicy("regionId", "subaccessKeyId", "subaccessKeySecret", "roleArn", "roleSession", "policy")
+```
+
+
+### EcsRamRole 客户端
+通过指定角色名称,让客户端在发起请求前自动申请维护 STS Token,自动转变为一个有时限性的STS客户端。您也可以自行申请维护 STS Token,再创建 `STS客户端`。  
+> 示例代码:创建一个 EcsRamRole 方式认证的客户端。
+
+```go
+client, err := NewClientWithEcsRamRole("regionid", "roleName")
+```
+
+
+### Bearer Token 客户端
+如呼叫中心(CCC)需用此类认证方式的客户端,请自行申请维护 Bearer Token。  
+> 示例代码:创建一个 Bearer Token 方式认证的客户端。
+
+```go
+client, err := NewClientWithBearerToken("regionId", "bearerToken")
+```
+
+
+### RsaKeyPair 客户端
+通过指定公钥ID和私钥文件,让客户端在发起请求前自动申请维护 AccessKey,自动转变成为一个有时限性的AccessKey客户端,仅支持日本站。  
+> 示例代码:创建一个 RsaKeyPair 方式认证的客户端。
+
+
+```go
+client, err := NewClientWithRsaKeyPair("regionid", "publicKey", "privateKey", 3600)
+```
+
+## 自动创建客户端
+在发送请求前,如果没有创建任何客户端,将使用默认凭证提供程序链创建客户端,也可以自定义程序链。
+
+### 默认凭证提供程序链
+默认凭证提供程序链查找可用的客户端,寻找顺序如下:
+
+#### 1. 环境凭证
+程序首先会在环境变量里寻找环境凭证,如果定义了 `ALIBABA_CLOUD_ACCESS_KEY_ID`  和 `ALIBABA_CLOUD_ACCESS_KEY_SECRET` 环境变量且不为空,程序将使用他们创建默认客户端。如果请求指定的客户端不是默认客户端,程序会在配置文件中加载和寻找客户端。
+
+#### 2. 配置文件
+> 如果用户主目录存在默认文件 `~/.alibabacloud/credentials` (Windows 为 `C:\Users\USER_NAME\.alibabacloud\credentials`),程序会自动创建指定类型和名称的客户端。默认文件可以不存在,但解析错误会抛出异常。  客户端名称不分大小写,若客户端同名,后者会覆盖前者。也可以手动加载指定文件: `AlibabaCloud::load('/data/credentials', 'vfs://AlibabaCloud/credentials', ...);` 不同的项目、工具之间可以共用这个配置文件,因为超出项目之外,也不会被意外提交到版本控制。Windows 上可以使用环境变量引用到主目录 %UserProfile%。类 Unix 的系统可以使用环境变量 $HOME 或 ~ (tilde)。 可以通过定义 `ALIBABA_CLOUD_CREDENTIALS_FILE` 环境变量修改默认文件的路径。
+
+```ini
+[default]                          # 默认客户端
+type = access_key                  # 认证方式为 access_key
+access_key_id = foo                # Key
+access_key_secret = bar            # Secret
+
+[client1]                          # 命名为 `client1` 的客户端
+type = ecs_ram_role                # 认证方式为 ecs_ram_role
+role_name = EcsRamRoleTest         # Role Name
+
+[client2]                          # 命名为 `client2` 的客户端
+type = ram_role_arn                # 认证方式为 ram_role_arn
+access_key_id = foo
+access_key_secret = bar
+role_arn = role_arn
+role_session_name = session_name
+
+[client3]                          # 命名为 `client3` 的客户端
+type = rsa_key_pair                # 认证方式为 rsa_key_pair
+public_key_id = publicKeyId        # Public Key ID
+private_key_file = /your/pk.pem    # Private Key 文件
+
+```
+
+#### 3. 实例 RAM 角色
+如果定义了环境变量 `ALIBABA_CLOUD_ECS_METADATA` 且不为空,程序会将该环境变量的值作为角色名称,请求 `http://100.100.100.200/latest/meta-data/ram/security-credentials/` 获取临时安全凭证,再创建一个默认客户端。
+
+### 自定义凭证提供程序链
+可通过自定义程序链代替默认程序链的寻找顺序,也可以自行编写闭包传入提供者。
+```go
+client, err := sdk.NewClientWithProvider("regionId", ProviderInstance, ProviderProfile, ProviderEnv)
+```
+
+***
+[← 安装](1-Installation-CN.md) | 客户端[(English)](2-Client-EN.md) | [SSL 验证 →](3-Verify-CN.md)
+
+[ak]: https://usercenter.console.aliyun.com/#/manage/ak
+[ram]: https://ram.console.aliyun.com/users
+[permissions]: https://ram.console.aliyun.com/permissions
+[RAM Role]: https://ram.console.aliyun.com/#/role/list

+ 120 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/2-Client-EN.md

@@ -0,0 +1,120 @@
+[← Installation](1-Installation-EN.md) | Client[(中文)](2-Client-CN.md) | [SSL Verify →](3-Verify-EN.md)
+***
+
+# Client
+You may create multiple different clients simultaneously. Each client can have its own configuration, and each request can be sent by specified client. Use the Default Client if it is not specified. The client can be created by auto-loading of the configuration files, or created and managed manually. Different types of clients require different `Credential`,and different `Signature` algorithms that are selected. You may also customize the client: that is, pass in custom credentials and signatures.
+
+## Client Type
+
+### AccessKey Client
+Setup AccessKey through [User Information Management][ak], they have full authority over the account, please keep them safe. Sometimes for security reasons, you cannot hand over a primary account AccessKey with full access to the developer of a project. You may create a sub-account [RAM Sub-account][ram] , grant its [authorization][permissions],and use the AccessKey of RAM Sub-account to make API calls.
+> Sample Code: Create a client with a certification type AccessKey.
+
+```go
+client, err := sdk.NewClientWithAccessKey("regionId", "accessKeyId", "accessKeySecret")
+
+```
+
+
+### STS Client
+Create a temporary security client by applying Temporary Security Credentials (TSC) through the Security Token Service (STS).
+> Sample Code: Create a client with a certification type StsToken.
+
+```go
+client, err := sdk.NewClientWithStsToken("regionId", "subaccessKeyId", "subaccessKeySecret", "stsToken")
+```
+
+
+### RamRoleArn Client
+By specifying [RAM Role][RAM Role], the client will be able to automatically request maintenance of STS Token before making a request, and be automatically converted to a time-limited STS client. You may also apply for Token maintenance by yourself before creating `STS Client`.  
+> Sample Code: Create a client with a certification type RamRoleArn.
+
+```go
+client, err := sdk.NewClientWithRamRoleArn("regionId", "subaccessKeyId", "subaccessKeySecret", "roleArn", "roleSession")
+```
+
+If you want to limit the policy([How to make a policy][policy]) of STS Token, you can create a client as following:
+```go
+client, err := sdk.NewClientWithRamRoleArnAndPolicy("regionId", "subaccessKeyId", "subaccessKeySecret", "roleArn", "roleSession", "policy")
+```
+
+### EcsRamRole Client
+By specifying the role name, the client will be able to automatically request maintenance of STS Token before making a request, and be automatically converted to a time-limited STS client. You may also apply for Token maintenance by yourself before creating `STS Client`.  
+> Sample Code: Create a client with a certification type EcsRamRole.
+
+```go
+client, err := NewClientWithEcsRamRole("regionid", "roleName")
+```
+
+
+### Bearer Token Client
+If clients with this certification type are required by the Cloud Call Centre (CCC), please apply for Bearer Token maintenance by yourself.
+> Sample Code: Create a client with a certification type Bearer Token.
+
+```go
+client, err := NewClientWithBearerToken("regionId", "bearerToken")
+```
+
+
+### RsaKeyPair Client
+By specifying the public key ID and the private key file, the client will be able to automatically request maintenance of the AccessKey before sending the request, and be automatically converted to a time-limited AccessKey client. Only Japan station is supported. 
+> Sample Code: Create a client with a certification type RsaKeyPair.
+
+```go
+client, err := NewClientWithRsaKeyPair("regionid", "publicKey", "privateKey", 3600)
+```
+
+## Create the client automatically
+If no client is created before the request is sent, the client will be created using the default credential provider chain, or the program chain can be customized.
+
+### Default Credential Provider Chain
+The default credential provider chain looks for available clients, looking in the following order:
+
+#### 1. Environment Credentials
+The program first looks for environment credentials in the environment variable. If the `ALIBABA_CLOUD_ACCESS_KEY_ID` and `ALIBABA_CLOUD_ACCESS_KEY_SECRET` environment variables are defined and are not empty, the program will use them to create the default client. If the client specified by the request is not the default client, the program loads and looks for the client in the configuration file.
+
+#### 2. Credentials File
+> If there is `~/.alibabacloud/credentials` default file (Windows shows `C:\Users\USER_NAME\.alibabacloud\credentials`), the program will automatically create clients with the specified type and name. The default file may not exist, but a parse error throws an exception. The client name is case-insensitive, and if the clients have the same name, the latter will override the former. The specified files can also be loaded indefinitely: `AlibabaCloud::load('/data/credentials', 'vfs://AlibabaCloud/credentials', ...);` This configuration file can be shared between different projects and between different tools.  Because it is outside the project and will not be accidentally committed to the version control. Environment variables can be used on Windows to refer to the home directory %UserProfile%. Unix-like systems can use the environment variable $HOME or ~ (tilde). The path to the default file can be modified by defining the `ALIBABA_CLOUD_CREDENTIALS_FILE` environment variable.
+
+```ini
+[default]                          # Default client
+type = access_key                  # Certification type: access_key
+access_key_id = foo                # Key
+access_key_secret = bar            # Secret
+
+[client1]                          # Client that is named as `client1`
+type = ecs_ram_role                # Certification type: ecs_ram_role
+role_name = EcsRamRoleTest         # Role Name
+
+[client2]                          # Client that is named as `client2` 
+type = ram_role_arn                # Certification type: ram_role_arn
+access_key_id = foo
+access_key_secret = bar
+role_arn = role_arn
+role_session_name = session_name
+
+
+[client3]                          # Client that is named as `client3`
+type = rsa_key_pair                # Certification type: rsa_key_pair
+public_key_id = publicKeyId        # Public Key ID
+private_key_file = /your/pk.pem    # Private Key file
+
+```
+
+#### 3. Instance RAM Role
+If the environment variable `ALIBABA_CLOUD_ECS_METADATA` is defined and not empty, the program will take the value of the environment variable as the role name and request `http://100.100.100.200/latest/meta-data/ram/security-credentials/` to get the temporary Security credentials, then create a default client.
+
+### Custom Credential Provider Chain
+You can replace the default order of the program chain by customizing the program chain, or you can write the closure to the provider.
+```go
+client, err := sdk.NewClientWithProvider("regionId", ProviderInstance, ProviderProfile, ProviderEnv)
+```
+
+***
+[← Installation](1-Installation-EN.md) | Client[(中文)](2-Client-CN.md) | [SSL Verify →](3-Verify-EN.md)
+
+[ak]: https://usercenter.console.aliyun.com/#/manage/ak
+[ram]: https://ram.console.aliyun.com/users
+[policy]: https://www.alibabacloud.com/help/doc-detail/28664.htm?spm=a2c63.p38356.a3.3.27a63b01khWgdh
+[permissions]: https://ram.console.aliyun.com/permissions
+[RAM Role]: https://ram.console.aliyun.com/#/role/list

+ 32 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/3-Verify-CN.md

@@ -0,0 +1,32 @@
+[← 客户端](2-Client-CN.md) | SSL 验证[(English)](3-Verify-EN.md) | [代理 →](4-Proxy-CN.md)
+***
+
+# SSL 验证
+
+## 摘要
+请求时验证SSL证书行为。
+- 设置成 `true` 禁用证书验证,(这是不安全的,请设置证书!)。
+- 设置成 `false` 启用SSL证书验证,默认使用操作系统提供的CA包。
+
+## 默认值
+- `false` 
+
+## 设置
+### 通过请求设置
+```go
+// 设置请求 HTTPSInsecure (只影响当前)
+request.SetHTTPSInsecure(true)                           // 设置请求 HTTPSInsecure 为 true
+isInsecure := request.GetHTTPSInsecure()                 // 获取请求 HTTPSInsecure
+```
+
+### 通过客户端设置
+> 当请求未设置时,客户端设置才能生效.
+
+```go
+// 设置客户端 HTTPSInsecure (用于客户端发送的所有请求)。
+client.SetHTTPSInsecure(true)                         // 设置客户端 HTTPSInsecure 为 true
+isInsecure := client.GetHTTPSInsecure()               // 获取客户端 HTTPSInsecure
+```
+
+***
+[← 客户端](2-Client-CN.md) | SSL 验证[(English)](3-Verify-EN.md) | [代理 →](4-Proxy-CN.md)

+ 32 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/3-Verify-EN.md

@@ -0,0 +1,32 @@
+[← Client](2-Client-EN.md) | SSL Verify[(中文)](3-Verify-CN.md) | [Proxy →](4-Proxy-EN.md)
+***
+
+# SSL Verify
+
+## Summary
+Describes the SSL certificate verification behavior of a request.
+- Set `true` to disable certificate validation, (This is not safe, please set certificates! )
+- Set to `false` to enable SSL certificate verification and use the default CA bundle provided by operating system.
+
+## Default
+- `false` 
+
+## Setting
+### Setting on Request
+```go
+// Set request HTTPSInsecure(Only the request is effected.)
+request.SetHTTPSInsecure(true)                           // Set request HTTPSInsecure to true.
+isInsecure := request.GetHTTPSInsecure()                 // Get request HTTPSInsecure.
+```
+
+### Setting on Client
+> When the request is not set, the client settings are used.
+
+```go
+// Set client HTTPSInsecure(For all requests which is sent by the client.)
+client.SetHTTPSInsecure(true)                         // Set client HTTPSInsecure to true.
+isInsecure := client.GetHTTPSInsecure()               // Get client HTTPSInsecure.
+```
+
+***
+[← Client](2-Client-EN.md) | SSL Verify[(中文)](3-Verify-CN.md) | [Proxy →](4-Proxy-EN.md)

+ 31 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/4-Proxy-CN.md

@@ -0,0 +1,31 @@
+[← SSL 验证](3-Verify-CN.md) | 代理[(English)](4-Proxy-EN.md) | [超时 →](5-Timeout-CN.md)
+***
+
+# 代理
+
+## 描述
+当你需要使用代理来发送你的请求时,你可以通过设置环境变量或者通过客户端来设置代理。
+`HTTP_PROXY`: 仅对 http 请求有效。
+`HTTPS_PROXY`: 仅对 https 请求有效。
+`NO_PROXY`: NO_PROXY 中的 ip 或者域名不使用代理。
+
+## 设置
+
+### 通过环境变量设置
+你可以设置环境变量 `HTTP_PROXY`, `HTTPS_PROXY` 或者 `NO_PROXY` 。
+
+### 通过客户端设置
+```go
+// 客户端设置代理优先级比环境变量高
+client.SetHttpProxy("http://127.0.0.1:8080")   // 设置 Http 代理
+client.GetHttpProxy()                          // 获取 Http 代理.
+
+client.SetHttpsProxy("https://127.0.0.1:8080")   // 设置 Https 代理.
+client.GetHttpsProxy()                           // 获取 Https 代理.
+
+client.SetNoProxy("127.0.0.1,localhost")     // 设置代理白名单.
+client.GetNoProxy()                          // 获取代理白名单
+```
+
+***
+[← SSL 验证](3-Verify-CN.md) | 代理[(English)](4-Proxy-EN.md) | [超时 →](5-Timeout-CN.md)

+ 31 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/4-Proxy-EN.md

@@ -0,0 +1,31 @@
+[← SSL Verify](3-Verify-EN.md) | Proxy[(中文)](4-Proxy-CN.md) | [Timeout →](5-Timeout-EN.md)
+***
+
+# Proxy
+
+## Description
+When you need to use proxy to send your request, you can set environment variables or you can set them by client:
+`HTTP_PROXY`: Only the HTTP request to take effect.
+`HTTPS_PROXY`: Only the HTTPS request to take effect.
+`NO_PROXY`: The Ips or domains in it will not use proxy.
+
+## Setting
+
+### Setting by environment variables
+You can set environment variables `HTTP_PROXY`, `HTTPS_PROXY` or `NO_PROXY`
+
+### Setting by client
+```go
+// client proxy has a high priority than environment variables.
+client.SetHttpProxy("http://127.0.0.1:8080")   // Set Http Proxy.
+client.GetHttpProxy()                          // Get Http Proxy.
+
+client.SetHttpsProxy("https://127.0.0.1:8080")   // Set Https Proxy.
+client.GetHttpsProxy()                           // Get Https Proxy.
+
+client.SetNoProxy("127.0.0.1,localhost")     // Set No Proxy.
+client.GetNoProxy()                          // Get No Proxy.
+```
+
+***
+[← SSL Verify](3-Verify-EN.md) | Proxy[(中文)](4-Proxy-CN.md) | [Timeout →](5-Timeout-EN.md)

+ 35 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/5-Timeout-CN.md

@@ -0,0 +1,35 @@
+[← 代理](4-Proxy-CN.md) | 超时[(English)](5-Timeout-EN.md) | [调试 →](6-Debug-CN.md)
+***
+
+# 超时
+
+## 描述
+如果你想限制请求花费的时间,你可以通过请求或者客户端设置 `ConnectTimeout` 和 `ReadTimeout`。
+
+## 默认值
+- `defaultConnectTimeout`: 5 秒
+- `defaultReadTimeout`: 10 秒
+
+## 设置
+### 通过请求设置
+```go
+// 设置请求超时(仅对当前请求有效)
+request.SetReadTimeout(10 * time.Second)             // 设置请求读超时为10秒
+readTimeout := request.GetReadTimeout()              // 获取请求读超时
+request.SetConnectTimeout(5 * time.Second)           // 设置请求连接超时为5秒
+connectTimeout := request.GetConnectTimeout()        // 获取请求连接超时
+```
+
+### 通过客户端设置
+> 当请求未设置超时时,客户端设置的超时才会生效。
+
+```go
+// 设置客户端超时(对所有通过该客户端发送的请求生效)
+client.SetReadTimeout(10 * time.Second)             // 设置客户端读超时为10秒
+readTimeout := client.GetReadTimeout()              // 获取客户端读超时
+client.SetConnectTimeout(5 * time.Second)           // 设置客户端连接超时为5秒
+connectTimeout := client.GetConnectTimeout()        // 获取客户端连接超时
+```
+
+***
+[← 代理](4-Proxy-CN.md) | 超时[(English)](5-Timeout-EN.md) | [调试 →](6-Debug-CN.md)

+ 35 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/5-Timeout-EN.md

@@ -0,0 +1,35 @@
+[← Proxy](4-Proxy-EN.md) | Timeout[(中文)](5-Timeout-CN.md) | [Debug →](6-Debug-EN.md)
+***
+
+# Timeout
+
+## Description
+When you want to limit the time of request costing, you can set `ConnectTimeout` and `ReadTimeout` by request or client:
+
+## Default
+- `defaultConnectTimeout`: 5 * time.Second
+- `defaultReadTimeout`: 10 * time.Second
+
+## Setting
+### Setting on Request
+```go
+// Set request Timeout(Only the request is effected.)
+request.SetReadTimeout(10 * time.Second)             // Set request ReadTimeout to 10 second.
+readTimeout := request.GetReadTimeout()              // Get request ReadTimeout.
+request.SetConnectTimeout(5 * time.Second)           // Set request ConnectTimeout to 5 second.
+connectTimeout := request.GetConnectTimeout()        // Get request ConnectTimeout.
+```
+
+### Setting on Client
+> When the request is not set, the client settings are used.
+
+```go
+// Set client Timeout(For all requests which is sent by the client.)
+client.SetReadTimeout(10 * time.Second)              // Set client ReadTimeout to 10 second.
+readTimeout := client.GetReadTimeout()               // Get client ReadTimeout.
+client.SetConnectTimeout(5 * time.Second)            // Set client ConnectTimeout to 5 second.
+connectTimeout := client.GetConnectTimeout()         // Get client ConnectTimeout.
+```
+
+***
+[← Proxy](4-Proxy-EN.md) | Timeout[(中文)](5-Timeout-CN.md) | [Debug →](6-Debug-EN.md)

+ 8 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/6-Debug-CN.md

@@ -0,0 +1,8 @@
+[← 超时](5-Timeout-CN.md) | 调试[(English)](6-Debug-EN.md) | [日志 →](7-Logger-CN.md)
+***
+
+# 调试
+如果环境变量 `DEBUG=sdk` 存在, 所有的请求都将启用调试模式。
+
+***
+[← 超时](5-Timeout-CN.md) | 调试[(English)](6-Debug-EN.md) | [日志 →](7-Logger-CN.md)

+ 8 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/6-Debug-EN.md

@@ -0,0 +1,8 @@
+[← Timeout](5-Timeout-EN.md) | Debug[(中文)](6-Debug-CN.md) | [Logger →](7-Logger-EN.md)
+***
+
+# Debugging
+If there is an environment variable `DEBUG=sdk` , all requests will enable debug mode.
+
+***
+[← Timeout](5-Timeout-EN.md) | Debug[(中文)](6-Debug-CN.md) | [Logger →](7-Logger-EN.md)

+ 56 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/7-Logger-CN.md

@@ -0,0 +1,56 @@
+[← 调试](6-Debug-CN.md) | 日志[(English)](7-Logger-EN.md) | [并发 →](8-Concurrent-CN.md)
+***
+
+# 日志
+
+## 描述
+
+logger 主要用于提供支持审计的能力,用于记录每次的调用情况,类似服务端的 access log。
+
+## 使用
+
+### 初始化日志
+
+如果您想要使用日志功能,您需要先初始化一个日志对象,您可以在初始化日志对象的时候设置日志等级,日志模版, 日志的输出路径以及 channel。
+```go
+// level: 默认为 info
+// channel: 默认为 AlibabaCloud
+// file: 一个实现了 io.writer 接口的对象
+// templete: 日志的模板, 若不输入,则默认为 `{time} {channel}: "{method} {uri} HTTP/{version}" {code} {cost} {hostname}`
+client.SetLogger("level", "channel", file, templete)      // 设置客户端的日志, 当您调用该方法,默认为您开启日志功能
+```
+
+### 相关操作
+
+```go
+logger := client.GetLogger()    // 获取客户端的 logger 
+client.OpenLogger()            // 开启日志功能,若此时客户端的 logger 不存在, 则创建一个配置一个默认的 logger
+client.CloseLogger()           // 关闭日志功能
+client.GetLoggerMsg()          // 获取上一条日志信息,若此时客户端的 logger 不存在, 则创建一个配置一个默认的 logger 
+client.SetTemplate(templete)   // 设置日志模板,若此时客户端的 logger 不存在, 则创建一个配置一个默认的 logger
+client.GetTemplate()           // 获取当前的日志模板,若此时客户端的 logger 不存在, 则创建一个配置一个默认的 logger
+```
+
+### 变量
+
+|    变量    |   描述    |
+|----------|-------------|
+| {channel}     | 日志的对象 |
+| {host}     | 请求主机 |
+| {ts}     | GMT中的 ISO 8601日期 |
+| {method}     | 请求方法 |
+| {uri}     | 请求的URI |
+| {version}     | 协议版本 |
+| {target}     | 请求目标 (path + query) |
+| {hostname}     | 发送请求的计算机的主机名 |
+| {code}     | 响应的状态代码(如果可用) |
+| {error}     | 任何错误消息(如果有) |
+| {req_headers}     | 请求头 |
+| {res_headers}     | 响应头 |
+| {pid}     | PID |
+| {cost}     | 耗时 |
+| {start_time}  | 开始时间 |
+| {res_body}  | 响应主体 |
+
+***
+[← 调试](6-Debug-CN.md) | 日志[(English)](7-Logger-EN.md) | [并发 →](8-Concurrent-CN.md)

+ 57 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/7-Logger-EN.md

@@ -0,0 +1,57 @@
+[← Debug](6-Debug-EN.md) | Logger[(中文)](7-Logger-CN.md) | [Concurrent →](8-Concurrent-EN.md)
+***
+
+
+# Logger
+
+## Description
+
+The logger is mainly used to provide support for auditing, to record each call, similar to the server's access log.
+
+## Using
+
+### Init logger
+
+If you want to use the log function, you need to initialize a log object first. You can set the log level, log template, log output path and channel when initializing the log object.
+```go
+// level: default value is info
+// channel: default value is AlibabaCloud
+// file: should be an object that implements the io.writer interface
+// templete: logger template, If not entered, the default value is `{time} {channel}: "{method} {uri} HTTP/{version}" {code} {cost} {hostname}`
+client.SetLogger("level", "channel", file, templete)      // Set the client's log. When you call this method, the log function is enabled by default.
+```
+
+### Related operations
+
+```go
+logger := client.GetLogger()    // Get client logger 
+client.OpenLogger()            // Open logger, if clien logger is not exist, there will create a default logger for client
+client.CloseLogger()           // Close logger
+client.GetLoggerMsg()          // Get last logger message,if clien logger is not exist, there will create a default logger for client
+client.SetTemplate(templete)   // Set client logger template,if clien logger is not exist, there will create a default logger for client
+client.GetTemplate()           // Get client logger template,if clien logger is not exist, there will create a default logger for client
+```
+
+### Variables
+
+| Variables |  Description  |
+|----------|-------------|
+| {channel}     | name of the log |
+| {host}     | Host of the request |
+| {ts}     | GMT中的 ISO 8601日期 |
+| {method}     | Method of the request |
+| {uri}     | URI of the request |
+| {version}     | Protocol version |
+| {target}     | Request target of the request (path + query) |
+| {hostname}     | Hostname of the machine that sent the request |
+| {code}     | Status code of the response (if available) |
+| {error}     | Any error messages (if available) |
+| {req_headers}     | Request headers |
+| {res_headers}     | Response headers |
+| {pid}     | PID |
+| {cost}     | Cost Time |
+| {start_time}     | start Time |
+| {res_body}  | Response body |
+
+***
+[← Debug](6-Debug-EN.md) | Logger[(中文)](7-Logger-CN.md) | [Concurrent →](8-Concurrent-EN.md)

+ 29 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/8-Concurrent-CN.md

@@ -0,0 +1,29 @@
+[← 日志](7-Logger-CN.md) | 并发[(English)](8-Concurrent-EN.md) | [异步调用 →](9-Asynchronous-CN.md)
+***
+
+## 并发请求
+
+* 因 Go 语言的并发特性,我们建议您在应用层面控制 SDK 的并发请求。
+* 为了方便您的使用,我们也提供了可直接使用的并发调用方式,相关的并发控制由 SDK 内部实现。
+
+### 开启 SDK Client 的并发功能
+
+```go
+// 最大并发数
+poolSize := 2
+// 可缓存的最大请求数
+maxTaskQueueSize := 5
+
+// 在创建时开启异步功能
+config := sdk.NewConfig()
+            .WithEnableAsync(true)
+            .WithGoRoutinePoolSize(poolSize)            // 可选,默认5
+            .WithMaxTaskQueueSize(maxTaskQueueSize)     // 可选,默认1000
+ecsClient, err := ecs.NewClientWithOptions(config)
+
+// 也可以在client初始化后再开启
+client.EnableAsync(poolSize, maxTaskQueueSize)
+```
+
+***
+[← 日志](7-Logger-CN.md) | 并发[(English)](8-Concurrent-EN.md) | [异步调用 →](9-Asynchronous-CN.md)

+ 29 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/8-Concurrent-EN.md

@@ -0,0 +1,29 @@
+[← Logger](7-Logger-EN.md) | Concurrent[(中文)](8-Concurrent-CN.md) | [Asynchronous Call →](9-Asynchronous-EN.md)
+***
+
+## Concurrent Request
+
+* Due to the concurrency nature of the Go language, we recommend that you control the concurrent requests for the SDK at the application level.
+* In order to facilitate your use, we also provide a direct use of concurrent invocation mode, the relevant concurrency control by the SDK internal implementation.
+
+### Open SDK Client's concurrent function.
+
+```go
+// Maximum Running Vusers
+poolSize := 2
+// The maximum number of requests that can be cached
+maxTaskQueueSize := 5
+
+// Enable asynchronous functionality at creation time
+config := sdk.NewConfig()
+            .WithEnableAsync(true)
+            .WithGoRoutinePoolSize(poolSize)            // Optional,default:5
+            .WithMaxTaskQueueSize(maxTaskQueueSize)     // Optional,default:1000
+ecsClient, err := ecs.NewClientWithOptions(config)
+
+// It can also be opened after client is initialized
+client.EnableAsync(poolSize, maxTaskQueueSize)
+```
+
+***
+[← Logger](7-Logger-EN.md) | Concurrent[(中文)](8-Concurrent-CN.md) | [Asynchronous Call →](9-Asynchronous-EN.md)

+ 30 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/9-Asynchronous-CN.md

@@ -0,0 +1,30 @@
+[← 并发](8-Concurrent-CN.md) | 异步调用[(English)](9-Asynchronous-EN.md) | [包管理 →](10-Package-Management-CN.md)
+***
+## 异步调用
+
+### 发起异步调用
+Alibaba Cloud SDK for Go 支持两种方式的异步调用:
+
+1. 使用channel作为返回值
+    ```go
+    responseChannel, errChannel := client.FooWithChan(request)
+
+    // this will block
+    response := <-responseChannel
+    err = <-errChannel
+    ```
+
+2. 使用 callback 控制回调
+
+    ```go
+    blocker := client.FooWithCallback(request, func(response *FooResponse, err error) {
+        // handle the response and err
+    })
+
+    // blocker 为(chan int),用于控制同步,返回1为成功,0为失败
+    // 在<-blocker返回失败时,err依然会被传入的callback处理
+    result := <-blocker
+    ```
+
+***
+[← 并发](8-Concurrent-CN.md) | 异步调用[(English)](9-Asynchronous-EN.md) | [包管理 →](10-Package-Management-CN.md)

+ 30 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/docs/9-Asynchronous-EN.md

@@ -0,0 +1,30 @@
+[← Concurrent](8-Concurrent-EN.md) | Asynchronous Call[(中文)](9-Asynchronous-CN.md) | [Package Management →](10-Package-Management-EN.md)
+***
+## Asynchronous Call
+
+### Make an asynchronous call
+Alibaba Cloud Go SDK supports asynchronous calls in two ways:
+
+1. Using channel as return values
+    ```go
+    responseChannel, errChannel := client.FooWithChan(request)
+
+    // this will block
+    response := <-responseChannel
+    err = <-errChannel
+    ```
+
+2. Use callback to control the callback
+
+    ```go
+    blocker := client.FooWithCallback(request, func(response *FooResponse, err error) {
+        // handle the response and err
+    })
+
+    // blocker which is type of (chan int),is used to control synchronization,when returning 1 means success,and returning 0 means failure.
+    // When <-blocker returns failure,err also will be handled by afferent callback.
+    result := <-blocker
+    ```
+
+***
+[← Concurrent](8-Concurrent-EN.md) | Asynchronous Call[(中文)](9-Asynchronous-CN.md) | [Package Management →](10-Package-Management-EN.md)

+ 15 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/go.mod

@@ -0,0 +1,15 @@
+module github.com/aliyun/alibaba-cloud-sdk-go
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d
+	github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af
+	github.com/json-iterator/go v1.1.5
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
+	github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
+	github.com/stretchr/testify v1.3.0
+	gopkg.in/ini.v1 v1.42.0
+)
+
+go 1.13

+ 33 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/go.sum

@@ -0,0 +1,33 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q=
+github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
+gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

+ 272 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/api_test.go

@@ -0,0 +1,272 @@
+package integration
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/endpoints"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/airec"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/bssopenapi"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/cdn"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/cs"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/ram"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/rds"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/slb"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/vpc"
+	"github.com/stretchr/testify/assert"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"strings"
+	"testing"
+)
+
+var (
+	securityGroupId = ""
+	flag            = false
+)
+
+func Test_CreateDiversifyWithROArequestWithXMLWithGet(t *testing.T) {
+	client, err := airec.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := airec.CreateCreateDiversifyRequest()
+	request.SetDomain("airec.cn-hangzhou.aliyuncs.com")
+	request.SetContentType("XML")
+	request.SetScheme("HTTPS")
+	response, err := client.CreateDiversify(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_ScaleClusterWithROArequestWithXMLWithPUT(t *testing.T) {
+	client, err := cs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := cs.CreateScaleClusterRequest()
+	request.SetContentType("XML")
+	request.SetScheme("HTTPS")
+	response, err := client.ScaleCluster(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_CreateClusterTokenWithROArequestWithXMLWithPOST(t *testing.T) {
+	client, err := cs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := cs.CreateCreateClusterRequest()
+	request.SetContentType("XML")
+	request.SetScheme("HTTPS")
+	response, err := client.CreateCluster(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request body can't be empty")
+}
+
+func Test_DeleteClusterWithROArequestWithXMLWithDelete(t *testing.T) {
+	client, err := cs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := cs.CreateDeleteClusterRequest()
+	request.SetContentType("XML")
+	request.SetScheme("HTTPS")
+	response, err := client.DeleteCluster(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_DeleteClusterWithROArequestWithJSONWithDelete(t *testing.T) {
+	client, err := cs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := cs.CreateDeleteClusterRequest()
+	request.SetContentType("JSON")
+	request.SetScheme("HTTPS")
+	response, err := client.DeleteCluster(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_ScaleClusterWithROArequestWithJSONWithPUT(t *testing.T) {
+	client, err := cs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := cs.CreateScaleClusterRequest()
+	request.SetContentType("JSON")
+	request.SetScheme("HTTPS")
+	response, err := client.ScaleCluster(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_CreateSecurityGroupWithRPCrequestWithJSONWithNestingparametersWithPOST(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateCreateSecurityGroupRequest()
+	request.SetContentType("JSON")
+	tag := ecs.CreateSecurityGroupTag{
+		Key:   "test",
+		Value: "test",
+	}
+	request.Tag = &[]ecs.CreateSecurityGroupTag{tag}
+	response, err := client.CreateSecurityGroup(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, len(response.SecurityGroupId) > 0)
+	securityGroupId = response.SecurityGroupId
+}
+
+func Test_ECS_DescribeSecurityGroupsWithRPCrequestWithJSONWithNestingparametersWithGET(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateDescribeSecurityGroupsRequest()
+	request.SetContentType("JSON")
+	request.Method = requests.GET
+	response, err := client.DescribeSecurityGroups(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	for _, securitygroup := range response.SecurityGroups.SecurityGroup {
+		if securitygroup.SecurityGroupId == securityGroupId {
+			flag = true
+			break
+		}
+	}
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, flag)
+	flag = false
+
+}
+
+func Test_ECS_DeleteSecurityGroupWithRPCrequestWithJSONWithPOST(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateDeleteSecurityGroupRequest()
+	request.SetContentType("JSON")
+	request.SecurityGroupId = securityGroupId
+	response, err := client.DeleteSecurityGroup(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+	securityGroupId = ""
+}
+
+func Test_RDS_DescribeDBInstancesWithRPCrequest(t *testing.T) {
+	client, err := rds.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	assert.NotNil(t, client)
+	request := rds.CreateDescribeDBInstancesRequest()
+	request.SetContentType("JSON")
+	response, err := client.DescribeDBInstances(request)
+	assert.Nil(t, err)
+	assert.NotNil(t, response)
+}
+
+func Test_CDN_DescribeCdnDomainDetailWithRPCrequest(t *testing.T) {
+	client, err := cdn.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	assert.NotNil(t, client)
+	request := cdn.CreateDescribeRefreshTasksRequest()
+	response, err := client.DescribeRefreshTasks(request)
+	assert.Nil(t, err)
+	assert.NotNil(t, response)
+	assert.Equal(t, 36, len(response.RequestId))
+}
+
+func Test_RAM_ListRolesWithRPCrequest(t *testing.T) {
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ram.CreateListRolesRequest()
+	request.Scheme = "HTTPS"
+	response, err := client.ListRoles(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+}
+
+func Test_SLB_DescribeRegionsWithRPCrequest(t *testing.T) {
+	client, err := slb.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := slb.CreateDescribeRegionsRequest()
+	response, err := client.DescribeRegions(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, len(response.Regions.Region) > 0)
+}
+
+func Test_VPC_DescribeRegionsWithRPCrequest(t *testing.T) {
+	client, err := vpc.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := vpc.CreateDescribeRegionsRequest()
+	response, err := client.DescribeRegions(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, len(response.Regions.Region) > 0)
+}
+
+func mockServer(status int, json string) (server *httptest.Server) {
+	// Start a test server locally.
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(status)
+		w.Write([]byte(json))
+		return
+	}))
+	return ts
+}
+
+func Test_DescribeRegionsWithRPCrequestWithunicode(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "HTTP"
+	ts := mockServer(400, `{"Code": "&&&&杭州&&&"}`)
+	defer ts.Close()
+	domain := strings.Replace(ts.URL, "http://", "", 1)
+	request.Domain = domain
+	response, err := client.DescribeRegions(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Equal(t, "{\"Code\": \"&&&&杭州&&&\"}", response.GetHttpContentString())
+}
+
+func Test_DescribeRegionsWithRPCrequestWithescape(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "HTTP"
+	ts := mockServer(400, `{"Code": "\t"}`)
+	defer ts.Close()
+	domain := strings.Replace(ts.URL, "http://", "", 1)
+	request.Domain = domain
+	response, err := client.DescribeRegions(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Equal(t, "{\"Code\": \"\\t\"}", response.GetHttpContentString())
+}
+
+func Test_DescribeRegionsWithRPCrequestWith3XX(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "HTTP"
+	ts := mockServer(307, `{"error"}`)
+	defer ts.Close()
+	domain := strings.Replace(ts.URL, "http://", "", 1)
+	request.Domain = domain
+	response, err := client.DescribeRegions(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 307, response.GetHttpStatus())
+	assert.Equal(t, "{\"error\"}", response.GetHttpContentString())
+}
+
+func Test_QueryAvaliableInstances(t *testing.T) {
+	client, err := bssopenapi.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := bssopenapi.CreateQueryAvailableInstancesRequest()
+	endpoints.AddEndpointMapping(os.Getenv("REGION_ID"), "BssOpenApi", "business.aliyuncs.com")
+	response, err := client.QueryAvailableInstances(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+	assert.Equal(t, 36, len(response.RequestId))
+}

+ 206 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/base.go

@@ -0,0 +1,206 @@
+package integration
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/ram"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
+
+	"fmt"
+	"os"
+	"strings"
+)
+
+var role_doc = `{
+		"Statement": [{
+		    "Action": "sts:AssumeRole",
+		    "Effect": "Allow",
+		    "Principal": {
+		     	"RAM": [
+				      "acs:ram::%s:root"
+		        ]
+            }
+	    }],
+	   "Version": "1"
+	}`
+
+var (
+	travisValue = strings.Split(os.Getenv("TRAVIS_JOB_NUMBER"), ".")
+	username    = "test-go-user" + travisValue[len(travisValue)-1]
+	rolename    = "test-go-role" + travisValue[len(travisValue)-1]
+	rolearn     = fmt.Sprintf("acs:ram::%s:role/%s", os.Getenv("USER_ID"), rolename)
+)
+
+func createRole(userid string) (string, string, error) {
+	listRequest := ram.CreateListRolesRequest()
+	listRequest.Scheme = "HTTPS"
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	if err != nil {
+		return "", "", err
+	}
+	listResponse, err := client.ListRoles(listRequest)
+	if err != nil {
+		return "", "", err
+	}
+	for _, role := range listResponse.Roles.Role {
+		if strings.ToLower(role.RoleName) == rolename {
+			return role.RoleName, role.Arn, nil
+		}
+	}
+	createRequest := ram.CreateCreateRoleRequest()
+	createRequest.Scheme = "HTTPS"
+	createRequest.RoleName = rolename
+	createRequest.AssumeRolePolicyDocument = fmt.Sprintf(role_doc, userid)
+	res, err := client.CreateRole(createRequest)
+	if err != nil {
+		return "", "", err
+	}
+	return res.Role.RoleName, res.Role.Arn, nil
+}
+
+func createUser() error {
+	listRequest := ram.CreateListUsersRequest()
+	listRequest.Scheme = "HTTPS"
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	if err != nil {
+		return err
+	}
+	listResponse, err := client.ListUsers(listRequest)
+	if err != nil {
+		return err
+	}
+	for _, user := range listResponse.Users.User {
+		if user.UserName == username {
+			return nil
+		}
+	}
+	createRequest := ram.CreateCreateUserRequest()
+	createRequest.Scheme = "HTTPS"
+	createRequest.UserName = username
+	_, err = client.CreateUser(createRequest)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func createAttachPolicyToUser() error {
+	listRequest := ram.CreateListPoliciesForUserRequest()
+	listRequest.UserName = username
+	listRequest.Scheme = "HTTPS"
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	if err != nil {
+		return err
+	}
+	listResponse, err := client.ListPoliciesForUser(listRequest)
+	if err != nil {
+		return err
+	}
+	for _, policy := range listResponse.Policies.Policy {
+		if policy.PolicyName == "AliyunSTSAssumeRoleAccess" {
+			return nil
+		}
+	}
+	createRequest := ram.CreateAttachPolicyToUserRequest()
+	createRequest.Scheme = "HTTPS"
+	createRequest.PolicyName = "AliyunSTSAssumeRoleAccess"
+	createRequest.UserName = username
+	createRequest.PolicyType = "System"
+	_, err = client.AttachPolicyToUser(createRequest)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func createAttachPolicyToRole() error {
+	listRequest := ram.CreateListPoliciesForRoleRequest()
+	listRequest.RoleName = rolename
+	listRequest.Scheme = "HTTPS"
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	if err != nil {
+		return err
+	}
+	listResponse, err := client.ListPoliciesForRole(listRequest)
+	if err != nil {
+		return err
+	}
+	for _, policy := range listResponse.Policies.Policy {
+		if policy.PolicyName == "AdministratorAccess" {
+			return nil
+		}
+	}
+	createRequest := ram.CreateAttachPolicyToRoleRequest()
+	createRequest.Scheme = "HTTPS"
+	createRequest.PolicyName = "AdministratorAccess"
+	createRequest.RoleName = rolename
+	createRequest.PolicyType = "System"
+	_, err = client.AttachPolicyToRole(createRequest)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func createAccessKey() (string, string, error) {
+	client, err := ram.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	if err != nil {
+		return "", "", err
+	}
+	listrequest := ram.CreateListAccessKeysRequest()
+	listrequest.UserName = username
+	listrequest.Scheme = "HTTPS"
+	listresponse, err := client.ListAccessKeys(listrequest)
+	if err != nil {
+		return "", "", err
+	}
+	if listresponse.AccessKeys.AccessKey != nil {
+		if len(listresponse.AccessKeys.AccessKey) >= 2 {
+			accesskey := listresponse.AccessKeys.AccessKey[0]
+			deleterequest := ram.CreateDeleteAccessKeyRequest()
+			deleterequest.UserAccessKeyId = accesskey.AccessKeyId
+			deleterequest.UserName = username
+			deleterequest.Scheme = "HTTPS"
+			_, err := client.DeleteAccessKey(deleterequest)
+			if err != nil {
+				return "", "", err
+			}
+		}
+	}
+	request := ram.CreateCreateAccessKeyRequest()
+	request.Scheme = "HTTPS"
+	request.UserName = username
+	response, err := client.CreateAccessKey(request)
+	if err != nil {
+		return "", "", err
+	}
+
+	return response.AccessKey.AccessKeyId, response.AccessKey.AccessKeySecret, nil
+}
+
+func createAssumeRole() (*sts.AssumeRoleResponse, error) {
+	err := createUser()
+	if err != nil {
+		return nil, err
+	}
+	_, _, err = createRole(os.Getenv("USER_ID"))
+	if err != nil {
+		return nil, err
+	}
+	err = createAttachPolicyToUser()
+	if err != nil {
+		return nil, err
+	}
+	subaccesskeyid, subaccesskeysecret, err := createAccessKey()
+	if err != nil {
+		return nil, err
+	}
+	request := sts.CreateAssumeRoleRequest()
+	request.RoleArn = rolearn
+	request.RoleSessionName = "alice_test"
+	request.Scheme = "HTTPS"
+	client, err := sts.NewClientWithAccessKey(os.Getenv("REGION_ID"), subaccesskeyid, subaccesskeysecret)
+	response, err := client.AssumeRole(request)
+	if err != nil {
+		return nil, err
+	}
+	return response, nil
+}

+ 347 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/core_test.go

@@ -0,0 +1,347 @@
+package integration
+
+import (
+	"fmt"
+	"github.com/goji/httpauth"
+	"net/http"
+	"net/http/httptest"
+	"net/http/httputil"
+	"net/url"
+	"os"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_DescribeRegionsWithCommonRequestWithRPC(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_DescribeRegionsWithCommonRequestWithSTStoken(t *testing.T) {
+	assumeresponse, err := createAssumeRole()
+	assert.Nil(t, err)
+	credential := assumeresponse.Credentials
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithStsToken(os.Getenv("REGION_ID"), credential.AccessKeyId, credential.AccessKeySecret, credential.SecurityToken)
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_DescribeRegionsWithCommonRequestWithHTTPS(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	request.SetScheme("HTTPS")
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_DescribeRegionsWithCommonRequestWithUnicodeSpecificParams(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	request.SetContent([]byte("sdk&-杭&&&州-test"))
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_DescribeRegionsWithCommonRequestWithError(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "Describe"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	_, err = client.ProcessCommonRequest(request)
+	realerr := err.(errors.Error)
+	assert.Equal(t, "InvalidParameter", realerr.ErrorCode())
+	assert.Equal(t, "The specified parameter \"Action or Version\" is not valid.", realerr.Message())
+}
+
+func Test_DescribeRegionsWithCommonRequestWithIncompleteSignature(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.AcceptFormat = "json"
+	request.SetScheme("https")
+	request.Method = "POST"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), strings.ToUpper(os.Getenv("ACCESS_KEY_SECRET")))
+	assert.Nil(t, err)
+	_, err = client.ProcessCommonRequest(request)
+	realerr := err.(*errors.ServerError)
+	assert.Equal(t, "IncompleteSignature", realerr.ErrorCode())
+	assert.Equal(t, "InvalidAccessKeySecret: Please check you AccessKeySecret", realerr.Recommend())
+}
+
+func Test_DescribeClustersWithCommonRequestWithROA(t *testing.T) {
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "GET"
+	request.Domain = "cs.aliyuncs.com"
+	request.Version = "2015-12-15"
+	request.PathPattern = "/clusters"
+	request.ApiName = "DescribeClusters"
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.TransToAcsRequest()
+	resp, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.Equal(t, 200, resp.GetHttpStatus())
+
+}
+
+func Test_DescribeClustersWithCommonRequestWithSignatureDostNotMatch(t *testing.T) {
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), strings.ToUpper(os.Getenv("ACCESS_KEY_SECRET")))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "GET"
+	request.Domain = "cs.aliyuncs.com"
+	request.Version = "2015-12-15"
+	request.PathPattern = "/clusters/[ClusterId]"
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.TransToAcsRequest()
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	real, _ := err.(*errors.ServerError)
+	assert.Contains(t, real.Recommend(), "InvalidAccessKeySecret: Please check you AccessKeySecret")
+	assert.Equal(t, real.ErrorCode(), "SignatureDoesNotMatch")
+}
+
+func Test_DescribeClustersWithCommonRequestWithROAWithSTStoken(t *testing.T) {
+	assumeresponse, err := createAssumeRole()
+	assert.Nil(t, err)
+	credential := assumeresponse.Credentials
+	client, err := sdk.NewClientWithStsToken(os.Getenv("REGION_ID"), credential.AccessKeyId, credential.AccessKeySecret, credential.SecurityToken)
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "GET"
+	request.Domain = "cs.aliyuncs.com"
+	request.Version = "2015-12-15"
+	request.PathPattern = "/clusters/[ClusterId]"
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.TransToAcsRequest()
+	f1, err := os.Create("test.txt")
+	defer os.Remove("test.txt")
+	assert.Nil(t, err)
+	templete := `{version}, {host}`
+	client.SetLogger("error", "Alibaba", f1, templete)
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, client.GetLoggerMsg(), `1.1, cs.aliyuncs.com`)
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_DescribeClusterDetailWithCommonRequestWithROAWithHTTPS(t *testing.T) {
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "GET"
+	request.Domain = "cs.aliyuncs.com"
+	request.Version = "2015-12-15"
+	request.SetScheme("HTTPS")
+	request.PathPattern = "/clusters/[ClusterId]"
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.TransToAcsRequest()
+
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_DescribeClusterDetailWithCommonRequestWithTimeout(t *testing.T) {
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Domain = "cs.aliyuncs.com"
+	request.Version = "2015-12-15"
+	request.SetScheme("HTTPS")
+	request.PathPattern = "/clusters/[ClusterId]"
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.ReadTimeout = 1 * time.Millisecond
+	request.ConnectTimeout = 1 * time.Nanosecond
+	request.TransToAcsRequest()
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+
+	request.ConnectTimeout = 1 * time.Second
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "Read timeout. Please set a valid ReadTimeout.")
+}
+
+func Test_CreateInstanceWithCommonRequestWithPolicy(t *testing.T) {
+	err := createAttachPolicyToRole()
+	assert.Nil(t, err)
+
+	subaccesskeyid, subaccesskeysecret, err := createAccessKey()
+	assert.Nil(t, err)
+	client, err := sdk.NewClientWithRamRoleArnAndPolicy(os.Getenv("REGION_ID"), subaccesskeyid, subaccesskeysecret, rolearn, "alice_test", "")
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "POST"
+	request.Product = "Ecs"
+	request.Domain = "ecs.aliyuncs.com"
+	request.Version = "2014-05-26"
+	request.SetScheme("HTTPS")
+	request.ApiName = "CreateInstance"
+	request.QueryParams["ImageId"] = "win2008r2_64_ent_sp1_en-us_40G_alibase_20170915.vhd"
+	request.QueryParams["InstanceType"] = "ecs.g5.large"
+	request.TransToAcsRequest()
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "user order resource type [classic] not exists in [random]")
+
+	policy := `{
+    "Version": "1",
+    "Statement": [
+        {
+            "Action": "rds:*",
+            "Resource": "*",
+            "Effect": "Allow"
+        },
+        {
+            "Action": "dms:LoginDatabase",
+            "Resource": "acs:rds:*:*:*",
+            "Effect": "Allow"
+        }
+    ]
+}`
+	client, err = sdk.NewClientWithRamRoleArnAndPolicy(os.Getenv("REGION_ID"), subaccesskeyid, subaccesskeysecret, rolearn, "alice_test", policy)
+	assert.Nil(t, err)
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "User not authorized to operate on the specified resource, or this API doesn't support RAM.")
+}
+
+func handlerTrue(w http.ResponseWriter, r *http.Request) {
+	w.WriteHeader(200)
+	w.Write([]byte("test"))
+	return
+}
+
+func handlerFake(w http.ResponseWriter, r *http.Request) {
+	trueserver := handlerTrueServer()
+	url, err := url.Parse(trueserver.URL)
+	if err != nil {
+		return
+	}
+	proxy := httputil.NewSingleHostReverseProxy(url)
+	w.Write([]byte("sdk"))
+	proxy.ServeHTTP(w, r)
+
+	return
+}
+
+func handlerFakeServer() (server *httptest.Server) {
+	handleFunc := httpauth.SimpleBasicAuth("someuser", "somepassword")(http.HandlerFunc(handlerFake))
+	server = httptest.NewServer(handleFunc)
+
+	return server
+}
+
+func handlerTrueServer() (server *httptest.Server) {
+	server = httptest.NewServer(http.HandlerFunc(handlerTrue))
+
+	return server
+}
+
+func Test_HTTPProxy(t *testing.T) {
+
+	ts := handlerFakeServer()
+	ts1 := handlerTrueServer()
+	defer func() {
+		ts.Close()
+		ts1.Close()
+	}()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	domain := strings.Replace(ts1.URL, "http://", "", 1)
+	request.Domain = domain
+	request.Version = "2015-12-15"
+	request.TransToAcsRequest()
+	resp, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.Equal(t, 200, resp.GetHttpStatus())
+	assert.Equal(t, "test", resp.GetHttpContentString())
+
+	originEnv := os.Getenv("HTTP_PROXY")
+	domain = strings.Replace(ts.URL, "http://", "", 1)
+	os.Setenv("HTTP_PROXY", fmt.Sprintf("http://someuser:somepassword@%s", domain))
+	resp, err = client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.Equal(t, 200, resp.GetHttpStatus())
+	assert.Equal(t, "sdktest", resp.GetHttpContentString())
+
+	os.Setenv("HTTP_PROXY", originEnv)
+}
+
+func Test_DdoscooWithServiceCode(t *testing.T) {
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Version = "2017-12-28"
+	request.Product = "ddoscoo"
+	request.ServiceCode = "ddoscoo"
+	request.ApiName = "DescribeInstanceSpecs"
+	request.RegionId = "cn-hangzhou"
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "InstanceIds is mandatory for this action.")
+}
+
+func Test_RoaRequestWithEcsRole(t *testing.T) {
+	client, err := sdk.NewClientWithEcsRamRole("cn-shenzhen", "test-go-role")
+	assert.Nil(t, err)
+	request := requests.NewCommonRequest()
+	request.Method = "POST"
+	request.Scheme = "https" // https | http
+	request.Domain = "edas.cn-hangzhou.aliyuncs.com"
+	request.Version = "2017-08-01"
+	request.PathPattern = "/pop/v5/resource/region_list"
+
+	request.QueryParams["RegionId"] = "cn-shenzhen"
+	_, err = client.ProcessCommonRequest(request)
+	assert.NotNil(t, err)
+	assert.Contains(t, err.Error(), "refresh Ecs sts token err")
+}

+ 100 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/cr_test.go

@@ -0,0 +1,100 @@
+package integration
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/cr"
+
+	"github.com/stretchr/testify/assert"
+
+	"fmt"
+	"os"
+	"testing"
+)
+
+var crTestKey = "crtestkey" + travisValue[len(travisValue)-1]
+
+func Test_CR_CreateNamespace(t *testing.T) {
+	client, err := cr.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+
+	request := cr.CreateCreateNamespaceRequest()
+	domain := fmt.Sprintf("cr." + os.Getenv("REGION_ID") + ".aliyuncs.com")
+	request.SetDomain(domain)
+	request.SetContentType("JSON")
+	content := fmt.Sprintf(
+		`{
+			"Namespace":{
+				"Namespace":"%s"
+			}
+		}`, crTestKey,
+	)
+	request.SetContent([]byte(content))
+
+	response, err := client.CreateNamespace(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_CR_UpdateNamespace(t *testing.T) {
+	client, err := cr.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+
+	request := cr.CreateUpdateNamespaceRequest()
+	domain := fmt.Sprintf("cr." + os.Getenv("REGION_ID") + ".aliyuncs.com")
+	request.SetDomain(domain)
+	request.Namespace = crTestKey
+	request.SetContentType("JSON")
+	content := fmt.Sprintf(
+		`{
+			"Namespace":{
+				"AutoCreate":%v,
+				"DefaultVisibility":"%s"
+			}
+		}`, false, "PUBLIC",
+	)
+	request.SetContent([]byte(content))
+
+	response, err := client.UpdateNamespace(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_CR_GetNamespace(t *testing.T) {
+	client, err := cr.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+
+	request := cr.CreateGetNamespaceRequest()
+	domain := fmt.Sprintf("cr." + os.Getenv("REGION_ID") + ".aliyuncs.com")
+	request.SetDomain(domain)
+	request.Namespace = crTestKey
+
+	response, err := client.GetNamespace(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_CR_GetNamespaceList(t *testing.T) {
+	client, err := cr.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+
+	request := cr.CreateGetNamespaceListRequest()
+	domain := fmt.Sprintf("cr." + os.Getenv("REGION_ID") + ".aliyuncs.com")
+	request.SetDomain(domain)
+
+	response, err := client.GetNamespaceList(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func Test_CR_DeleteNamespace(t *testing.T) {
+	client, err := cr.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+
+	request := cr.CreateDeleteNamespaceRequest()
+	domain := fmt.Sprintf("cr." + os.Getenv("REGION_ID") + ".aliyuncs.com")
+	request.SetDomain(domain)
+	request.Namespace = crTestKey
+
+	response, err := client.DeleteNamespace(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}

+ 156 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/credential_test.go

@@ -0,0 +1,156 @@
+package integration
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"strings"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/airec"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
+	"github.com/stretchr/testify/assert"
+
+	"os"
+	"testing"
+)
+
+func Test_DescribeRegionsWithRPCrequestWithAK(t *testing.T) {
+	client, err := ecs.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	assert.NotNil(t, client)
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "https"
+	response, err := client.DescribeRegions(request)
+	assert.Nil(t, err)
+	assert.NotNil(t, response)
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, len(response.Regions.Region) > 0)
+}
+
+func Test_DescribeRegionsWithRPCrequestWithSTStoken(t *testing.T) {
+	assumeresponse, err := createAssumeRole()
+	assert.Nil(t, err)
+	credential := assumeresponse.Credentials
+	client, err := ecs.NewClientWithStsToken(os.Getenv("REGION_ID"), credential.AccessKeyId, credential.AccessKeySecret, credential.SecurityToken)
+	assert.Nil(t, err)
+	assert.NotNil(t, client)
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "https"
+	response, err := client.DescribeRegions(request)
+	assert.Nil(t, err)
+	assert.NotNil(t, response)
+	assert.Equal(t, 36, len(response.RequestId))
+	assert.True(t, len(response.Regions.Region) > 0)
+}
+
+func Test_CreateDiversifyWithROArequestWithAK(t *testing.T) {
+	client, err := airec.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	request := airec.CreateCreateDiversifyRequest()
+	request.SetDomain("airec.cn-hangzhou.aliyuncs.com")
+	request.QueryParams["RegionId"] = os.Getenv("REGION_ID")
+	request.Method = "GET"
+	response, err := client.CreateDiversify(request)
+	assert.NotNil(t, err)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "Request url is invalid")
+}
+
+func Test_DescribeRegionsWithRPCrequestWithArn(t *testing.T) {
+	subaccesskeyid, subaccesskeysecret, err := createAccessKey()
+	assert.Nil(t, err)
+	client, err := ecs.NewClientWithRamRoleArn(os.Getenv("REGION_ID"), subaccesskeyid, subaccesskeysecret, rolearn, "alice_test")
+	assert.Nil(t, err)
+
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "https"
+	request.Domain = "ecs.aliyuncs.com"
+	response, err := client.DescribeRegions(request)
+	assert.Nil(t, err)
+	assert.Equal(t, 36, len(response.RequestId))
+}
+
+func TestDescribeRegionsWithProviderAndAk(t *testing.T) {
+	os.Setenv(provider.ENVAccessKeyID, os.Getenv("ACCESS_KEY_ID"))
+	os.Setenv(provider.ENVAccessKeySecret, os.Getenv("ACCESS_KEY_SECRET"))
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithProvider(os.Getenv("REGION_ID"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func TestDescribeRegionsWithProviderAndRsaKeyPair(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("ecs.ap-northeast-1.aliyuncs.com")
+	request.TransToAcsRequest()
+
+	key := os.Getenv("RSA_FILE_AES_KEY")
+
+	srcfile, err := os.Open("./encyptfile")
+	assert.Nil(t, err)
+	defer srcfile.Close()
+
+	buf := new(bytes.Buffer)
+	read := bufio.NewReader(srcfile)
+	read.WriteTo(buf)
+
+	block, err := aes.NewCipher([]byte(key))
+	assert.Nil(t, err)
+
+	origData := buf.Bytes()
+	blockdec := cipher.NewCBCDecrypter(block, []byte(key)[:block.BlockSize()])
+	orig := make([]byte, len(origData))
+	blockdec.CryptBlocks(orig, origData)
+	orig = PKCS7UnPadding(orig)
+
+	cyphbuf := bytes.NewBuffer(orig)
+	scan := bufio.NewScanner(cyphbuf)
+	var data string
+	for scan.Scan() {
+		if strings.HasPrefix(scan.Text(), "----") {
+			continue
+		}
+		data += scan.Text() + "\n"
+	}
+
+	client, err := sdk.NewClientWithRsaKeyPair("ap-northeast-1", os.Getenv("PUBLIC_KEY_ID"), data, 3600)
+	assert.Nil(t, err)
+
+	response, err := client.ProcessCommonRequest(request)
+	assert.Nil(t, err)
+	assert.True(t, response.IsSuccess())
+}
+
+func PKCS7UnPadding(origData []byte) []byte {
+	length := len(origData)
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}
+
+func TestDescribeRegionsWithBearToken(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2017-07-05"
+	request.Product = "CCC"
+	request.ApiName = "ListRoles "
+	request.SetDomain("ccc.cn-shanghai.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithBearerToken("cn-shanghai", "eyJhbGciOiJSUzI1NiIsImsyaWQiOiJlNE92NnVOUDhsMEY2RmVUMVhvek5wb1NBcVZLblNGRyIsImtpZCI6IkpDOXd4enJocUowZ3RhQ0V0MlFMVWZldkVVSXdsdEZodWk0TzFiaDY3dFUifQ.TjU2UldwZzFzRE1oVEN5UStjYlZLV1dzNW45cFBOSWdNRDhzQmVXYmVpLytWY012MEJqYjdTdnB3SE9LcHBiZkorUGdvclAxRy9GTjdHeldmaWZFVndoa05ueUNTem80dU0rUVFKdDFSY2V0bmFQcml5WFljTDhmNUZ2c1pFd3BhTDFOajVvRW9QVG83S1NVU3JpTFdKQmNnVHB1U094cUd4cGpCeFdXS0pDVnN0L3lzRkp4RTVlSFNzUm1Qa1FBVTVwS1lmaXE0QVFSd3lPQjdYSk1uUGFKU1BiSWhyWVFVS21WOVd5K2d3PT0.jxdCiNimyes3swDRBSxdsgaL4IlOD2Kz49Gf5w0VZ0Xap9ozUyxvSSywGzMrKvCTIoeh9QMCMjCpnt9A-nQxENj3YGAeBk8Wy19uHiT-4OVo-CiCKmKxILpzxcpOptNO-LER1swVLbt0NiTuTH4KB5CUaRwJKIFJuUwa57HcsWbvWQyZa1ms0NNOccNfGJl4177eY2LTUyyXWi4wYNA_L0YMTkZz4sOFM_Mdzks8bHXiSbGkkjfWQy0QblkLz6Bboh1OYlg3_RCLSWby_FMNoxU_eG2lGAsDnYxZDmCAq2jedY0x1RzZodo9HYRQN7DujlBhfzqm4hOBNvA3LiJfzw")
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.True(t, strings.Contains(err.Error(), "Bearertoken has expired"))
+	assert.False(t, response.IsSuccess())
+}

二进制
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/encyptfile


+ 93 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/integration/error_test.go

@@ -0,0 +1,93 @@
+package integration
+
+import (
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
+	"github.com/stretchr/testify/assert"
+
+	"os"
+	"strings"
+	"testing"
+)
+
+func Test_DescribeRegionsWithParameterError(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "Describe"
+	request.SetDomain("ecs.aliyuncs.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	_, err = client.ProcessCommonRequest(request)
+	realerr := err.(errors.Error)
+	assert.Equal(t, "InvalidParameter", realerr.ErrorCode())
+	assert.Equal(t, "The specified parameter \"Action or Version\" is not valid.", realerr.Message())
+}
+
+func Test_DescribeRegionsWithUnreachableError(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	request.SetDomain("www.aliyun-hangzhou.com")
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Equal(t, 0, response.GetHttpStatus())
+	realerr := err.(errors.Error)
+	assert.True(t, strings.Contains(realerr.OriginError().Error(), "www.aliyun-hangzhou.com"))
+}
+
+func Test_DescribeRegionsWithTimeout(t *testing.T) {
+	credentail := credentials.NewAccessKeyCredential(os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	config := &sdk.Config{
+		Timeout: 100,
+	}
+	request := ecs.CreateDescribeRegionsRequest()
+	request.Scheme = "https"
+	request.SetDomain("ecs.aliyuncs.com")
+	client, err := ecs.NewClientWithOptions(os.Getenv("REGION_ID"), config, credentail)
+	response, err := client.DescribeRegions(request)
+	assert.Equal(t, 0, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "https://ecs.aliyuncs.com")
+	assert.Contains(t, err.Error(), "Client.Timeout exceeded while awaiting headers")
+}
+
+func Test_DescribeRegionsWithNilbody(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	ts := mockServer(400, ``)
+	defer ts.Close()
+	domain := strings.Replace(ts.URL, "http://", "", 1)
+	request.Domain = domain
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.NotNil(t, err)
+}
+
+func Test_DescribeRegionsWithFormatError(t *testing.T) {
+	request := requests.NewCommonRequest()
+	request.Version = "2014-05-26"
+	request.Product = "Ecs"
+	request.ApiName = "DescribeRegions"
+	ts := mockServer(400, `bad json`)
+	defer ts.Close()
+	domain := strings.Replace(ts.URL, "http://", "", 1)
+	request.Domain = domain
+	request.TransToAcsRequest()
+	client, err := sdk.NewClientWithAccessKey(os.Getenv("REGION_ID"), os.Getenv("ACCESS_KEY_ID"), os.Getenv("ACCESS_KEY_SECRET"))
+	assert.Nil(t, err)
+	response, err := client.ProcessCommonRequest(request)
+	assert.Equal(t, 400, response.GetHttpStatus())
+	assert.Contains(t, err.Error(), "bad json")
+}

+ 249 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/api_timeout.go

@@ -0,0 +1,249 @@
+package sdk
+
+import (
+	"encoding/json"
+	"strings"
+	"time"
+)
+
+var apiTimeouts = `{
+  "ecs": {
+      "ActivateRouterInterface": 10,
+      "AddTags": 61,
+      "AllocateDedicatedHosts": 10,
+      "AllocateEipAddress": 17,
+      "AllocatePublicIpAddress": 36,
+      "ApplyAutoSnapshotPolicy": 10,
+      "AssignIpv6Addresses": 10,
+      "AssignPrivateIpAddresses": 10,
+      "AssociateEipAddress": 17,
+      "AttachClassicLinkVpc": 14,
+      "AttachDisk": 36,
+      "AttachInstanceRamRole": 11,
+      "AttachKeyPair": 16,
+      "AttachNetworkInterface": 16,
+      "AuthorizeSecurityGroupEgress": 16,
+      "AuthorizeSecurityGroup": 16,
+      "CancelAutoSnapshotPolicy": 10,
+      "CancelCopyImage": 10,
+      "CancelPhysicalConnection": 10,
+      "CancelSimulatedSystemEvents": 10,
+      "CancelTask": 10,
+      "ConnectRouterInterface": 10,
+      "ConvertNatPublicIpToEip": 12,
+      "CopyImage": 10,
+      "CreateAutoSnapshotPolicy": 10,
+      "CreateCommand": 16,
+      "CreateDeploymentSet": 16,
+      "CreateDisk": 36,
+      "CreateHpcCluster": 10,
+      "CreateImage": 36,
+      "CreateInstance": 86,
+      "CreateKeyPair": 10,
+      "CreateLaunchTemplate": 10,
+      "CreateLaunchTemplateVersion": 10,
+      "CreateNatGateway": 36,
+      "CreateNetworkInterfacePermission": 13,
+      "CreateNetworkInterface": 16,
+      "CreatePhysicalConnection": 10,
+      "CreateRouteEntry": 17,
+      "CreateRouterInterface": 10,
+      "CreateSecurityGroup": 86,
+      "CreateSimulatedSystemEvents": 10,
+      "CreateSnapshot": 86,
+      "CreateVirtualBorderRouter": 10,
+      "CreateVpc": 16,
+      "CreateVSwitch": 17,
+      "DeactivateRouterInterface": 10,
+      "DeleteAutoSnapshotPolicy": 10,
+      "DeleteBandwidthPackage": 10,
+      "DeleteCommand": 16,
+      "DeleteDeploymentSet": 12,
+      "DeleteDisk": 16,
+      "DeleteHpcCluster": 10,
+      "DeleteImage": 36,
+      "DeleteInstance": 66,
+      "DeleteKeyPairs": 10,
+      "DeleteLaunchTemplate": 10,
+      "DeleteLaunchTemplateVersion": 10,
+      "DeleteNatGateway": 10,
+      "DeleteNetworkInterfacePermission": 10,
+      "DeleteNetworkInterface": 16,
+      "DeletePhysicalConnection": 10,
+      "DeleteRouteEntry": 16,
+      "DeleteRouterInterface": 10,
+      "DeleteSecurityGroup": 87,
+      "DeleteSnapshot": 17,
+      "DeleteVirtualBorderRouter": 10,
+      "DeleteVpc": 17,
+      "DeleteVSwitch": 17,
+      "DescribeAccessPoints": 10,
+      "DescribeAccountAttributes": 10,
+      "DescribeAutoSnapshotPolicyEx": 16,
+      "DescribeAvailableResource": 10,
+      "DescribeBandwidthLimitation": 16,
+      "DescribeBandwidthPackages": 10,
+      "DescribeClassicLinkInstances": 15,
+      "DescribeCloudAssistantStatus": 16,
+      "DescribeClusters": 10,
+      "DescribeCommands": 16,
+      "DescribeDedicatedHosts": 10,
+      "DescribeDedicatedHostTypes": 10,
+      "DescribeDeploymentSets": 26,
+      "DescribeDiskMonitorData": 16,
+      "DescribeDisksFullStatus": 14,
+      "DescribeDisks": 19,
+      "DescribeEipAddresses": 16,
+      "DescribeEipMonitorData": 16,
+      "DescribeEniMonitorData": 10,
+      "DescribeHaVips": 10,
+      "DescribeHpcClusters": 16,
+      "DescribeImageSharePermission": 10,
+      "DescribeImages": 38,
+      "DescribeImageSupportInstanceTypes": 16,
+      "DescribeInstanceAttribute": 36,
+      "DescribeInstanceAutoRenewAttribute": 17,
+      "DescribeInstanceHistoryEvents": 19,
+      "DescribeInstanceMonitorData": 19,
+      "DescribeInstancePhysicalAttribute": 10,
+      "DescribeInstanceRamRole": 11,
+      "DescribeInstancesFullStatus": 14,
+      "DescribeInstances": 10,
+      "DescribeInstanceStatus": 26,
+      "DescribeInstanceTopology": 12,
+      "DescribeInstanceTypeFamilies": 17,
+      "DescribeInstanceTypes": 17,
+      "DescribeInstanceVncPasswd": 10,
+      "DescribeInstanceVncUrl": 36,
+      "DescribeInvocationResults": 16,
+      "DescribeInvocations": 16,
+      "DescribeKeyPairs": 12,
+      "DescribeLaunchTemplates": 16,
+      "DescribeLaunchTemplateVersions": 16,
+      "DescribeLimitation": 36,
+      "DescribeNatGateways": 10,
+      "DescribeNetworkInterfacePermissions": 13,
+      "DescribeNetworkInterfaces": 16,
+      "DescribeNewProjectEipMonitorData": 16,
+      "DescribePhysicalConnections": 10,
+      "DescribePrice": 16,
+      "DescribeRecommendInstanceType": 10,
+      "DescribeRegions": 19,
+      "DescribeRenewalPrice": 16,
+      "DescribeResourceByTags": 10,
+      "DescribeResourcesModification": 17,
+      "DescribeRouterInterfaces": 10,
+      "DescribeRouteTables": 17,
+      "DescribeSecurityGroupAttribute": 133,
+      "DescribeSecurityGroupReferences": 16,
+      "DescribeSecurityGroups": 25,
+      "DescribeSnapshotLinks": 17,
+      "DescribeSnapshotMonitorData": 12,
+      "DescribeSnapshotPackage": 10,
+      "DescribeSnapshots": 26,
+      "DescribeSnapshotsUsage": 26,
+      "DescribeSpotPriceHistory": 22,
+      "DescribeTags": 17,
+      "DescribeTaskAttribute": 10,
+      "DescribeTasks": 11,
+      "DescribeUserBusinessBehavior": 13,
+      "DescribeUserData": 10,
+      "DescribeVirtualBorderRoutersForPhysicalConnection": 10,
+      "DescribeVirtualBorderRouters": 10,
+      "DescribeVpcs": 41,
+      "DescribeVRouters": 17,
+      "DescribeVSwitches": 17,
+      "DescribeZones": 103,
+      "DetachClassicLinkVpc": 14,
+      "DetachDisk": 17,
+      "DetachInstanceRamRole": 10,
+      "DetachKeyPair": 10,
+      "DetachNetworkInterface": 16,
+      "EipFillParams": 19,
+      "EipFillProduct": 13,
+      "EipNotifyPaid": 10,
+      "EnablePhysicalConnection": 10,
+      "ExportImage": 10,
+      "GetInstanceConsoleOutput": 14,
+      "GetInstanceScreenshot": 14,
+      "ImportImage": 29,
+      "ImportKeyPair": 10,
+      "InstallCloudAssistant": 10,
+      "InvokeCommand": 16,
+      "JoinResourceGroup": 10,
+      "JoinSecurityGroup": 66,
+      "LeaveSecurityGroup": 66,
+      "ModifyAutoSnapshotPolicyEx": 10,
+      "ModifyBandwidthPackageSpec": 11,
+      "ModifyCommand": 10,
+      "ModifyDeploymentSetAttribute": 10,
+      "ModifyDiskAttribute": 16,
+      "ModifyDiskChargeType": 13,
+      "ModifyEipAddressAttribute": 14,
+      "ModifyImageAttribute": 10,
+      "ModifyImageSharePermission": 16,
+      "ModifyInstanceAttribute": 22,
+      "ModifyInstanceAutoReleaseTime": 15,
+      "ModifyInstanceAutoRenewAttribute": 16,
+      "ModifyInstanceChargeType": 22,
+      "ModifyInstanceDeployment": 10,
+      "ModifyInstanceNetworkSpec": 36,
+      "ModifyInstanceSpec": 62,
+      "ModifyInstanceVncPasswd": 35,
+      "ModifyInstanceVpcAttribute": 15,
+      "ModifyLaunchTemplateDefaultVersion": 10,
+      "ModifyNetworkInterfaceAttribute": 10,
+      "ModifyPhysicalConnectionAttribute": 10,
+      "ModifyPrepayInstanceSpec": 13,
+      "ModifyRouterInterfaceAttribute": 10,
+      "ModifySecurityGroupAttribute": 10,
+      "ModifySecurityGroupEgressRule": 10,
+      "ModifySecurityGroupPolicy": 10,
+      "ModifySecurityGroupRule": 16,
+      "ModifySnapshotAttribute": 10,
+      "ModifyUserBusinessBehavior": 10,
+      "ModifyVirtualBorderRouterAttribute": 10,
+      "ModifyVpcAttribute": 10,
+      "ModifyVRouterAttribute": 10,
+      "ModifyVSwitchAttribute": 10,
+      "ReActivateInstances": 10,
+      "RebootInstance": 27,
+      "RedeployInstance": 14,
+      "ReInitDisk": 16,
+      "ReleaseDedicatedHost": 10,
+      "ReleaseEipAddress": 16,
+      "ReleasePublicIpAddress": 10,
+      "RemoveTags": 10,
+      "RenewInstance": 19,
+      "ReplaceSystemDisk": 36,
+      "ResetDisk": 36,
+      "ResizeDisk": 11,
+      "RevokeSecurityGroupEgress": 13,
+      "RevokeSecurityGroup": 16,
+      "RunInstances": 86,
+      "StartInstance": 46,
+      "StopInstance": 27,
+      "StopInvocation": 10,
+      "TerminatePhysicalConnection": 10,
+      "TerminateVirtualBorderRouter": 10,
+      "UnassignIpv6Addresses": 10,
+      "UnassignPrivateIpAddresses": 10,
+      "UnassociateEipAddress": 16 
+  }
+}
+`
+
+func getAPIMaxTimeout(product, actionName string) (time.Duration, bool) {
+	timeout := make(map[string]map[string]int)
+	err := json.Unmarshal([]byte(apiTimeouts), &timeout)
+	if err != nil {
+		return 0 * time.Millisecond, false
+	}
+
+	obj := timeout[strings.ToLower(product)]
+	if obj != nil && obj[actionName] != 0 {
+		return time.Duration(obj[actionName]) * time.Second, true
+	}
+
+	return 0 * time.Millisecond, false
+}

+ 22 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/api_timeout_test.go

@@ -0,0 +1,22 @@
+package sdk
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_getAPIMaxTimeout(t *testing.T) {
+	timeout, ok := getAPIMaxTimeout("ecs", "UnassociateEipAddress")
+	assert.True(t, ok)
+	assert.Equal(t, 16*time.Second, timeout)
+
+	timeout, ok = getAPIMaxTimeout("Ecs", "UnassociateEipAddress")
+	assert.True(t, ok)
+	assert.Equal(t, 16*time.Second, timeout)
+
+	timeout, ok = getAPIMaxTimeout("Acs", "UnassociateEipAddress")
+	assert.False(t, ok)
+	assert.Equal(t, 0*time.Second, timeout)
+}

+ 18 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential.go

@@ -0,0 +1,18 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package auth
+
+type Credential interface {
+}

+ 1 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credential_test.go

@@ -0,0 +1 @@
+package auth

+ 34 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential.go

@@ -0,0 +1,34 @@
+package credentials
+
+// Deprecated: Use AccessKeyCredential in this package instead.
+type BaseCredential struct {
+	AccessKeyId     string
+	AccessKeySecret string
+}
+
+type AccessKeyCredential struct {
+	AccessKeyId     string
+	AccessKeySecret string
+}
+
+// Deprecated: Use NewAccessKeyCredential in this package instead.
+func NewBaseCredential(accessKeyId, accessKeySecret string) *BaseCredential {
+	return &BaseCredential{
+		AccessKeyId:     accessKeyId,
+		AccessKeySecret: accessKeySecret,
+	}
+}
+
+func (baseCred *BaseCredential) ToAccessKeyCredential() *AccessKeyCredential {
+	return &AccessKeyCredential{
+		AccessKeyId:     baseCred.AccessKeyId,
+		AccessKeySecret: baseCred.AccessKeySecret,
+	}
+}
+
+func NewAccessKeyCredential(accessKeyId, accessKeySecret string) *AccessKeyCredential {
+	return &AccessKeyCredential{
+		AccessKeyId:     accessKeyId,
+		AccessKeySecret: accessKeySecret,
+	}
+}

+ 19 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/access_key_credential_test.go

@@ -0,0 +1,19 @@
+package credentials
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAccessKeyCredential(t *testing.T) {
+	c := NewAccessKeyCredential("accesskeyid", "accesskeysecret")
+	assert.Equal(t, "accesskeyid", c.AccessKeyId)
+	assert.Equal(t, "accesskeysecret", c.AccessKeySecret)
+	b:= NewBaseCredential("accesskeyid", "accesskeysecret")
+	assert.Equal(t, "accesskeyid", b.AccessKeyId)
+	assert.Equal(t, "accesskeysecret", b.AccessKeySecret)
+	a := b.ToAccessKeyCredential()
+	assert.Equal(t, "accesskeyid", a.AccessKeyId)
+	assert.Equal(t, "accesskeysecret", a.AccessKeySecret)
+}

+ 12 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/bearer_token_credential.go

@@ -0,0 +1,12 @@
+package credentials
+
+type BearerTokenCredential struct {
+	BearerToken string
+}
+
+// NewBearerTokenCredential return a BearerTokenCredential object
+func NewBearerTokenCredential(token string) *BearerTokenCredential {
+	return &BearerTokenCredential{
+		BearerToken: token,
+	}
+}

+ 12 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/bearer_token_credential_test.go

@@ -0,0 +1,12 @@
+package credentials
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewBearerTokenCredential(t *testing.T) {
+	bc := NewBearerTokenCredential("Bearer.Token")
+	assert.Equal(t, &BearerTokenCredential{"Bearer.Token"}, bc)
+}

+ 29 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role.go

@@ -0,0 +1,29 @@
+package credentials
+
+func (oldCred *StsRoleNameOnEcsCredential) ToEcsRamRoleCredential() *EcsRamRoleCredential {
+	return &EcsRamRoleCredential{
+		RoleName: oldCred.RoleName,
+	}
+}
+
+type EcsRamRoleCredential struct {
+	RoleName string
+}
+
+func NewEcsRamRoleCredential(roleName string) *EcsRamRoleCredential {
+	return &EcsRamRoleCredential{
+		RoleName: roleName,
+	}
+}
+
+// Deprecated: Use EcsRamRoleCredential in this package instead.
+type StsRoleNameOnEcsCredential struct {
+	RoleName string
+}
+
+// Deprecated: Use NewEcsRamRoleCredential in this package instead.
+func NewStsRoleNameOnEcsCredential(roleName string) *StsRoleNameOnEcsCredential {
+	return &StsRoleNameOnEcsCredential{
+		RoleName: roleName,
+	}
+}

+ 16 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/ecs_ram_role_test.go

@@ -0,0 +1,16 @@
+package credentials
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestECSRamRole(t *testing.T) {
+	c := NewEcsRamRoleCredential("rolename")
+	assert.Equal(t, "rolename", c.RoleName)
+	s := NewStsRoleNameOnEcsCredential("rolename")
+	assert.Equal(t, "rolename", s.RoleName)
+	r := s.ToEcsRamRoleCredential()
+	assert.Equal(t, "rolename", r.RoleName)
+}

+ 30 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/env.go

@@ -0,0 +1,30 @@
+package provider
+
+import (
+	"errors"
+	"os"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+)
+
+type EnvProvider struct{}
+
+var ProviderEnv = new(EnvProvider)
+
+func NewEnvProvider() Provider {
+	return &EnvProvider{}
+}
+
+func (p *EnvProvider) Resolve() (auth.Credential, error) {
+	accessKeyID, ok1 := os.LookupEnv(ENVAccessKeyID)
+	accessKeySecret, ok2 := os.LookupEnv(ENVAccessKeySecret)
+	if !ok1 || !ok2 {
+		return nil, nil
+	}
+	if accessKeyID == "" || accessKeySecret == "" {
+		return nil, errors.New("Environmental variable (ALIBABACLOUD_ACCESS_KEY_ID or ALIBABACLOUD_ACCESS_KEY_SECRET) is empty")
+	}
+	return credentials.NewAccessKeyCredential(accessKeyID, accessKeySecret), nil
+}

+ 30 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/env_test.go

@@ -0,0 +1,30 @@
+package provider_test
+
+import (
+	"os"
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider"
+)
+
+func TestEnvResolve(t *testing.T) {
+	p := provider.NewEnvProvider()
+	assert.Equal(t, &provider.EnvProvider{}, p)
+	c, err := p.Resolve()
+	assert.Nil(t, c)
+	assert.Nil(t, err)
+	os.Setenv(provider.ENVAccessKeyID, "")
+	os.Setenv(provider.ENVAccessKeySecret, "")
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "Environmental variable (ALIBABACLOUD_ACCESS_KEY_ID or ALIBABACLOUD_ACCESS_KEY_SECRET) is empty")
+	os.Setenv(provider.ENVAccessKeyID, "AccessKeyId")
+	os.Setenv(provider.ENVAccessKeySecret, "AccessKeySecret")
+	c, err = p.Resolve()
+	assert.Nil(t, err)
+	assert.Equal(t, &credentials.AccessKeyCredential{"AccessKeyId", "AccessKeySecret"}, c)
+}

+ 92 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/instance_credentials.go

@@ -0,0 +1,92 @@
+package provider
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"time"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+)
+
+var securityCredURL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"
+
+type InstanceCredentialsProvider struct{}
+
+var ProviderInstance = new(InstanceCredentialsProvider)
+
+var HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+	return fn
+}
+
+func NewInstanceCredentialsProvider() Provider {
+	return &InstanceCredentialsProvider{}
+}
+
+func (p *InstanceCredentialsProvider) Resolve() (auth.Credential, error) {
+	roleName, ok := os.LookupEnv(ENVEcsMetadata)
+	if !ok {
+		return nil, nil
+	}
+	if roleName == "" {
+		return nil, errors.New("Environmental variable 'ALIBABA_CLOUD_ECS_METADATA' are empty")
+	}
+	status, content, err := HookGet(get)(securityCredURL + roleName)
+	if err != nil {
+		return nil, err
+	}
+	if status != 200 {
+		if status == 404 {
+			return nil, fmt.Errorf("The role was not found in the instance")
+		}
+		return nil, fmt.Errorf("Received %d when getting security credentials for %s", status, roleName)
+	}
+	body := make(map[string]interface{})
+
+	if err := json.Unmarshal(content, &body); err != nil {
+		return nil, err
+	}
+
+	accessKeyID, err := extractString(body, "AccessKeyId")
+	if err != nil {
+		return nil, err
+	}
+	accessKeySecret, err := extractString(body, "AccessKeySecret")
+	if err != nil {
+		return nil, err
+	}
+	securityToken, err := extractString(body, "SecurityToken")
+	if err != nil {
+		return nil, err
+	}
+
+	return credentials.NewStsTokenCredential(accessKeyID, accessKeySecret, securityToken), nil
+}
+
+func get(url string) (status int, content []byte, err error) {
+	httpClient := http.DefaultClient
+	httpClient.Timeout = 1 * time.Second
+	resp, err := httpClient.Get(url)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+	content, err = ioutil.ReadAll(resp.Body)
+	return resp.StatusCode, content, err
+}
+
+func extractString(m map[string]interface{}, key string) (string, error) {
+	raw, ok := m[key]
+	if !ok {
+		return "", fmt.Errorf("%s not in map", key)
+	}
+	str, ok := raw.(string)
+	if !ok {
+		return "", fmt.Errorf("%s is not a string in map", key)
+	}
+	return str, nil
+}

+ 136 - 0
src/github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider/instance_credentials_test.go

@@ -0,0 +1,136 @@
+package provider_test
+
+import (
+	"os"
+	"testing"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials/provider"
+)
+
+func TestInstanceCredentialsProvider(t *testing.T) {
+	//testcase 1
+	//return nil, nil
+	p := provider.NewInstanceCredentialsProvider()
+	c, err := p.Resolve()
+	assert.Nil(t, c)
+	assert.Nil(t, err)
+
+	//testcase 2
+	//return nil, errors.New("Environmental variable 'ALIBABA_CLOUD_ECS_METADATA' are empty")
+	os.Setenv(provider.ENVEcsMetadata, "")
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "Environmental variable 'ALIBABA_CLOUD_ECS_METADATA' are empty")
+
+	//testcase 3
+	//return nil, err
+	os.Setenv(provider.ENVEcsMetadata, "test")
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.NotNil(t, err)
+
+	//testcase 4
+	//return nil, fmt.Errorf("The role was not found in the instance")
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 404, []byte(""), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "The role was not found in the instance")
+
+	//testcase 5
+	//return nil, fmt.Errorf("Received %d when getting security credentials for %s", status, roleName)
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 400, []byte(""), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "Received 400 when getting security credentials for test")
+
+	//testcase 6
+	//json unmarshal error
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 200, []byte(`{
+				AccessKSTS.*******
+			  }`), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.NotNil(t, err)
+
+	//testcase 7, AccessKeyId does not receive
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 200, []byte(`{
+				"AccessKeySecret" : "*******",
+				"Expiration" : "2019-01-28T15:15:56Z",
+				"SecurityToken" : "SecurityToken",
+				"LastUpdated" : "2019-01-28T09:15:55Z",
+				"Code" : "Success"
+			  }`), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "AccessKeyId not in map")
+
+	//testcase 8,AccessKeySecret does not receive
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 200, []byte(`{
+				"AccessKeyId" : "STS.*******",
+				"Expiration" : "2019-01-28T15:15:56Z",
+				"SecurityToken" : "SecurityToken",
+				"LastUpdated" : "2019-01-28T09:15:55Z",
+				"Code" : "Success"
+			  }`), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "AccessKeySecret not in map")
+
+	//testcase 9, SecurityToken does not receive
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 200, []byte(`{
+				"AccessKeyId" : "STS.*******",
+				"AccessKeySecret" : "*******",
+				"Expiration" : "2019-01-28T15:15:56Z",
+				"LastUpdated" : "2019-01-28T09:15:55Z",
+				"Code" : "Success"
+			  }`), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, c)
+	assert.EqualError(t, err, "SecurityToken not in map")
+
+	//testcase, normal receive
+	provider.HookGet = func(fn func(string) (int, []byte, error)) func(string) (int, []byte, error) {
+		return func(string) (int, []byte, error) {
+			return 200, []byte(`{
+				"AccessKeyId" : "STS.*******",
+				"AccessKeySecret" : "*******",
+				"Expiration" : "2019-01-28T15:15:56Z",
+				"SecurityToken" : "SecurityToken",
+				"LastUpdated" : "2019-01-28T09:15:55Z",
+				"Code" : "Success"
+			  }`), nil
+		}
+	}
+	c, err = p.Resolve()
+	assert.Nil(t, err)
+	assert.Equal(t, credentials.NewStsTokenCredential("STS.*******", "*******", "SecurityToken"), c)
+
+}

部分文件因为文件数量过多而无法显示