Преглед изворни кода

rebuid 更改share_controller报错

abiao пре 5 година
родитељ
комит
efcc982285
25 измењених фајлова са 3133 додато и 0 уклоњено
  1. 24 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/.gitignore
  2. 19 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/.travis.yml
  3. 201 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/LICENSE
  4. 106 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/README.md
  5. BIN
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/doc/dysms_python_20170525.zip
  6. 126 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/doc/tips.md
  7. 251 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/common.go
  8. 35 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/common_test.go
  9. 57 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/errors.go
  10. 236 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/query_send_details_request.go
  11. 202 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/send_sms_request.go
  12. 39 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/signature.go
  13. 14 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/signature_test.go
  14. 36 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/example/sample-dysms.go
  15. 35 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/example/sample.go
  16. 36 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sample-dysms.go
  17. 39 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/signature.go
  18. 494 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/sms.go
  19. 39 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/sms_test.go
  20. 47 0
      go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/wercker.yml
  21. 24 0
      go/gopath/src/github.com/GiterLab/urllib/.gitignore
  22. 202 0
      go/gopath/src/github.com/GiterLab/urllib/LICENSE
  23. 118 0
      go/gopath/src/github.com/GiterLab/urllib/README.md
  24. 547 0
      go/gopath/src/github.com/GiterLab/urllib/urllib.go
  25. 206 0
      go/gopath/src/github.com/GiterLab/urllib/urllib_test.go

+ 24 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/.gitignore

@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 19 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/.travis.yml

@@ -0,0 +1,19 @@
+language: go
+
+go:
+  - 1.2
+  - 1.3
+  - 1.4
+  - 1.5
+  - 1.6
+  - 1.7
+  - tip
+
+install:
+  - go get github.com/GiterLab/urllib
+  - go get github.com/tobyzxj/uuid
+  - go get github.com/GiterLab/aliyun-sms-go-sdk/sms
+
+script: 
+  - cd sms
+  - go test 

+ 201 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/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 {yyyy} {name of copyright owner}
+
+   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.

+ 106 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/README.md

@@ -0,0 +1,106 @@
+# aliyun-sms-go-sdk
+Aliyun SMS SDK for golang
+
+[![wercker status](https://app.wercker.com/status/5ef19ea6b2a854db200521592d0d7b2e/m/master "wercker status")](https://app.wercker.com/project/byKey/5ef19ea6b2a854db200521592d0d7b2e)
+
+[![Build Status](https://travis-ci.org/GiterLab/aliyun-sms-go-sdk.svg?branch=master)](https://travis-ci.org/GiterLab/aliyun-sms-go-sdk)
+[![GoDoc](https://godoc.org/github.com/GiterLab/aliyun-sms-go-sdk/sms?status.svg)](https://godoc.org/github.com/GiterLab/aliyun-sms-go-sdk/sms)
+
+## About
+短信服务(Short Message Service)是阿里云为用户提供的一种通信服务的能力,支持快速发送短信验证码、短信通知等。 完美支撑双11期间2亿用户,发送6亿短信,8万并发量。三网合一专属通道,与工信部携号转网平台实时互联。电信级运维保障,实时监控自动切换,到达率高达99%。
+
+<font color=#FF0000 size=4>
+注意: 
+2017年12月20日至2018年1月21日 消息服务中的短信功能和云市场(阿里短信服务)将迁移至云通信短信服务
+为了尽快使用更专业的服务,还请您确认迁移后尽快下载正确的SKD和API代码</font>
+## Install
+
+	$ go get -u -v github.com/GiterLab/aliyun-sms-go-sdk
+
+	`github.com/GiterLab/aliyun-sms-go-sdk/sms` 将停止维护
+	`github.com/GiterLab/aliyun-sms-go-sdk/dysms` 为迁移至云通信后的新SDK
+
+## Usage
+
+[使用帮助](https://github.com/GiterLab/aliyun-sms-go-sdk/blob/master/doc/tips.md)
+
+**已过时示例**
+
+	package main
+	
+	import (
+		"fmt"
+		"os"
+	
+		"github.com/GiterLab/aliyun-sms-go-sdk/sms"
+	)
+	
+	// modify it to yours
+	const (
+		ACCESSID  = "your_accessid"
+		ACCESSKEY = "your_accesskey"
+	)
+	
+	func main() {
+		// 2017年12月20日至2018年1月21日 消息服务中的短信功能和云市场(阿里短信服务)将迁移至云通信短信服务
+		// 为了尽快使用更专业的服务,还请您确认迁移后尽快下载正确的SKD和API代码
+		// 此测试接口过时,请勿再使用
+		sms.HttpDebugEnable = true
+		c := sms.New(ACCESSID, ACCESSKEY)
+		// send to one person
+		e, err := c.SendOne("1375821****", "多协云", "SMS_22175101", `{"company":"duoxieyun"}`)
+		if err != nil {
+			fmt.Println("send sms failed", err, e.Error())
+			os.Exit(0)
+		}
+		// send to more than one person
+		e, err = c.SendMulti([]string{"1375821****", "1835718****"}, "多协云", "SMS_22175101", `{"company":"duoxieyun"}`)
+		if err != nil {
+			fmt.Println("send sms failed", err, e.Error())
+			os.Exit(0)
+		}
+		fmt.Println("send sms succeed", e.GetRequestId())
+	}
+
+**迁移后的例子:**
+
+	package main
+	
+	import (
+		"fmt"
+		"os"
+	
+		"github.com/GiterLab/aliyun-sms-go-sdk/dysms"
+		"github.com/tobyzxj/uuid"
+	)
+	
+	// modify it to yours
+	const (
+		ACCESSID  = "your_accessid"
+		ACCESSKEY = "your_accesskey"
+	)
+	
+	func main() {
+		dysms.HTTPDebugEnable = true
+		dysms.SetACLClient(ACCESSID, ACCESSKEY) // dysms.New(ACCESSID, ACCESSKEY)
+	
+		// send to one person
+		respSendSms, err := dysms.SendSms(uuid.New(), "1375821****", "多协云", "SMS_22175101", `{"company":"duoxieyun"}`).DoActionWithException()
+		if err != nil {
+			fmt.Println("send sms failed", err, respSendSms.Error())
+			os.Exit(0)
+		}
+		fmt.Println("send sms succeed", respSendSms.GetRequestID())
+	}
+
+
+
+
+## Links 
+- [Short Message Service,SMS(短信服务)](https://www.aliyun.com/product/sms)
+- [HTTP协议及签名](https://help.aliyun.com/document_detail/56189.html?spm=5176.doc56189.6.576.JIUq2i)
+- [API签名机制](https://help.aliyun.com/document_detail/56189.html?spm=5176.product44282.6.576.VQczaW)
+
+## License
+
+This project is under the Apache Licence, Version 2.0. See the [LICENSE](https://github.com/GiterLab/aliyun-sms-go-sdk/blob/master/LICENSE) file for the full license text.

BIN
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/doc/dysms_python_20170525.zip


+ 126 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/doc/tips.md

@@ -0,0 +1,126 @@
+开发阿里云短信服务注意事项
+=======================
+
+### 名词解释 ###
+
+以下几个变量名称在调用 `SendOne` 和 `SendMulti` 函数时使用)
+
+`recnum`: 用户接收的手机号
+
+`signname`: 签名名称
+
+`templatecode`: 模板CODE
+
+`paramstring`: 参数字符串
+
+### 准备工作 ###
+
+[直达帮助页面](https://help.aliyun.com/document_detail/44346.html?spm=5176.doc44348.6.103.z0JAmF)
+
+1. 新建短信签名
+
+	用户需要先`新建短信签名`, 阿里云审核通过后会得到一个`签名名称`, 此`签名名称`即为`signname`
+
+2. 新建模板
+
+	- 用户需要先`新建模板`, 阿里云审核通过后会得到一个`模板CODE`, 此`模板CODE`即为`templatecode`
+
+	- 用户在创建模板的时候,会在模板中添加变量(一个或多个),如下为一个例子:
+
+			尊敬的用户,您的${device_id}(${devicename})设备已离线
+		
+		上面的`device_id`和`devicename`既是模板变量,用户在使用此SDK时,需要把这两个变量转换为参数字符串`paramstring`,此`paramstring`是一个json对象,如下所示:
+
+			{"device_id":"T0000001","devicename":"测试设备"}
+		
+		以上字符串即为 `paramstring`.
+
+		下面提供一个golang方式转换方法:
+		
+			A. 为每一个模板CODE建立一个模板参数结构体,实现一个String()方法
+
+				type Alarm_Offline_SMS_22120102 struct {
+					DeviceId   string `json:"device_id"`
+					DeviceName string `json:"devicename"`
+				}
+				
+				func (this Alarm_Offline_SMS_22120102) String() string {
+					body, err := json.Marshal(this)
+					if err != nil {
+						return ""
+					}
+					return string(body)
+				}
+
+			B. 在设置paramstring参数时,直接使用String()方法产生:
+
+				o := new(Alarm_Offline_SMS_22120102)
+				o.DeviceId = "T0000001"
+				o.DeviceName = "测试设备"
+				paramstring := o.String()
+
+				// 这里paramstring将会是符合要求的结果:{"device_id":"T0000001","devicename":"测试设备"}
+				fmt.Println(paramstring)
+
+			C. 例子sample.go,可以改写成如下形式:
+
+				package main
+				
+				import (
+					"encoding/json"
+					"fmt"
+					"os"
+				
+					"github.com/GiterLab/aliyun-sms-go-sdk/sms"
+				)
+				
+				// modify it to yours
+				const (
+					ENDPOINT  = "https://sms.aliyuncs.com/"
+					ACCESSID  = "your_accessid"
+					ACCESSKEY = "your_accesskey"
+				)
+				
+				type Register_SMS_22175101 struct {
+					CompanyName string `json:"company"`
+				}
+				
+				func (this *Register_SMS_22175101) String() string {
+					body, err := json.Marshal(this)
+					if err != nil {
+						return ""
+					}
+					return string(body)
+				}
+				
+				func main() {
+					sms.HttpDebugEnable = true
+					c := sms.New(ACCESSID, ACCESSKEY)
+				
+					// create a paramstring object
+					o := new(Register_SMS_22175101)
+					o.CompanyName = "duoxieyun"
+				
+					// send to one person
+					e, err := c.SendOne("1375821****", "多协云", "SMS_22175101", o.String())
+					if err != nil {
+						fmt.Println("send sms failed", err, e.Error())
+						os.Exit(0)
+					}
+					// send to more than one person
+					e, err = c.SendMulti([]string{"1375821****", "1835718****"}, "多协云", "SMS_22175101", o.String())
+					if err != nil {
+						fmt.Println("send sms failed", err, e.Error())
+						os.Exit(0)
+					}
+					fmt.Println("send sms succeed", e.GetRequestId())
+				}
+
+				
+
+
+		
+
+	
+
+	

+ 251 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/common.go

@@ -0,0 +1,251 @@
+// Package dysms Copyright 2016 The GiterLab Authors. All rights reserved.
+package dysms
+
+import (
+	"compress/gzip"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/GiterLab/urllib"
+	"github.com/tobyzxj/uuid"
+)
+
+// HTTPDebugEnable http调试开关
+var HTTPDebugEnable = false
+
+// acsClient 服务权限配置信息
+var acsClient Client
+
+func init() {
+	acsClient.SetVersion("2017-05-25")
+	acsClient.SetRegion("cn-hangzhou")
+	acsClient.SetEndPoint("http://dysmsapi.aliyuncs.com/")
+}
+
+// Request 请求参数设置
+type Request struct {
+	Param map[string]string
+}
+
+// Put 添加请求参数
+func (r *Request) Put(key, value string) error {
+	if r != nil {
+		if r.Param == nil {
+			r.Param = make(map[string]string)
+		}
+		r.Param[key] = value
+	}
+	return errors.New("requset is nil")
+}
+
+// Get 获取请求参数
+func (r *Request) Get(key string) string {
+	if r != nil && r.Param != nil {
+		return r.Param[key]
+	}
+	return ""
+}
+
+// CalcStringToSign 计算签名字符串
+func (r *Request) CalcStringToSign(httpMethod string) string {
+	if r != nil && r.Param != nil {
+		strslice := make([]string, len(r.Param))
+		i := 0
+		for k, v := range r.Param {
+			data := url.Values{}
+			data.Add(k, v)
+			strslice[i] = data.Encode()
+			strslice[i] = percentEncodeBefore(strslice[i])
+			i++
+		}
+		sort.Strings(strslice)
+		return httpMethod + "&" + percentEncode("/") + "&" + percentEncode(strings.Join(strslice, "&"))
+	}
+	return ""
+}
+
+// Do 发送HTTP请求
+func (r *Request) Do(action string) (body []byte, httpCode int, err error) {
+	if r == nil || r.Param == nil {
+		return nil, 0, errors.New("requset is nil")
+	}
+
+	if action != "" {
+		r.Put("Action", action)
+	}
+	signature := signatureMethod(acsClient.AccessKey, r.CalcStringToSign("GET"))
+
+	// HTTP requset
+	httpReq := urllib.Get(acsClient.EndPoint)
+	if HTTPDebugEnable {
+		httpReq.Debug(true)
+	}
+	for k, v := range r.Param {
+		httpReq.Param(k, v)
+	}
+	httpReq.Param("Signature", signature)
+	resp, err := httpReq.Response()
+	if err != nil {
+		return nil, 0, err
+	}
+	if resp.Body == nil {
+		return nil, resp.StatusCode, nil
+	}
+	defer resp.Body.Close()
+	if resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, errGzip := gzip.NewReader(resp.Body)
+		if errGzip != nil {
+			return nil, resp.StatusCode, errGzip
+		}
+		body, err = ioutil.ReadAll(reader)
+	} else {
+		body, err = ioutil.ReadAll(resp.Body)
+	}
+	if err != nil {
+		return nil, resp.StatusCode, err
+	}
+	if HTTPDebugEnable {
+		fmt.Println("C-->S:", httpReq.DumpRequestString())
+		fmt.Println("S-->C:", string(body))
+	}
+	return body, resp.StatusCode, nil
+}
+
+// 创建一个新的请求参数
+func newRequset() *Request {
+	req := &Request{Param: make(map[string]string)}
+
+	// 1. 系统参数
+	req.Put("SignatureMethod", "HMAC-SHA1")
+	req.Put("SignatureNonce", uuid.New())
+	req.Put("AccessKeyId", acsClient.AccessID)
+	req.Put("SignatureVersion", "1.0")
+	req.Put("Timestamp", time.Now().UTC().Format(time.RFC3339))
+	req.Put("Format", "JSON")
+
+	// 2. 业务API参数
+	// req.Put("Action", "SendSms")
+	req.Put("Version", acsClient.Version)
+	req.Put("RegionId", acsClient.Region)
+	// req.Put("PhoneNumbers", "your_phonenumbers")
+	// req.Put("SignName", "your_signname")
+	// req.Put("TemplateParam", "your_ParamString")
+	// req.Put("TemplateCode", "your_templatecode")
+	// req.Put("OutId", "your_outid")
+
+	return req
+}
+
+// Client HTTP请求配置信息
+type Client struct {
+	// API版本
+	Version string
+	// SMS服务地域, 默认为cn-hangzhou
+	Region string
+	// SMS服务的地址,默认为(http://dysmsapi.aliyuncs.com/)
+	EndPoint string
+	// 访问SMS服务的accessid,通过官方网站申请或通过管理员获取
+	AccessID string
+	// 访问SMS服务的accesskey,通过官方网站申请或通过管理员获取
+	AccessKey string
+	// 连接池中每个连接的Socket超时,单位为秒,可以为int或float。默认值为30
+	SocketTimeout int
+}
+
+// SetVersion API版本
+func (c *Client) SetVersion(version string) {
+	if c != nil {
+		c.Version = version
+	}
+}
+
+// SetRegion 设置SMS服务地域
+func (c *Client) SetRegion(region string) {
+	if c != nil {
+		c.Region = region
+	}
+}
+
+// SetEndPoint 设置短信服务器
+func (c *Client) SetEndPoint(endPoint string) {
+	if c != nil {
+		c.EndPoint = endPoint
+	}
+}
+
+// SetAccessID 设置短信服务的accessid,通过官方网站申请或通过管理员获取
+func (c *Client) SetAccessID(accessid string) {
+	if c != nil {
+		c.AccessID = accessid
+	}
+}
+
+// SetAccessKey 设置短信服务的accesskey,通过官方网站申请或通过管理员获取
+func (c *Client) SetAccessKey(accesskey string) {
+	if c != nil {
+		c.AccessKey = accesskey
+	}
+}
+
+// SetSocketTimeout 设置短信服务的Socket超时,单位为秒,可以为int或float。默认值为30
+func (c *Client) SetSocketTimeout(sockettimeout int) {
+	if sockettimeout == 0 {
+		sockettimeout = 30
+	}
+	if c != nil {
+		c.SocketTimeout = sockettimeout
+	}
+}
+
+// SetACLClient 配置默认的服务权限信息
+func SetACLClient(accessid, accesskey string) *Client {
+	acsClient.SetAccessID(accessid)
+	acsClient.SetAccessKey(accesskey)
+
+	if urllib.GetDefaultSetting().Transport == nil {
+		// set default setting for urllib
+		trans := &http.Transport{
+			MaxIdleConnsPerHost: 500,
+			Dial: (&net.Dialer{
+				Timeout: time.Duration(15) * time.Second,
+			}).Dial,
+		}
+
+		urlSetting := urllib.HttpSettings{
+			ShowDebug:        false,            // ShowDebug
+			UserAgent:        "GiterLab",       // UserAgent
+			ConnectTimeout:   15 * time.Second, // ConnectTimeout
+			ReadWriteTimeout: 30 * time.Second, // ReadWriteTimeout
+			TlsClientConfig:  nil,              // TlsClientConfig
+			Proxy:            nil,              // Proxy
+			Transport:        trans,            // Transport
+			EnableCookie:     false,            // EnableCookie
+			Gzip:             true,             // Gzip
+			DumpBody:         true,             // DumpBody
+		}
+		if acsClient.SocketTimeout != 0 {
+			urlSetting.ConnectTimeout = time.Duration(acsClient.SocketTimeout) * time.Second
+			urlSetting.ReadWriteTimeout = time.Duration(acsClient.SocketTimeout) * time.Second
+		}
+		if HTTPDebugEnable {
+			urlSetting.ShowDebug = true
+		} else {
+			urlSetting.ShowDebug = false
+		}
+		urllib.SetDefaultSetting(urlSetting)
+	}
+
+	return &acsClient
+}
+
+// New 兼容 sms SDK
+func New(accessid, accesskey string) *Client {
+	return SetACLClient(accessid, accesskey)
+}

Разлика између датотеке није приказан због своје велике величине
+ 35 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/common_test.go


+ 57 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/errors.go

@@ -0,0 +1,57 @@
+// Package dysms Copyright 2016 The GiterLab Authors. All rights reserved.
+package dysms
+
+import (
+	"encoding/json"
+)
+
+// ErrorMessage 短信服务器返回的错误信息
+type ErrorMessage struct {
+	HTTPCode  int     `json:"-"`
+	RequestID *string `json:"RequestId,omitempty"`
+	Code      *string `json:"Code,omitempty"`
+	Message   *string `json:"Message,omitempty"`
+}
+
+// SetHTTPCode 设置HTTP错误码
+func (e *ErrorMessage) SetHTTPCode(code int) {
+	e.HTTPCode = code
+}
+
+// GetHTTPCode 获取HTTP请求的错误码
+func (e *ErrorMessage) GetHTTPCode() int {
+	return e.HTTPCode
+}
+
+// GetRequestID 获取请求的ID序列
+func (e *ErrorMessage) GetRequestID() string {
+	if e != nil && e.RequestID != nil {
+		return *e.RequestID
+	}
+	return ""
+}
+
+// GetCode 获取请求的错误码
+func (e *ErrorMessage) GetCode() string {
+	if e != nil && e.Code != nil {
+		return *e.Code
+	}
+	return ""
+}
+
+// GetMessage 获取错误信息
+func (e *ErrorMessage) GetMessage() string {
+	if e != nil && e.Message != nil {
+		return *e.Message
+	}
+	return ""
+}
+
+// Error 序列化成字符串
+func (e *ErrorMessage) Error() string {
+	body, err := json.Marshal(e)
+	if err != nil {
+		return ""
+	}
+	return string(body)
+}

+ 236 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/query_send_details_request.go

@@ -0,0 +1,236 @@
+// Package dysms Copyright 2016 The GiterLab Authors. All rights reserved.
+package dysms
+
+import (
+	"encoding/json"
+	"errors"
+)
+
+// SmsSendDetailDTO 短信发送记录信息
+type SmsSendDetailDTO struct {
+	PhoneNum     string `json:"PhoneNum"`     // 手机号码
+	SendStatus   int    `json:"SendStatus"`   // 发送状态 1:等待回执,2:发送失败,3:发送成功
+	ErrCode      string `json:"ErrCode"`      // 运营商短信错误码
+	TemplateCode string `json:"TemplateCode"` // 模板ID
+	Content      string `json:"Content"`      // 短信内容
+	SendDate     string `json:"SendDate"`     // 发送时间
+	ReceiveDate  string `json:"ReceiveDate"`  // 接收时间
+	OutID        string `json:"OutId"`        // 外部流水扩展字段
+}
+
+// SmsSendDetailDTOs 短信发送记录查询列表
+type SmsSendDetailDTOs struct {
+	SmsSendDetailDTO []SmsSendDetailDTO `json:"SmsSendDetailDTO"`
+}
+
+// QuerySendDetailsResponse 短信发送记录查询接口服务器响应
+type QuerySendDetailsResponse struct {
+	ErrorMessage
+	TotalCount        *int               `json:"TotalCount,omitempty"`        // 发送总条数
+	TotalPage         *int               `json:"TotalPage,omitempty"`         // 总页数
+	SmsSendDetailDTOs *SmsSendDetailDTOs `json:"SmsSendDetailDTOs,omitempty"` // 发送明细结构体
+}
+
+// GetTotalCount 发送总条数
+func (q *QuerySendDetailsResponse) GetTotalCount() int {
+	if q != nil && q.TotalCount != nil {
+		return *q.TotalCount
+	}
+	return 0
+}
+
+// GetTotalPage 总页数
+func (q *QuerySendDetailsResponse) GetTotalPage() int {
+	if q != nil && q.TotalPage != nil {
+		return *q.TotalPage
+	}
+	return 0
+}
+
+// GetSmsSendDetailDTOs 获取短信发送记录
+func (q *QuerySendDetailsResponse) GetSmsSendDetailDTOs() *SmsSendDetailDTOs {
+	if q != nil && q.SmsSendDetailDTOs != nil {
+		return q.SmsSendDetailDTOs
+	}
+	return nil
+}
+
+// String 序列化成JSON字符串
+func (q QuerySendDetailsResponse) String() string {
+	body, err := json.Marshal(q)
+	if err != nil {
+		return ""
+	}
+	return string(body)
+}
+
+// QuerySendDetailsRequest 短信发送记录查询接口请求
+type QuerySendDetailsRequest struct {
+	Request *Request
+}
+
+// SetSendDate 设置短信发送日期 必须
+// 短信发送日期格式yyyyMMdd,支持最近30天记录查询
+func (q *QuerySendDetailsRequest) SetSendDate(sendDate string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("SendDate", sendDate)
+	}
+}
+
+// GetSendDate 获取短信发送日期
+func (q *QuerySendDetailsRequest) GetSendDate() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("SendDate")
+	}
+	return ""
+}
+
+// SetPageSize 设置页大小 必须
+// 页大小Max=50
+func (q *QuerySendDetailsRequest) SetPageSize(pageSize string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("PageSize", pageSize)
+	}
+}
+
+// GetPageSize 获取设置页大小
+func (q *QuerySendDetailsRequest) GetPageSize() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("PageSize")
+	}
+	return ""
+}
+
+// SetResourceOwnerID 来源于python,未知参数
+func (q *QuerySendDetailsRequest) SetResourceOwnerID(resourceOwnerID string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("ResourceOwnerId", resourceOwnerID)
+	}
+}
+
+// GetResourceOwnerID 来源于python,未知参数
+func (q *QuerySendDetailsRequest) GetResourceOwnerID() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("ResourceOwnerId")
+	}
+	return ""
+}
+
+// SetOwnerID 来源于python,未知参数
+func (q *QuerySendDetailsRequest) SetOwnerID(ownerID string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("OwnerId", ownerID)
+	}
+}
+
+// GetOwnerID 来源于python,未知参数
+func (q *QuerySendDetailsRequest) GetOwnerID() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("OwnerId")
+	}
+	return ""
+}
+
+// SetPhoneNumber 设置短信接收号码 必须
+func (q *QuerySendDetailsRequest) SetPhoneNumber(phoneNumber string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("PhoneNumber", phoneNumber)
+	}
+}
+
+// GetPhoneNumber 获取短信接收号码
+func (q *QuerySendDetailsRequest) GetPhoneNumber() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("PhoneNumber")
+	}
+	return ""
+}
+
+// SetCurrentPage 设置当前页码 必须
+func (q *QuerySendDetailsRequest) SetCurrentPage(currentPage string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("CurrentPage", currentPage)
+	}
+}
+
+// GetCurrentPage 获取当前页码
+func (q *QuerySendDetailsRequest) GetCurrentPage() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("CurrentPage")
+	}
+	return ""
+}
+
+// SetBizID 设置发送流水号 可选
+// 从调用发送接口返回值中获取
+func (q *QuerySendDetailsRequest) SetBizID(bizID string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("BizId", bizID)
+	}
+}
+
+// GetBizID 获取发送流水号
+func (q *QuerySendDetailsRequest) GetBizID() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("BizId")
+	}
+	return ""
+}
+
+// SetResourceOwnerAccount 来源于python,未知参数
+func (q *QuerySendDetailsRequest) SetResourceOwnerAccount(resourceOwnerAccount string) {
+	if q != nil && q.Request != nil {
+		q.Request.Put("ResourceOwnerAccount", resourceOwnerAccount)
+	}
+}
+
+// GetResourceOwnerAccount 来源于python,未知参数
+func (q *QuerySendDetailsRequest) GetResourceOwnerAccount() string {
+	if q != nil && q.Request != nil {
+		return q.Request.Get("ResourceOwnerAccount")
+	}
+	return ""
+}
+
+// DoActionWithException 发起HTTP请求
+func (q *QuerySendDetailsRequest) DoActionWithException() (resp *QuerySendDetailsResponse, err error) {
+	if q != nil && q.Request != nil {
+		resp := &QuerySendDetailsResponse{}
+		body, httpCode, err := q.Request.Do("QuerySendDetails")
+		resp.SetHTTPCode(httpCode)
+		if err != nil {
+			return resp, err
+		}
+		err = json.Unmarshal(body, resp)
+		if err != nil {
+			return resp, err
+		}
+		if httpCode != 200 {
+			return resp, errors.New(resp.GetCode())
+		}
+		return resp, nil
+	}
+	return nil, errors.New("QuerySendDetailsRequest is nil")
+}
+
+// QuerySendDetails 短信发送记录查询接口
+// bizID 可选 - 流水号
+// phoneNumber 查询的手机号码
+// pageSize 必填 - 页大小
+// currentPage 必填 - 当前页码从1开始计数
+// sendDate 必填 - 发送日期 支持30天内记录查询,格式yyyyMMdd
+func QuerySendDetails(bizID, phoneNumber, pageSize, currentPage, sendDate string) *QuerySendDetailsRequest {
+	req := newRequset()
+	req.Put("Version", "2017-05-25")
+	req.Put("Action", "QuerySendDetails")
+
+	r := &QuerySendDetailsRequest{Request: req}
+	r.SetPhoneNumber(phoneNumber) // 查询的手机号码
+	if bizID != "" {
+		r.SetBizID(bizID) // 可选 - 流水号
+	}
+	r.SetSendDate(sendDate)       // 必填 - 发送日期 支持30天内记录查询,格式yyyyMMdd
+	r.SetCurrentPage(currentPage) // 必填 - 当前页码从1开始计数
+	r.SetPageSize(pageSize)       // 必填 - 页大小
+	return r
+}

+ 202 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/send_sms_request.go

@@ -0,0 +1,202 @@
+// Package dysms Copyright 2016 The GiterLab Authors. All rights reserved.
+package dysms
+
+import (
+	"encoding/json"
+	"errors"
+)
+
+// SendSmsResponse 发送短信接口服务器响应
+type SendSmsResponse struct {
+	ErrorMessage
+	BizID *string `json:"BizId,omitempty"` // 发送回执ID,可根据该ID查询具体的发送状态
+}
+
+// GetBizID 发送回执ID,可根据该ID查询具体的发送状态
+func (s *SendSmsResponse) GetBizID() string {
+	if s != nil && s.BizID != nil {
+		return *s.BizID
+	}
+	return ""
+}
+
+// String 序列化成JSON字符串
+func (s SendSmsResponse) String() string {
+	body, err := json.Marshal(s)
+	if err != nil {
+		return ""
+	}
+	return string(body)
+}
+
+// SendSmsRequest 发送短信接口请求
+type SendSmsRequest struct {
+	Request *Request
+}
+
+// SetOutID 设置外部流水扩展字段
+func (s *SendSmsRequest) SetOutID(outID string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("OutId", outID)
+	}
+}
+
+// GetOutID 获取外部流水扩展字段
+func (s *SendSmsRequest) GetOutID(outID string) string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("OutId")
+	}
+	return ""
+}
+
+// SetSignName 设置短信签名
+func (s *SendSmsRequest) SetSignName(signName string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("SignName", signName)
+	}
+}
+
+// GetSignName 获取短信签名
+func (s *SendSmsRequest) GetSignName() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("SignName")
+	}
+	return ""
+}
+
+// SetResourceOwnerID 来源于python,未知参数
+func (s *SendSmsRequest) SetResourceOwnerID(resourceOwnerID string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("ResourceOwnerId", resourceOwnerID)
+	}
+}
+
+// GetResourceOwnerID 来源于python,未知参数
+func (s *SendSmsRequest) GetResourceOwnerID() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("ResourceOwnerId")
+	}
+	return ""
+}
+
+// SetOwnerID 来源于python,未知参数
+func (s *SendSmsRequest) SetOwnerID(ownerID string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("OwnerId", ownerID)
+	}
+}
+
+// GetOwnerID 来源于python,未知参数
+func (s *SendSmsRequest) GetOwnerID() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("OwnerId")
+	}
+	return ""
+}
+
+// SetTemplateCode 短信模板ID
+func (s *SendSmsRequest) SetTemplateCode(templateCode string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("TemplateCode", templateCode)
+	}
+}
+
+// GetTemplateCode 获取短信模板ID
+func (s *SendSmsRequest) GetTemplateCode() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("TemplateCode")
+	}
+	return ""
+}
+
+// SetPhoneNumbers 短信接收号码。
+// 支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,
+// 验证码类型的短信推荐使用单条调用的方式
+func (s *SendSmsRequest) SetPhoneNumbers(phoneNumbers string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("PhoneNumbers", phoneNumbers)
+	}
+}
+
+// GetPhoneNumbers 获取短信接收号码。
+func (s *SendSmsRequest) GetPhoneNumbers() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("PhoneNumbers")
+	}
+	return ""
+}
+
+// SetResourceOwnerAccount 来源于python,未知参数
+func (s *SendSmsRequest) SetResourceOwnerAccount(resourceOwnerAccount string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("ResourceOwnerAccount", resourceOwnerAccount)
+	}
+}
+
+// GetResourceOwnerAccount 来源于python,未知参数
+func (s *SendSmsRequest) GetResourceOwnerAccount() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("ResourceOwnerAccount")
+	}
+	return ""
+}
+
+// SetTemplateParam 短信模板变量替换JSON串,
+// 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,
+// 比如短信内容中包含\r\n的情况在JSON中需要表示成\r\n,否则会导致JSON在服务端解析失败
+func (s *SendSmsRequest) SetTemplateParam(templateParam string) {
+	if s != nil && s.Request != nil {
+		s.Request.Put("TemplateParam", templateParam)
+	}
+}
+
+// GetTemplateParam 获取短信模板变量替换JSON串,
+func (s *SendSmsRequest) GetTemplateParam() string {
+	if s != nil && s.Request != nil {
+		return s.Request.Get("TemplateParam")
+	}
+	return ""
+}
+
+// DoActionWithException 发起HTTP请求
+func (s *SendSmsRequest) DoActionWithException() (resp *SendSmsResponse, err error) {
+	if s != nil && s.Request != nil {
+		resp := &SendSmsResponse{}
+		body, httpCode, err := s.Request.Do("SendSms")
+		resp.SetHTTPCode(httpCode)
+		if err != nil {
+			return resp, err
+		}
+		err = json.Unmarshal(body, resp)
+		if err != nil {
+			return resp, err
+		}
+		if httpCode != 200 {
+			return resp, errors.New(resp.GetCode())
+		}
+		return resp, nil
+	}
+	return nil, errors.New("SendSmsRequest is nil")
+}
+
+// SendSms 发送短信接口
+// businessID 设置业务请求流水号,必填。
+// phoneNumbers 短信发送的号码列表,必填。 多手机号使用,分割
+// signName 短信签名
+// templateCode 申请的短信模板编码,必填
+// templateParam 短信模板变量参数
+func SendSms(businessID, phoneNumbers, signName, templateCode, templateParam string) *SendSmsRequest {
+	req := newRequset()
+	req.Put("Version", "2017-05-25")
+	req.Put("Action", "SendSms")
+
+	r := &SendSmsRequest{Request: req}
+	r.SetOutID(businessID)          // 设置业务请求流水号,必填
+	r.SetSignName(signName)         // 短信签名
+	r.SetPhoneNumbers(phoneNumbers) // 短信发送的号码列表,必填。
+	r.SetTemplateCode(templateCode) // 短信模板
+	if templateParam != "" {
+		r.SetTemplateParam(templateParam) // 短信模板参数
+	}
+	return r
+}

+ 39 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/signature.go

@@ -0,0 +1,39 @@
+// Package dysms Copyright 2016 The GiterLab Authors. All rights reserved.
+package dysms
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
+	"net/url"
+	"strings"
+)
+
+// 计算 HMAC 值。
+//     按照 RFC2104 的定义,使用得到的签名字符串计算签名 HMAC 值。
+//     注意:计算签名时使用的 Key 就是您持有的 Access Key Secret 并加上一个 “&” 字符(ASCII:38),使用的哈希算法是 SHA1。
+// 计算签名值。
+//     按照 Base64 编码规则 把步骤 3 中的 HMAC 值编码成字符串,即得到签名值(Signature)。
+func signatureMethod(key, stringToSign string) string {
+	// The signature method is supposed to be HmacSHA1
+	// A switch case is required if there is other methods available
+	mac := hmac.New(sha1.New, []byte(key+"&"))
+	mac.Write([]byte(stringToSign))
+	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
+}
+
+// 把编码后的字符串中加号(+)替换成%20、星号(*)替换成%2A、%7E 替换回波浪号(~), 即可得到所需要的编码字符串
+func percentEncodeBefore(s string) string {
+	s = strings.Replace(s, "+", "%20", -1)
+	s = strings.Replace(s, "*", "%2A", -1)
+	s = strings.Replace(s, "%7E", "~", -1)
+
+	return s
+}
+
+// 一般支持 URL 编码的库(比如 Java 中的 java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的。
+// 实现时可以直接使用这类方式进行编码,
+func percentEncode(s string) string {
+	s = url.QueryEscape(s)
+	return percentEncodeBefore(s)
+}

Разлика између датотеке није приказан због своје велике величине
+ 14 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/dysms/signature_test.go


+ 36 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/example/sample-dysms.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/GiterLab/aliyun-sms-go-sdk/dysms"
+	"github.com/tobyzxj/uuid"
+)
+
+// modify it to yours
+const (
+	ACCESSID  = "your_accessid"
+	ACCESSKEY = "your_accesskey"
+)
+
+func main() {
+	dysms.HTTPDebugEnable = true
+	dysms.SetACLClient(ACCESSID, ACCESSKEY) // dysms.New(ACCESSID, ACCESSKEY)
+
+	// 短信发送
+	respSendSms, err := dysms.SendSms(uuid.New(), "1375821****", "多协云", "SMS_22175101", `{"company":"duoxieyun"}`).DoActionWithException()
+	if err != nil {
+		fmt.Println("send sms failed", err, respSendSms.Error())
+		os.Exit(0)
+	}
+	fmt.Println("send sms succeed", respSendSms.String())
+
+	// 查询短信
+	respQuerySendDetails, err := dysms.QuerySendDetails("612710515335092485^0", "1375821****", "10", "1", "20180107").DoActionWithException()
+	if err != nil {
+		fmt.Println("query sms failed", err, respQuerySendDetails.Error())
+		os.Exit(0)
+	}
+	fmt.Println("query sms succeed", respQuerySendDetails.String())
+}

+ 35 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/example/sample.go

@@ -0,0 +1,35 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/GiterLab/aliyun-sms-go-sdk/sms"
+)
+
+// modify it to yours
+const (
+	ACCESSID  = "your_accessid"
+	ACCESSKEY = "your_accesskey"
+)
+
+func main() {
+	// 2017年12月20日至2018年1月21日 消息服务中的短信功能和云市场(阿里短信服务)将迁移至云通信短信服务
+	// 为了尽快使用更专业的服务,还请您确认迁移后尽快下载正确的SKD和API代码
+	// 此测试接口过时,请勿再使用
+	sms.HTTPDebugEnable = true
+	c := sms.New(ACCESSID, ACCESSKEY)
+	// send to one person
+	e, err := c.SendOne("1375821****", "多协云", "SMS_22175101", `{"company":"duoxieyun"}`)
+	if err != nil {
+		fmt.Println("send sms failed", err, e.Error())
+		os.Exit(0)
+	}
+	// send to more than one person
+	e, err = c.SendMulti([]string{"1375821****", "1835718****"}, "多协云", "SMS_22175101", `{"company":"duoxieyun"}`)
+	if err != nil {
+		fmt.Println("send sms failed", err, e.Error())
+		os.Exit(0)
+	}
+	fmt.Println("send sms succeed", e.GetRequestID())
+}

+ 36 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sample-dysms.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/GiterLab/aliyun-sms-go-sdk/dysms"
+	"github.com/tobyzxj/uuid"
+)
+
+// modify it to yours
+const (
+	ACCESSID  = "your_accessid"
+	ACCESSKEY = "your_accesskey"
+)
+
+func main() {
+	dysms.HTTPDebugEnable = true
+	dysms.SetACLClient(ACCESSID, ACCESSKEY) // dysms.New(ACCESSID, ACCESSKEY)
+
+	// 短信发送
+	respSendSms, err := dysms.SendSms(uuid.New(), "1375821****", "多协云", "SMS_22175101", `{"company":"duoxieyun"}`).DoActionWithException()
+	if err != nil {
+		fmt.Println("send sms failed", err, respSendSms.Error())
+		os.Exit(0)
+	}
+	fmt.Println("send sms succeed", respSendSms.String())
+
+	// 查询短信
+	respQuerySendDetails, err := dysms.QuerySendDetails("612710515335092485^0", "1375821****", "10", "1", "20180107").DoActionWithException()
+	if err != nil {
+		fmt.Println("query sms failed", err, respQuerySendDetails.Error())
+		os.Exit(0)
+	}
+	fmt.Println("query sms succeed", respQuerySendDetails.String())
+}

+ 39 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/signature.go

@@ -0,0 +1,39 @@
+// Package sms Copyright 2016 The GiterLab Authors. All rights reserved.
+package sms
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
+	"net/url"
+	"strings"
+)
+
+// 计算 HMAC 值。
+//     按照 RFC2104 的定义,使用得到的签名字符串计算签名 HMAC 值。
+//     注意:计算签名时使用的 Key 就是您持有的 Access Key Secret 并加上一个 “&” 字符(ASCII:38),使用的哈希算法是 SHA1。
+// 计算签名值。
+//     按照 Base64 编码规则 把步骤 3 中的 HMAC 值编码成字符串,即得到签名值(Signature)。
+func signatureMethod(key, stringToSign string) string {
+	// The signature method is supposed to be HmacSHA1
+	// A switch case is required if there is other methods available
+	mac := hmac.New(sha1.New, []byte(key+"&"))
+	mac.Write([]byte(stringToSign))
+	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
+}
+
+// 把编码后的字符串中加号(+)替换成%20、星号(*)替换成%2A、%7E 替换回波浪号(~), 即可得到所需要的编码字符串
+func percentEncodeBefore(s string) string {
+	s = strings.Replace(s, "+", "%20", -1)
+	s = strings.Replace(s, "*", "%2A", -1)
+	s = strings.Replace(s, "%7E", "~", -1)
+
+	return s
+}
+
+// 一般支持 URL 编码的库(比如 Java 中的 java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的。
+// 实现时可以直接使用这类方式进行编码,
+func percentEncode(s string) string {
+	s = url.QueryEscape(s)
+	return percentEncodeBefore(s)
+}

+ 494 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/sms.go

@@ -0,0 +1,494 @@
+// Package sms Copyright 2016 The GiterLab Authors. All rights reserved.
+package sms
+
+import (
+	"compress/gzip"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/GiterLab/urllib"
+	"github.com/tobyzxj/uuid"
+)
+
+// HTTPDebugEnable http调试开关
+var HTTPDebugEnable = false
+
+// Param 短信发送所需要的参数
+type Param struct {
+	// 系统参数
+	AccessKeyID      string // 阿里云颁发给用户的访问服务所用的密钥ID
+	Timestamp        string // 格式为:yyyy-MM-dd’T’HH:mm:ss’Z’;时区为:GMT
+	Format           string // 没传默认为JSON,可选填值:XML
+	SignatureMethod  string // 建议固定值:HMAC-SHA1
+	SignatureVersion string // 建议固定值:1.0
+	SignatureNonce   string // 用于请求的防重放攻击,每次请求唯一
+	Signature        string // 最终生成的签名结果值
+
+	// 业务参数
+	Action       string // API的命名,固定值,如发送短信API的值为:SendSms
+	Version      string // API的版本,固定值,如短信API的值为:2017-05-25
+	RegionID     string // API支持的RegionID,如短信API的值为:cn-hangzhou
+	RecNum       string // 手机号
+	SignName     string // 短信签名
+	TemplateCode string // 短信模板
+	ParamString  string // 短信版本中的参数
+	OutID        string // 外部流水扩展字段
+}
+
+// SetAccessKeyID 设置密钥ID
+func (p *Param) SetAccessKeyID(accesskeyid string) {
+	p.AccessKeyID = accesskeyid
+}
+
+// GetAccessKeyID 获取密钥ID
+func (p *Param) GetAccessKeyID() string {
+	return p.AccessKeyID
+}
+
+// SetTimestamp 设置时间戳
+func (p *Param) SetTimestamp(timestamp string) {
+	p.Timestamp = timestamp
+}
+
+// GetTimestamp 获取时间戳
+func (p *Param) GetTimestamp() string {
+	return p.Timestamp
+}
+
+// SetFormat 设置返回格式,JSON/XML
+func (p *Param) SetFormat(format string) {
+	p.Format = format
+}
+
+// GetFormat 获取返回格式
+func (p *Param) GetFormat() string {
+	return p.Format
+}
+
+// SetSignatureMethod 设置签名方法
+func (p *Param) SetSignatureMethod(signaturemethod string) {
+	p.SignatureMethod = signaturemethod
+}
+
+// GetSignatureMethod 获取签名方法
+func (p *Param) GetSignatureMethod() string {
+	return p.SignatureMethod
+}
+
+// SetSignatureVersion 设置签名版本
+func (p *Param) SetSignatureVersion(signatureversion string) {
+	p.SignatureVersion = signatureversion
+}
+
+// GetSignatureVersion 获取签名版本
+func (p *Param) GetSignatureVersion() string {
+	return p.SignatureVersion
+}
+
+// SetSignatureNonce 设置每一次请求的唯一序列
+func (p *Param) SetSignatureNonce(signaturenonce string) {
+	p.SignatureNonce = signaturenonce
+}
+
+// GetSignatureNonce 获取当前请求的序列
+func (p *Param) GetSignatureNonce() string {
+	return p.SignatureNonce
+}
+
+// SetSignature 设置最终的签名结果
+func (p *Param) SetSignature(signature string) {
+	p.Signature = signature
+}
+
+// GetSignature 获取签名结果
+func (p *Param) GetSignature() string {
+	return p.Signature
+}
+
+// SetAction 设置API请求方法参数
+func (p *Param) SetAction(action string) {
+	p.Action = action
+}
+
+// GetAction 获取API请求方法参数
+func (p *Param) GetAction() string {
+	return p.Action
+}
+
+// SetVersion 设置API版本
+func (p *Param) SetVersion(version string) {
+	p.Version = version
+}
+
+// GetVersion 获取API版本
+func (p *Param) GetVersion() string {
+	return p.Version
+}
+
+// SetRegionID 设置API的RegionID
+func (p *Param) SetRegionID(regioniD string) {
+	p.RegionID = regioniD
+}
+
+// GetRegionID 获取API的RegionID
+func (p *Param) GetRegionID() string {
+	return p.RegionID
+}
+
+// SetRecNum 设置短信接收的手机号
+func (p *Param) SetRecNum(RecNum string) {
+	p.RecNum = RecNum
+}
+
+// GetRecNum 获取短信接收的手机号
+func (p *Param) GetRecNum() string {
+	return p.RecNum
+}
+
+// SetSignName 设置签名参数
+func (p *Param) SetSignName(signname string) {
+	p.SignName = signname
+}
+
+// GetSignName 获取签名参数
+func (p *Param) GetSignName() string {
+	return p.SignName
+}
+
+// SetTemplateCode 设置短信模板
+func (p *Param) SetTemplateCode(templatecode string) {
+	p.TemplateCode = templatecode
+}
+
+// GetTemplateCode 获取短信模板
+func (p *Param) GetTemplateCode() string {
+	return p.TemplateCode
+}
+
+// SetParamString 设置短信模板参数
+func (p *Param) SetParamString(ParamString string) {
+	p.ParamString = ParamString
+}
+
+// GetParamString 获取短信模板参数
+func (p *Param) GetParamString() string {
+	return p.ParamString
+}
+
+// SetOutID 设置外部流水扩展字段
+func (p *Param) SetOutID(outid string) {
+	p.OutID = outid
+}
+
+// GetOutID 获取外部流水扩展字段
+func (p *Param) GetOutID() string {
+	return p.OutID
+}
+
+// ErrorMessage 短信服务器返回的错误信息
+type ErrorMessage struct {
+	HTTPCode  int     `json:"-"`
+	Model     *string `json:"Model,omitempty"`
+	RequestID *string `json:"RequestId,omitempty"`
+	Message   *string `json:"Message,omitempty"`
+	Code      *string `json:"Code,omitempty"`
+}
+
+// GetHTTPCode 获取HTTP请求的错误码
+func (e *ErrorMessage) GetHTTPCode() int {
+	return e.HTTPCode
+}
+
+// SetHTTPCode 设置HTTP错误码
+func (e *ErrorMessage) SetHTTPCode(code int) {
+	e.HTTPCode = code
+}
+
+// GetModel get model
+func (e *ErrorMessage) GetModel() string {
+	if e != nil && e.Model != nil {
+		return *e.Model
+	}
+	return ""
+}
+
+// GetRequestID 获取请求的ID序列
+func (e *ErrorMessage) GetRequestID() string {
+	if e != nil && e.RequestID != nil {
+		return *e.RequestID
+	}
+	return ""
+}
+
+// GetMessage 获取错误信息
+func (e *ErrorMessage) GetMessage() string {
+	if e != nil && e.Message != nil {
+		return *e.Message
+	}
+	return ""
+}
+
+// GetCode 获取请求的错误码
+func (e *ErrorMessage) GetCode() string {
+	if e != nil && e.Code != nil {
+		return *e.Code
+	}
+	return ""
+}
+
+// Error 序列化成字符串
+func (e *ErrorMessage) Error() string {
+	body, err := json.Marshal(e)
+	if err != nil {
+		return ""
+	}
+	return string(body)
+}
+
+// Client HTTP请求配置信息
+type Client struct {
+	// SMS服务的地址,默认为(https://sms.aliyuncs.com)
+	EndPoint string
+	// 访问SMS服务的accessid,通过官方网站申请或通过管理员获取
+	AccessID string
+	// 访问SMS服务的accesskey,通过官方网站申请或通过管理员获取
+	AccessKey string
+	// 连接池中每个连接的Socket超时,单位为秒,可以为int或float。默认值为30
+	SocketTimeout int
+
+	// 其他参数
+	Param Param
+	param map[string]string
+}
+
+// SetEndPoint 设置短信服务器
+func (c *Client) SetEndPoint(endPoint string) {
+	c.EndPoint = endPoint
+}
+
+// SetAccessID 设置短信服务的accessid,通过官方网站申请或通过管理员获取
+func (c *Client) SetAccessID(accessid string) {
+	c.AccessID = accessid
+}
+
+// SetAccessKey 设置短信服务的accesskey,通过官方网站申请或通过管理员获取
+func (c *Client) SetAccessKey(accesskey string) {
+	c.AccessKey = accesskey
+}
+
+// SetSocketTimeout 设置短信服务的Socket超时,单位为秒,可以为int或float。默认值为30
+func (c *Client) SetSocketTimeout(sockettimeout int) {
+	if sockettimeout == 0 {
+		sockettimeout = 30
+	}
+	c.SocketTimeout = sockettimeout
+}
+
+func (c *Client) calcStringToSign() string {
+	c.param = make(map[string]string)
+	c.param["SignatureMethod"] = c.Param.GetSignatureMethod()
+	c.param["SignatureNonce"] = uuid.New()
+	// sync c.Param.SignatureNonce
+	c.Param.SetSignatureNonce(c.param["SignatureNonce"])
+	c.param["AccessKeyId"] = c.Param.GetAccessKeyID()
+	c.param["SignatureVersion"] = c.Param.GetSignatureVersion()
+	c.param["Timestamp"] = time.Now().UTC().Format(time.RFC3339)
+	// sync c.Param.Timestamp
+	c.Param.SetTimestamp(c.param["Timestamp"])
+	c.param["Format"] = c.Param.GetFormat()
+
+	c.param["Action"] = c.Param.GetAction()
+	c.param["Version"] = c.Param.GetVersion()
+	c.param["RegionId"] = c.Param.GetRegionID()
+	c.param["RecNum"] = c.Param.GetRecNum()
+	c.param["SignName"] = c.Param.GetSignName()
+	c.param["ParamString"] = c.Param.GetParamString()
+	c.param["TemplateCode"] = c.Param.GetTemplateCode()
+
+	strslice := make([]string, len(c.param))
+	i := 0
+	for k, v := range c.param {
+		data := url.Values{}
+		data.Add(k, v)
+		strslice[i] = data.Encode()
+		strslice[i] = percentEncodeBefore(strslice[i])
+		i++
+	}
+	sort.Strings(strslice)
+	return "POST&" + percentEncode("/") + "&" + percentEncode(strings.Join(strslice, "&"))
+}
+
+// SendOne 发送给一个手机号
+func (c *Client) SendOne(RecNum, signname, templatecode, ParamString string) (e *ErrorMessage, err error) {
+	var body []byte
+
+	e = &ErrorMessage{}
+	c.Param.SetSignName(signname)
+	c.Param.SetTemplateCode(templatecode)
+	c.Param.SetParamString(ParamString)
+	c.Param.SetRecNum(RecNum)
+	signature := signatureMethod(c.AccessKey, c.calcStringToSign())
+
+	req := urllib.Post(c.EndPoint)
+	if HTTPDebugEnable {
+		req.Debug(true)
+	}
+	for k, v := range c.param {
+		req.Param(k, v)
+	}
+	req.Param("Signature", signature)
+	resp, err := req.Response()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	if resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, errGzip := gzip.NewReader(resp.Body)
+		if errGzip != nil {
+			return nil, errGzip
+		}
+		body, err = ioutil.ReadAll(reader)
+	} else {
+		body, err = ioutil.ReadAll(resp.Body)
+	}
+	if err != nil {
+		return nil, err
+	}
+	err = json.Unmarshal(body, e)
+	e.SetHTTPCode(resp.StatusCode)
+	if HTTPDebugEnable {
+		fmt.Println("C-->S:", req.DumpRequestString())
+		fmt.Println("S-->C:", e.Error())
+	}
+	if err != nil {
+		return e, err
+	}
+	if e.GetCode() != "" {
+		return e, errors.New(e.GetCode())
+	}
+	return e, nil
+}
+
+// SendMulti 发送给多个手机号, 最多100个
+func (c *Client) SendMulti(RecNum []string, signname, templatecode, ParamString string) (e *ErrorMessage, err error) {
+	var body []byte
+
+	e = &ErrorMessage{}
+	if len(RecNum) > 100 {
+		return nil, errors.New("number of RecNum should be less than 100")
+	}
+	c.Param.SetSignName(signname)
+	c.Param.SetTemplateCode(templatecode)
+	c.Param.SetParamString(ParamString)
+	c.Param.SetRecNum(strings.Join(RecNum, ","))
+	signature := signatureMethod(c.AccessKey, c.calcStringToSign())
+
+	req := urllib.Post(c.EndPoint)
+	for k, v := range c.param {
+		req.Param(k, v)
+	}
+	req.Param("Signature", signature)
+	resp, err := req.Response()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	if resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, errGzip := gzip.NewReader(resp.Body)
+		if errGzip != nil {
+			return nil, errGzip
+		}
+		body, err = ioutil.ReadAll(reader)
+	} else {
+		body, err = ioutil.ReadAll(resp.Body)
+	}
+	if err != nil {
+		return nil, err
+	}
+	err = json.Unmarshal(body, e)
+	e.SetHTTPCode(resp.StatusCode)
+	if HTTPDebugEnable {
+		fmt.Println("C-->S:", req.DumpRequestString())
+		fmt.Println("S-->C:", e.Error())
+	}
+	if err != nil {
+		return e, err
+	}
+	if e.GetCode() != "" {
+		return e, errors.New(e.GetCode())
+	}
+	return e, nil
+}
+
+// New 创建一个短信发送客户端
+func New(accessid, accesskey string) (c *Client) {
+	c = new(Client)
+	if c.EndPoint == "" {
+		c.EndPoint = "https://sms.aliyuncs.com/"
+	}
+	c.AccessID = accessid
+	c.AccessKey = accesskey
+	c.Param.SetSignatureMethod("HMAC-SHA1")
+	c.Param.SetSignatureNonce(uuid.New())
+	c.Param.SetAccessKeyID(accessid)
+	c.Param.SetSignatureVersion("1.0")
+	c.Param.SetTimestamp(time.Now().UTC().Format(time.RFC3339))
+	c.Param.SetFormat("JSON")
+
+	c.Param.SetAction("SingleSendSms")
+	c.Param.SetVersion("2016-09-27")
+	c.Param.SetRegionID("cn-hangzhou")
+	c.Param.SetRecNum("your_RecNum")
+	c.Param.SetSignName("your_signname")
+	c.Param.SetParamString("your_ParamString")
+	c.Param.SetTemplateCode("your_templatecode")
+
+	if urllib.GetDefaultSetting().Transport == nil {
+		// set default setting for urllib
+		trans := &http.Transport{
+			MaxIdleConnsPerHost: 500,
+			Dial: (&net.Dialer{
+				Timeout: time.Duration(15) * time.Second,
+			}).Dial,
+		}
+
+		urlSetting := urllib.HttpSettings{
+			ShowDebug:        false,            // ShowDebug
+			UserAgent:        "GiterLab",       // UserAgent
+			ConnectTimeout:   15 * time.Second, // ConnectTimeout
+			ReadWriteTimeout: 30 * time.Second, // ReadWriteTimeout
+			TlsClientConfig:  nil,              // TlsClientConfig
+			Proxy:            nil,              // Proxy
+			Transport:        trans,            // Transport
+			EnableCookie:     false,            // EnableCookie
+			Gzip:             true,             // Gzip
+			DumpBody:         true,             // DumpBody
+		}
+		if c.SocketTimeout != 0 {
+			urlSetting.ConnectTimeout = time.Duration(c.SocketTimeout) * time.Second
+			urlSetting.ReadWriteTimeout = time.Duration(c.SocketTimeout) * time.Second
+		}
+		if HTTPDebugEnable {
+			urlSetting.ShowDebug = true
+		} else {
+			urlSetting.ShowDebug = false
+		}
+		urllib.SetDefaultSetting(urlSetting)
+	}
+
+	return c
+}

Разлика између датотеке није приказан због своје велике величине
+ 39 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/sms/sms_test.go


+ 47 - 0
go/gopath/src/github.com/GiterLab/aliyun-sms-go-sdk/wercker.yml

@@ -0,0 +1,47 @@
+# This references the default golang container from
+# the Docker Hub: https://registry.hub.docker.com/u/library/golang/
+# If you want Google's container you would reference google/golang
+# Read more about containers on our dev center
+# http://devcenter.wercker.com/docs/containers/index.html
+box: golang
+# This is the build pipeline. Pipelines are the core of wercker
+# Read more about pipelines on our dev center
+# http://devcenter.wercker.com/docs/pipelines/index.html
+
+# You can also use services such as databases. Read more on our dev center:
+# http://devcenter.wercker.com/docs/services/index.html
+# services:
+    # - postgres
+    # http://devcenter.wercker.com/docs/services/postgresql.html
+
+    # - mongo
+    # http://devcenter.wercker.com/docs/services/mongodb.html
+build:
+  # The steps that will be executed on build
+  # Steps make up the actions in your pipeline
+  # Read more about steps on our dev center:
+  # http://devcenter.wercker.com/docs/steps/index.html
+  steps:
+    # Sets the go workspace and places you package
+    # at the right place in the workspace tree
+    - setup-go-workspace
+
+    # Gets the dependencies
+    - script:
+        name: go get
+        code: |
+          go get
+
+    # Build the project
+    - script:
+        name: go build
+        code: |
+          go build example/sample.go
+          go build example/sample-dysms.go          
+
+    # Test the project
+    - script:
+        name: go test
+        code: |
+          go test ./sms
+          go test ./dysms

+ 24 - 0
go/gopath/src/github.com/GiterLab/urllib/.gitignore

@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 202 - 0
go/gopath/src/github.com/GiterLab/urllib/LICENSE

@@ -0,0 +1,202 @@
+                                 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 {yyyy} {name of copyright owner}
+
+   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.
+

+ 118 - 0
go/gopath/src/github.com/GiterLab/urllib/README.md

@@ -0,0 +1,118 @@
+# urllib
+urllib is an libs help you to curl remote url using golang.
+
+# How to use?
+
+## Example
+
+	package main
+	
+	import (
+		"fmt"
+	
+		"github.com/GiterLab/urllib"
+	)
+	
+	func main() {
+		req := urllib.Get("http://tobyzxj.me/")
+		req.Debug(true)
+		str, err := req.String()
+		if err != nil {
+			fmt.Println(err)
+		}
+		fmt.Println(req.DumpRequestString()) // debug out
+		fmt.Println(str)
+	}
+
+## GET
+you can use Get to crawl data.
+
+	import "github.com/GiterLab/urllib"
+	
+	str, err := urllib.Get("http://tobyzxj.me/").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## POST
+POST data to remote url
+
+	req := urllib.Post("http://tobyzxj.me/")
+	req.Param("username","tobyzxj")
+	req.Param("password","123456")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+## Set timeout
+
+The default timeout is `60` seconds, function prototype:
+
+	SetTimeout(connectTimeout, readWriteTimeout time.Duration)
+
+Exmaple:
+
+	// GET
+	urllib.Get("http://tobyzxj.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+	
+	// POST
+	urllib.Post("http://tobyzxj.me/").SetTimeout(100 * time.Second, 30 * time.Second)
+
+
+## Debug
+
+If you want to debug the request info, set the debug on
+
+	urllib.Get("http://tobyzxj.me/").Debug(true)
+	
+## Set HTTP Basic Auth
+
+	str, err := Get("http://tobyzxj.me/").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+	
+## Set HTTPS
+
+If request url is https, You can set the client support TSL:
+
+	urllib.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
+	
+More info about the `tls.Config` please visit http://golang.org/pkg/crypto/tls/#Config	
+
+## Set HTTP Version
+
+some servers need to specify the protocol version of HTTP
+
+	urllib.Get("http://tobyzxj.me/").SetProtocolVersion("HTTP/1.1")
+	
+## Set Cookie
+
+some http request need setcookie. So set it like this:
+
+	cookie := &http.Cookie{}
+	cookie.Name = "username"
+	cookie.Value  = "tobyzxj"
+	urllib.Get("http://tobyzxj.me/").SetCookie(cookie)
+
+## Upload file
+
+urllib support mutil file upload, use `req.PostFile()`
+
+	req := urllib.Post("http://tobyzxj.me/")
+	req.Param("username","tobyzxj")
+	req.PostFile("uploadfile1", "urllib.pdf")
+	str, err := req.String()
+	if err != nil {
+        	// error
+	}
+	fmt.Println(str)
+
+
+See godoc for further documentation and examples.
+
+* [godoc.org/github.com/GiterLab/urllib](https://godoc.org/github.com/GiterLab/urllib)

+ 547 - 0
go/gopath/src/github.com/GiterLab/urllib/urllib.go

@@ -0,0 +1,547 @@
+// Copyright 2014 The GiterLab Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Package urllib is a httplib for golang
+package urllib
+
+import (
+	"bytes"
+	"compress/gzip"
+	"crypto/tls"
+	"encoding/json"
+	"encoding/xml"
+	"io"
+	"io/ioutil"
+	"log"
+	"mime/multipart"
+	"net"
+	"net/http"
+	"net/http/cookiejar"
+	"net/http/httputil"
+	"net/url"
+	"os"
+	"strings"
+	"sync"
+	"time"
+)
+
+var defaultSetting = HttpSettings{
+	ShowDebug:        false,
+	UserAgent:        "GiterLab",
+	ConnectTimeout:   60 * time.Second,
+	ReadWriteTimeout: 60 * time.Second,
+	TlsClientConfig:  nil,
+	Proxy:            nil,
+	Transport:        nil,
+	EnableCookie:     false,
+	Gzip:             true,
+	DumpBody:         true,
+}
+var defaultCookieJar http.CookieJar
+var settingMutex sync.Mutex
+
+// createDefaultCookie creates a global cookiejar to store cookies.
+func createDefaultCookie() {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultCookieJar, _ = cookiejar.New(nil)
+}
+
+// Overwrite default settings
+func SetDefaultSetting(setting HttpSettings) {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+	defaultSetting = setting
+	if defaultSetting.ConnectTimeout == 0 {
+		defaultSetting.ConnectTimeout = 60 * time.Second
+	}
+	if defaultSetting.ReadWriteTimeout == 0 {
+		defaultSetting.ReadWriteTimeout = 60 * time.Second
+	}
+}
+
+// Get current default settings
+func GetDefaultSetting() *HttpSettings {
+	settingMutex.Lock()
+	defer settingMutex.Unlock()
+
+	return &defaultSetting
+}
+
+// return *HttpRequest with specific method
+func newRequest(rawurl, method string) *HttpRequest {
+	var resp http.Response
+	u, err := url.Parse(rawurl)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	req := http.Request{
+		URL:        u,
+		Method:     method,
+		Header:     make(http.Header),
+		Proto:      "HTTP/1.1",
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+	}
+
+	return &HttpRequest{
+		url:     rawurl,
+		req:     &req,
+		params:  map[string]string{},
+		files:   map[string]string{},
+		setting: defaultSetting,
+		resp:    &resp,
+		body:    nil,
+	}
+}
+
+// Get returns *HttpRequest with GET method.
+func Get(url string) *HttpRequest {
+	return newRequest(url, "GET")
+}
+
+// Post returns *HttpRequest with POST method.
+func Post(url string) *HttpRequest {
+	return newRequest(url, "POST")
+}
+
+// Put returns *HttpRequest with PUT method.
+func Put(url string) *HttpRequest {
+	return newRequest(url, "PUT")
+}
+
+// Delete returns *HttpRequest DELETE method.
+func Delete(url string) *HttpRequest {
+	return newRequest(url, "DELETE")
+}
+
+// Head returns *HttpRequest with HEAD method.
+func Head(url string) *HttpRequest {
+	return newRequest(url, "HEAD")
+}
+
+// Head returns *HttpRequest with PATCH method.
+func Patch(url string) *HttpRequest {
+	return newRequest(url, "PATCH")
+}
+
+// HttpSettings
+type HttpSettings struct {
+	ShowDebug        bool
+	UserAgent        string
+	ConnectTimeout   time.Duration
+	ReadWriteTimeout time.Duration
+	TlsClientConfig  *tls.Config
+	Proxy            func(*http.Request) (*url.URL, error)
+	Transport        http.RoundTripper
+	EnableCookie     bool
+	Gzip             bool
+	DumpBody         bool
+}
+
+// HttpRequest provides more useful methods for requesting one url than http.Request.
+type HttpRequest struct {
+	url     string
+	req     *http.Request
+	params  map[string]string
+	files   map[string]string
+	setting HttpSettings
+	resp    *http.Response
+	body    []byte
+	dump    []byte
+}
+
+// Change request settings
+func (b *HttpRequest) Setting(setting HttpSettings) *HttpRequest {
+	b.setting = setting
+	return b
+}
+
+// SetBasicAuth sets the request's Authorization header to use HTTP Basic Authentication with the provided username and password.
+func (b *HttpRequest) SetBasicAuth(username, password string) *HttpRequest {
+	b.req.SetBasicAuth(username, password)
+	return b
+}
+
+// SetEnableCookie sets enable/disable cookiejar
+func (b *HttpRequest) SetEnableCookie(enable bool) *HttpRequest {
+	b.setting.EnableCookie = enable
+	return b
+}
+
+// SetUserAgent sets User-Agent header field
+func (b *HttpRequest) SetUserAgent(useragent string) *HttpRequest {
+	b.setting.UserAgent = useragent
+	return b
+}
+
+// Debug sets show debug or not when executing request.
+func (b *HttpRequest) Debug(isdebug bool) *HttpRequest {
+	b.setting.ShowDebug = isdebug
+	return b
+}
+
+// Dump Body.
+func (b *HttpRequest) DumpBody(isdump bool) *HttpRequest {
+	b.setting.DumpBody = isdump
+	return b
+}
+
+// return the DumpRequest
+func (b *HttpRequest) DumpRequest() []byte {
+	return b.dump
+}
+
+// return the DumpRequest string
+func (b *HttpRequest) DumpRequestString() string {
+	return string(b.DumpRequest())
+}
+
+// SetTimeout sets connect time out and read-write time out for Request.
+func (b *HttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *HttpRequest {
+	b.setting.ConnectTimeout = connectTimeout
+	b.setting.ReadWriteTimeout = readWriteTimeout
+	return b
+}
+
+// SetTLSClientConfig sets tls connection configurations if visiting https url.
+func (b *HttpRequest) SetTLSClientConfig(config *tls.Config) *HttpRequest {
+	b.setting.TlsClientConfig = config
+	return b
+}
+
+// Header add header item string in request.
+func (b *HttpRequest) Header(key, value string) *HttpRequest {
+	b.req.Header.Set(key, value)
+	return b
+}
+
+// Set HOST
+func (b *HttpRequest) SetHost(host string) *HttpRequest {
+	b.req.Host = host
+	return b
+}
+
+// Set the protocol version for incoming requests.
+// Client requests always use HTTP/1.1.
+func (b *HttpRequest) SetProtocolVersion(vers string) *HttpRequest {
+	if len(vers) == 0 {
+		vers = "HTTP/1.1"
+	}
+
+	major, minor, ok := http.ParseHTTPVersion(vers)
+	if ok {
+		b.req.Proto = vers
+		b.req.ProtoMajor = major
+		b.req.ProtoMinor = minor
+	}
+
+	return b
+}
+
+// SetCookie add cookie into request.
+func (b *HttpRequest) SetCookie(cookie *http.Cookie) *HttpRequest {
+	b.req.Header.Add("Cookie", cookie.String())
+	return b
+}
+
+// Get default CookieJar
+func GetDefaultCookieJar() http.CookieJar {
+	return defaultCookieJar
+}
+
+// Set transport to
+func (b *HttpRequest) SetTransport(transport http.RoundTripper) *HttpRequest {
+	b.setting.Transport = transport
+	return b
+}
+
+// Set http proxy
+// example:
+//
+//	func(req *http.Request) (*url.URL, error) {
+// 		u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
+// 		return u, nil
+// 	}
+func (b *HttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *HttpRequest {
+	b.setting.Proxy = proxy
+	return b
+}
+
+// Param adds query param in to request.
+// params build query string as ?key1=value1&key2=value2...
+func (b *HttpRequest) Param(key, value string) *HttpRequest {
+	b.params[key] = value
+	return b
+}
+
+func (b *HttpRequest) PostFile(formname, filename string) *HttpRequest {
+	b.files[formname] = filename
+	return b
+}
+
+// Body adds request raw body.
+// it supports string and []byte.
+func (b *HttpRequest) Body(data interface{}) *HttpRequest {
+	switch t := data.(type) {
+	case string:
+		bf := bytes.NewBufferString(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	case []byte:
+		bf := bytes.NewBuffer(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	}
+	return b
+}
+
+// JsonBody adds request raw body encoding by JSON.
+func (b *HttpRequest) JsonBody(obj interface{}) (*HttpRequest, error) {
+	if b.req.Body == nil && obj != nil {
+		buf := bytes.NewBuffer(nil)
+		enc := json.NewEncoder(buf)
+		if err := enc.Encode(obj); err != nil {
+			return b, err
+		}
+		b.req.Body = ioutil.NopCloser(buf)
+		b.req.ContentLength = int64(buf.Len())
+		b.req.Header.Set("Content-Type", "application/json")
+	}
+	return b, nil
+}
+
+func (b *HttpRequest) buildUrl(paramBody string) {
+	// build GET url with query string
+	if b.req.Method == "GET" && len(paramBody) > 0 {
+		if strings.Index(b.url, "?") != -1 {
+			b.url += "&" + paramBody
+		} else {
+			b.url = b.url + "?" + paramBody
+		}
+		return
+	}
+
+	// build POST/PUT/PATCH url and body
+	if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil {
+		// with files
+		if len(b.files) > 0 {
+			pr, pw := io.Pipe()
+			bodyWriter := multipart.NewWriter(pw)
+			go func() {
+				for formname, filename := range b.files {
+					fileWriter, err := bodyWriter.CreateFormFile(formname, filename)
+					if err != nil {
+						log.Fatal(err)
+					}
+					fh, err := os.Open(filename)
+					if err != nil {
+						log.Fatal(err)
+					}
+					//iocopy
+					_, err = io.Copy(fileWriter, fh)
+					fh.Close()
+					if err != nil {
+						log.Fatal(err)
+					}
+				}
+				for k, v := range b.params {
+					bodyWriter.WriteField(k, v)
+				}
+				bodyWriter.Close()
+				pw.Close()
+			}()
+			b.Header("Content-Type", bodyWriter.FormDataContentType())
+			b.req.Body = ioutil.NopCloser(pr)
+			return
+		}
+
+		// with params
+		if len(paramBody) > 0 {
+			b.Header("Content-Type", "application/x-www-form-urlencoded")
+			b.Body(paramBody)
+		}
+	}
+}
+
+func (b *HttpRequest) getResponse() (*http.Response, error) {
+	if b.resp.StatusCode != 0 {
+		return b.resp, nil
+	}
+	resp, err := b.SendOut()
+	if err != nil {
+		return nil, err
+	}
+	b.resp = resp
+	return resp, nil
+}
+
+func (b *HttpRequest) SendOut() (*http.Response, error) {
+	var paramBody string
+	if len(b.params) > 0 {
+		var buf bytes.Buffer
+		for k, v := range b.params {
+			buf.WriteString(url.QueryEscape(k))
+			buf.WriteByte('=')
+			buf.WriteString(url.QueryEscape(v))
+			buf.WriteByte('&')
+		}
+		paramBody = buf.String()
+		paramBody = paramBody[0 : len(paramBody)-1]
+	}
+
+	b.buildUrl(paramBody)
+	url, err := url.Parse(b.url)
+	if err != nil {
+		return nil, err
+	}
+
+	b.req.URL = url
+
+	trans := b.setting.Transport
+
+	if trans == nil {
+		// create default transport
+		trans = &http.Transport{
+			TLSClientConfig: b.setting.TlsClientConfig,
+			Proxy:           b.setting.Proxy,
+			Dial:            TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout),
+		}
+	} else {
+		// if b.transport is *http.Transport then set the settings.
+		if t, ok := trans.(*http.Transport); ok {
+			if t.TLSClientConfig == nil {
+				t.TLSClientConfig = b.setting.TlsClientConfig
+			}
+			if t.Proxy == nil {
+				t.Proxy = b.setting.Proxy
+			}
+			if t.Dial == nil {
+				t.Dial = TimeoutDialer(b.setting.ConnectTimeout, b.setting.ReadWriteTimeout)
+			}
+		}
+	}
+
+	var jar http.CookieJar = nil
+	if b.setting.EnableCookie {
+		if defaultCookieJar == nil {
+			createDefaultCookie()
+		}
+		jar = defaultCookieJar
+	}
+
+	client := &http.Client{
+		Transport: trans,
+		Jar:       jar,
+	}
+
+	if b.setting.UserAgent != "" && b.req.Header.Get("User-Agent") == "" {
+		b.req.Header.Set("User-Agent", b.setting.UserAgent)
+	}
+
+	if b.setting.ShowDebug {
+		dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
+		if err != nil {
+			log.Println(err.Error())
+		}
+		b.dump = dump
+	}
+	return client.Do(b.req)
+}
+
+// String returns the body string in response.
+// it calls Response inner.
+func (b *HttpRequest) String() (string, error) {
+	data, err := b.Bytes()
+	if err != nil {
+		return "", err
+	}
+
+	return string(data), nil
+}
+
+// Bytes returns the body []byte in response.
+// it calls Response inner.
+func (b *HttpRequest) Bytes() ([]byte, error) {
+	if b.body != nil {
+		return b.body, nil
+	}
+	resp, err := b.getResponse()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" {
+		reader, err := gzip.NewReader(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+		b.body, err = ioutil.ReadAll(reader)
+	} else {
+		b.body, err = ioutil.ReadAll(resp.Body)
+	}
+	return b.body, err
+}
+
+// ToFile saves the body data in response to one file.
+// it calls Response inner.
+func (b *HttpRequest) ToFile(filename string) error {
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	resp, err := b.getResponse()
+	if err != nil {
+		return err
+	}
+	if resp.Body == nil {
+		return nil
+	}
+	defer resp.Body.Close()
+	_, err = io.Copy(f, resp.Body)
+	return err
+}
+
+// ToJson returns the map that marshals from the body bytes as json in response .
+// it calls Response inner.
+func (b *HttpRequest) ToJson(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(data, v)
+}
+
+// ToXml returns the map that marshals from the body bytes as xml in response .
+// it calls Response inner.
+func (b *HttpRequest) ToXml(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	return xml.Unmarshal(data, v)
+}
+
+// Response executes request client gets response mannually.
+func (b *HttpRequest) Response() (*http.Response, error) {
+	return b.getResponse()
+}
+
+// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
+func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
+	return func(netw, addr string) (net.Conn, error) {
+		conn, err := net.DialTimeout(netw, addr, cTimeout)
+		if err != nil {
+			return nil, err
+		}
+		err = conn.SetDeadline(time.Now().Add(rwTimeout))
+		return conn, err
+	}
+}

+ 206 - 0
go/gopath/src/github.com/GiterLab/urllib/urllib_test.go

@@ -0,0 +1,206 @@
+// Copyright 2014 The GiterLab Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// urllib for golang
+package urllib
+
+import (
+	"io/ioutil"
+	"os"
+	"strings"
+	"testing"
+)
+
+func TestResponse(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+}
+
+func TestGet(t *testing.T) {
+	req := Get("http://httpbin.org/get")
+	b, err := req.Bytes()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(b)
+
+	s, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(s)
+
+	if string(b) != s {
+		t.Fatal("request data not match")
+	}
+}
+
+func TestSimplePost(t *testing.T) {
+	v := "smallfish"
+	req := Post("http://httpbin.org/post")
+	req.Param("username", v)
+
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in post")
+	}
+}
+
+func TestPostFile(t *testing.T) {
+	v := "smallfish"
+	req := Post("http://httpbin.org/post")
+	req.Param("username", v)
+	req.PostFile("uploadfile", "urllib_test.go")
+
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in post")
+	}
+}
+
+func TestSimplePut(t *testing.T) {
+	str, err := Put("http://httpbin.org/put").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestSimpleDelete(t *testing.T) {
+	str, err := Delete("http://httpbin.org/delete").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}
+
+func TestWithCookie(t *testing.T) {
+	v := "smallfish"
+	str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	str, err = Get("http://httpbin.org/cookies").SetEnableCookie(true).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in cookie")
+	}
+}
+
+func TestWithBasicAuth(t *testing.T) {
+	str, err := Get("http://httpbin.org/basic-auth/user/passwd").SetBasicAuth("user", "passwd").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+	n := strings.Index(str, "authenticated")
+	if n == -1 {
+		t.Fatal("authenticated not found in response")
+	}
+}
+
+func TestWithUserAgent(t *testing.T) {
+	v := "GiterLab"
+	str, err := Get("http://httpbin.org/headers").SetUserAgent(v).String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestWithSetting(t *testing.T) {
+	v := "GiterLab"
+	var setting HttpSettings
+	setting.EnableCookie = true
+	setting.UserAgent = v
+	setting.Transport = nil
+	SetDefaultSetting(setting)
+
+	str, err := Get("http://httpbin.org/get").String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+
+	n := strings.Index(str, v)
+	if n == -1 {
+		t.Fatal(v + " not found in user-agent")
+	}
+}
+
+func TestToJson(t *testing.T) {
+	req := Get("http://httpbin.org/ip")
+	resp, err := req.Response()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(resp)
+
+	// httpbin will return http remote addr
+	type Ip struct {
+		Origin string `json:"origin"`
+	}
+	var ip Ip
+	err = req.ToJson(&ip)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(ip.Origin)
+
+	if n := strings.Count(ip.Origin, "."); n != 3 {
+		t.Fatal("response is not valid ip")
+	}
+}
+
+func TestToFile(t *testing.T) {
+	f := "GiterLab_testfile"
+	req := Get("http://httpbin.org/ip")
+	err := req.ToFile(f)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(f)
+	b, err := ioutil.ReadFile(f)
+	if n := strings.Index(string(b), "origin"); n == -1 {
+		t.Fatal(err)
+	}
+}
+
+func TestHeader(t *testing.T) {
+	req := Get("http://httpbin.org/headers")
+	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")
+	str, err := req.String()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(str)
+}