mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-28 17:50:56 +01:00
更新依赖安装逻辑
This commit is contained in:
201
backend/vendor/github.com/imroc/req/LICENSE
generated
vendored
Normal file
201
backend/vendor/github.com/imroc/req/LICENSE
generated
vendored
Normal file
@@ -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.
|
||||
302
backend/vendor/github.com/imroc/req/README.md
generated
vendored
Normal file
302
backend/vendor/github.com/imroc/req/README.md
generated
vendored
Normal file
@@ -0,0 +1,302 @@
|
||||
# req
|
||||
[](https://godoc.org/github.com/imroc/req)
|
||||
|
||||
A golang http request library for humans
|
||||
|
||||
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- Light weight
|
||||
- Simple
|
||||
- Easy play with JSON and XML
|
||||
- Easy for debug and logging
|
||||
- Easy file uploads and downloads
|
||||
- Easy manage cookie
|
||||
- Easy set up proxy
|
||||
- Easy set timeout
|
||||
- Easy customize http client
|
||||
|
||||
|
||||
Document
|
||||
========
|
||||
[中文](doc/README_cn.md)
|
||||
|
||||
|
||||
Install
|
||||
=======
|
||||
``` sh
|
||||
go get github.com/imroc/req
|
||||
```
|
||||
|
||||
Overview
|
||||
=======
|
||||
`req` implements a friendly API over Go's existing `net/http` library.
|
||||
|
||||
`Req` and `Resp` are two most important struct, you can think of `Req` as a client that initiate HTTP requests, `Resp` as a information container for the request and response. They all provide simple and convenient APIs that allows you to do a lot of things.
|
||||
``` go
|
||||
func (r *Req) Post(url string, v ...interface{}) (*Resp, error)
|
||||
```
|
||||
|
||||
In most cases, only url is required, others are optional, like headers, params, files or body etc.
|
||||
|
||||
There is a default `Req` object, all of its' public methods are wrapped by the `req` package, so you can also think of `req` package as a `Req` object
|
||||
``` go
|
||||
// use Req object to initiate requests.
|
||||
r := req.New()
|
||||
r.Get(url)
|
||||
|
||||
// use req package to initiate request.
|
||||
req.Get(url)
|
||||
```
|
||||
You can use `req.New()` to create lots of `*Req` as client with independent configuration
|
||||
|
||||
Examples
|
||||
=======
|
||||
[Basic](#Basic)
|
||||
[Set Header](#Set-Header)
|
||||
[Set Param](#Set-Param)
|
||||
[Set Body](#Set-Body)
|
||||
[Debug](#Debug)
|
||||
[Output Format](#Format)
|
||||
[ToJSON & ToXML](#ToJSON-ToXML)
|
||||
[Get *http.Response](#Response)
|
||||
[Upload](#Upload)
|
||||
[Download](#Download)
|
||||
[Cookie](#Cookie)
|
||||
[Set Timeout](#Set-Timeout)
|
||||
[Set Proxy](#Set-Proxy)
|
||||
[Customize Client](#Customize-Client)
|
||||
|
||||
## <a name="Basic">Basic</a>
|
||||
``` go
|
||||
header := req.Header{
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Basic YWRtaW46YWRtaW4=",
|
||||
}
|
||||
param := req.Param{
|
||||
"name": "imroc",
|
||||
"cmd": "add",
|
||||
}
|
||||
// only url is required, others are optional.
|
||||
r, err = req.Post("http://foo.bar/api", header, param)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
r.ToJSON(&foo) // response => struct/map
|
||||
log.Printf("%+v", r) // print info (try it, you may surprise)
|
||||
```
|
||||
|
||||
## <a name="Set-Header">Set Header</a>
|
||||
Use `req.Header` (it is actually a `map[string]string`)
|
||||
``` go
|
||||
authHeader := req.Header{
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Basic YWRtaW46YWRtaW4=",
|
||||
}
|
||||
req.Get("https://www.baidu.com", authHeader, req.Header{"User-Agent": "V1.1"})
|
||||
```
|
||||
use `http.Header`
|
||||
``` go
|
||||
header := make(http.Header)
|
||||
header.Set("Accept", "application/json")
|
||||
req.Get("https://www.baidu.com", header)
|
||||
```
|
||||
|
||||
## <a name="Set-Param">Set Param</a>
|
||||
Use `req.Param` (it is actually a `map[string]interface{}`)
|
||||
``` go
|
||||
param := req.Param{
|
||||
"id": "imroc",
|
||||
"pwd": "roc",
|
||||
}
|
||||
req.Get("http://foo.bar/api", param) // http://foo.bar/api?id=imroc&pwd=roc
|
||||
req.Post(url, param) // body => id=imroc&pwd=roc
|
||||
```
|
||||
use `req.QueryParam` force to append params to the url (it is also actually a `map[string]interface{}`)
|
||||
``` go
|
||||
req.Post("http://foo.bar/api", req.Param{"name": "roc", "age": "22"}, req.QueryParam{"access_token": "fedledGF9Hg9ehTU"})
|
||||
/*
|
||||
POST /api?access_token=fedledGF9Hg9ehTU HTTP/1.1
|
||||
Host: foo.bar
|
||||
User-Agent: Go-http-client/1.1
|
||||
Content-Length: 15
|
||||
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
|
||||
Accept-Encoding: gzip
|
||||
|
||||
age=22&name=roc
|
||||
*/
|
||||
```
|
||||
|
||||
## <a name="Set-Body">Set Body</a>
|
||||
Put `string`, `[]byte` and `io.Reader` as body directly.
|
||||
``` go
|
||||
req.Post(url, "id=roc&cmd=query")
|
||||
```
|
||||
Put object as xml or json body (add `Content-Type` header automatically)
|
||||
``` go
|
||||
req.Post(url, req.BodyJSON(&foo))
|
||||
req.Post(url, req.BodyXML(&bar))
|
||||
```
|
||||
|
||||
## <a name="Debug">Debug</a>
|
||||
Set global variable `req.Debug` to true, it will print detail infomation for every request.
|
||||
``` go
|
||||
req.Debug = true
|
||||
req.Post("http://localhost/test" "hi")
|
||||
```
|
||||

|
||||
|
||||
## <a name="Format">Output Format</a>
|
||||
You can use different kind of output format to log the request and response infomation in your log file in defferent scenarios. For example, use `%+v` output format in the development phase, it allows you to observe the details. Use `%v` or `%-v` output format in production phase, just log the information necessarily.
|
||||
|
||||
### `%+v` or `%+s`
|
||||
Output in detail
|
||||
``` go
|
||||
r, _ := req.Post(url, header, param)
|
||||
log.Printf("%+v", r) // output the same format as Debug is enabled
|
||||
```
|
||||
|
||||
### `%v` or `%s`
|
||||
Output in simple way (default format)
|
||||
``` go
|
||||
r, _ := req.Get(url, param)
|
||||
log.Printf("%v\n", r) // GET http://foo.bar/api?name=roc&cmd=add {"code":"0","msg":"success"}
|
||||
log.Prinln(r) // smae as above
|
||||
```
|
||||
|
||||
### `%-v` or `%-s`
|
||||
Output in simple way and keep all in one line (request body or response body may have multiple lines, this format will replace `"\r"` or `"\n"` with `" "`, it's useful when doing some search in your log file)
|
||||
|
||||
### Flag
|
||||
You can call `SetFlags` to control the output content, decide which pieces can be output.
|
||||
``` go
|
||||
const (
|
||||
LreqHead = 1 << iota // output request head (request line and request header)
|
||||
LreqBody // output request body
|
||||
LrespHead // output response head (response line and response header)
|
||||
LrespBody // output response body
|
||||
Lcost // output time costed by the request
|
||||
LstdFlags = LreqHead | LreqBody | LrespHead | LrespBody
|
||||
)
|
||||
```
|
||||
``` go
|
||||
req.SetFlags(req.LreqHead | req.LreqBody | req.LrespHead)
|
||||
```
|
||||
|
||||
### Monitoring time consuming
|
||||
``` go
|
||||
req.SetFlags(req.LstdFlags | req.Lcost) // output format add time costed by request
|
||||
r,_ := req.Get(url)
|
||||
log.Println(r) // http://foo.bar/api 3.260802ms {"code":0 "msg":"success"}
|
||||
if r.Cost() > 3 * time.Second { // check cost
|
||||
log.Println("WARN: slow request:", r)
|
||||
}
|
||||
```
|
||||
|
||||
## <a name="ToJSON-ToXML">ToJSON & ToXML</a>
|
||||
``` go
|
||||
r, _ := req.Get(url)
|
||||
r.ToJSON(&foo)
|
||||
r, _ = req.Post(url, req.BodyXML(&bar))
|
||||
r.ToXML(&baz)
|
||||
```
|
||||
|
||||
## <a name="Response">Get *http.Response</a>
|
||||
```go
|
||||
// func (r *Req) Response() *http.Response
|
||||
r, _ := req.Get(url)
|
||||
resp := r.Response()
|
||||
fmt.Println(resp.StatusCode)
|
||||
```
|
||||
|
||||
## <a name="Upload">Upload</a>
|
||||
Use `req.File` to match files
|
||||
``` go
|
||||
req.Post(url, req.File("imroc.png"), req.File("/Users/roc/Pictures/*.png"))
|
||||
```
|
||||
Use `req.FileUpload` to fully control
|
||||
``` go
|
||||
file, _ := os.Open("imroc.png")
|
||||
req.Post(url, req.FileUpload{
|
||||
File: file,
|
||||
FieldName: "file", // FieldName is form field name
|
||||
FileName: "avatar.png", //Filename is the name of the file that you wish to upload. We use this to guess the mimetype as well as pass it onto the server
|
||||
})
|
||||
```
|
||||
Use `req.UploadProgress` to listen upload progress
|
||||
```go
|
||||
progress := func(current, total int64) {
|
||||
fmt.Println(float32(current)/float32(total)*100, "%")
|
||||
}
|
||||
req.Post(url, req.File("/Users/roc/Pictures/*.png"), req.UploadProgress(progress))
|
||||
fmt.Println("upload complete")
|
||||
```
|
||||
|
||||
## <a name="Download">Download</a>
|
||||
``` go
|
||||
r, _ := req.Get(url)
|
||||
r.ToFile("imroc.png")
|
||||
```
|
||||
Use `req.DownloadProgress` to listen download progress
|
||||
```go
|
||||
progress := func(current, total int64) {
|
||||
fmt.Println(float32(current)/float32(total)*100, "%")
|
||||
}
|
||||
r, _ := req.Get(url, req.DownloadProgress(progress))
|
||||
r.ToFile("hello.mp4")
|
||||
fmt.Println("download complete")
|
||||
```
|
||||
|
||||
## <a name="Cookie">Cookie</a>
|
||||
By default, the underlying `*http.Client` will manage your cookie(send cookie header to server automatically if server has set a cookie for you), you can disable it by calling this function :
|
||||
``` go
|
||||
req.EnableCookie(false)
|
||||
```
|
||||
and you can set cookie in request just using `*http.Cookie`
|
||||
``` go
|
||||
cookie := new(http.Cookie)
|
||||
// ......
|
||||
req.Get(url, cookie)
|
||||
```
|
||||
|
||||
## <a name="Set-Timeout">Set Timeout</a>
|
||||
``` go
|
||||
req.SetTimeout(50 * time.Second)
|
||||
```
|
||||
|
||||
## <a name="Set-Proxy">Set Proxy</a>
|
||||
By default, req use proxy from system environment if `http_proxy` or `https_proxy` is specified, you can set a custom proxy or disable it by set `nil`
|
||||
``` go
|
||||
req.SetProxy(func(r *http.Request) (*url.URL, error) {
|
||||
if strings.Contains(r.URL.Hostname(), "google") {
|
||||
return url.Parse("http://my.vpn.com:23456")
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
```
|
||||
Set a simple proxy (use fixed proxy url for every request)
|
||||
``` go
|
||||
req.SetProxyUrl("http://my.proxy.com:23456")
|
||||
```
|
||||
|
||||
## <a name="Customize-Client">Customize Client</a>
|
||||
Use `SetClient` to change the default underlying `*http.Client`
|
||||
``` go
|
||||
req.SetClient(client)
|
||||
```
|
||||
Specify independent http client for some requests
|
||||
``` go
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
req.Get(url, client)
|
||||
```
|
||||
Change some properties of default client you want
|
||||
``` go
|
||||
req.Client().Jar, _ = cookiejar.New(nil)
|
||||
trans, _ := req.Client().Transport.(*http.Transport)
|
||||
trans.MaxIdleConns = 20
|
||||
trans.TLSHandshakeTimeout = 20 * time.Second
|
||||
trans.DisableKeepAlives = true
|
||||
trans.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
```
|
||||
216
backend/vendor/github.com/imroc/req/dump.go
generated
vendored
Normal file
216
backend/vendor/github.com/imroc/req/dump.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Debug enable debug mode if set to true
|
||||
var Debug bool
|
||||
|
||||
// dumpConn is a net.Conn which writes to Writer and reads from Reader
|
||||
type dumpConn struct {
|
||||
io.Writer
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (c *dumpConn) Close() error { return nil }
|
||||
func (c *dumpConn) LocalAddr() net.Addr { return nil }
|
||||
func (c *dumpConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
// delegateReader is a reader that delegates to another reader,
|
||||
// once it arrives on a channel.
|
||||
type delegateReader struct {
|
||||
c chan io.Reader
|
||||
r io.Reader // nil until received from c
|
||||
}
|
||||
|
||||
func (r *delegateReader) Read(p []byte) (int, error) {
|
||||
if r.r == nil {
|
||||
r.r = <-r.c
|
||||
}
|
||||
return r.r.Read(p)
|
||||
}
|
||||
|
||||
type dummyBody struct {
|
||||
N int
|
||||
off int
|
||||
}
|
||||
|
||||
func (d *dummyBody) Read(p []byte) (n int, err error) {
|
||||
if d.N <= 0 {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
left := d.N - d.off
|
||||
if left <= 0 {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
|
||||
if l := len(p); l > 0 {
|
||||
if l >= left {
|
||||
n = left
|
||||
err = io.EOF
|
||||
} else {
|
||||
n = l
|
||||
}
|
||||
d.off += n
|
||||
for i := 0; i < n; i++ {
|
||||
p[i] = '*'
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *dummyBody) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type dumpBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *dumpBuffer) Write(p []byte) {
|
||||
if b.Len() > 0 {
|
||||
b.Buffer.WriteString("\r\n\r\n")
|
||||
}
|
||||
b.Buffer.Write(p)
|
||||
}
|
||||
|
||||
func (b *dumpBuffer) WriteString(s string) {
|
||||
b.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (r *Resp) dumpRequest(dump *dumpBuffer) {
|
||||
head := r.r.flag&LreqHead != 0
|
||||
body := r.r.flag&LreqBody != 0
|
||||
|
||||
if head {
|
||||
r.dumpReqHead(dump)
|
||||
}
|
||||
if body {
|
||||
if r.multipartHelper != nil {
|
||||
dump.Write(r.multipartHelper.Dump())
|
||||
} else if len(r.reqBody) > 0 {
|
||||
dump.Write(r.reqBody)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resp) dumpReqHead(dump *dumpBuffer) {
|
||||
reqSend := new(http.Request)
|
||||
*reqSend = *r.req
|
||||
if reqSend.URL.Scheme == "https" {
|
||||
reqSend.URL = new(url.URL)
|
||||
*reqSend.URL = *r.req.URL
|
||||
reqSend.URL.Scheme = "http"
|
||||
}
|
||||
|
||||
if reqSend.ContentLength > 0 {
|
||||
reqSend.Body = &dummyBody{N: int(reqSend.ContentLength)}
|
||||
} else {
|
||||
reqSend.Body = &dummyBody{N: 1}
|
||||
}
|
||||
|
||||
// Use the actual Transport code to record what we would send
|
||||
// on the wire, but not using TCP. Use a Transport with a
|
||||
// custom dialer that returns a fake net.Conn that waits
|
||||
// for the full input (and recording it), and then responds
|
||||
// with a dummy response.
|
||||
var buf bytes.Buffer // records the output
|
||||
pr, pw := io.Pipe()
|
||||
defer pw.Close()
|
||||
dr := &delegateReader{c: make(chan io.Reader)}
|
||||
|
||||
t := &http.Transport{
|
||||
Dial: func(net, addr string) (net.Conn, error) {
|
||||
return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
|
||||
},
|
||||
}
|
||||
defer t.CloseIdleConnections()
|
||||
|
||||
client := new(http.Client)
|
||||
*client = *r.client
|
||||
client.Transport = t
|
||||
|
||||
// Wait for the request before replying with a dummy response:
|
||||
go func() {
|
||||
req, err := http.ReadRequest(bufio.NewReader(pr))
|
||||
if err == nil {
|
||||
// Ensure all the body is read; otherwise
|
||||
// we'll get a partial dump.
|
||||
io.Copy(ioutil.Discard, req.Body)
|
||||
req.Body.Close()
|
||||
}
|
||||
|
||||
dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
|
||||
pr.Close()
|
||||
}()
|
||||
|
||||
_, err := client.Do(reqSend)
|
||||
if err != nil {
|
||||
dump.WriteString(err.Error())
|
||||
} else {
|
||||
reqDump := buf.Bytes()
|
||||
if i := bytes.Index(reqDump, []byte("\r\n\r\n")); i >= 0 {
|
||||
reqDump = reqDump[:i]
|
||||
}
|
||||
dump.Write(reqDump)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resp) dumpResponse(dump *dumpBuffer) {
|
||||
head := r.r.flag&LrespHead != 0
|
||||
body := r.r.flag&LrespBody != 0
|
||||
if head {
|
||||
respDump, err := httputil.DumpResponse(r.resp, false)
|
||||
if err != nil {
|
||||
dump.WriteString(err.Error())
|
||||
} else {
|
||||
if i := bytes.Index(respDump, []byte("\r\n\r\n")); i >= 0 {
|
||||
respDump = respDump[:i]
|
||||
}
|
||||
dump.Write(respDump)
|
||||
}
|
||||
}
|
||||
if body && len(r.Bytes()) > 0 {
|
||||
dump.Write(r.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
// Cost return the time cost of the request
|
||||
func (r *Resp) Cost() time.Duration {
|
||||
return r.cost
|
||||
}
|
||||
|
||||
// Dump dump the request
|
||||
func (r *Resp) Dump() string {
|
||||
dump := new(dumpBuffer)
|
||||
if r.r.flag&Lcost != 0 {
|
||||
dump.WriteString(fmt.Sprint(r.cost))
|
||||
}
|
||||
r.dumpRequest(dump)
|
||||
l := dump.Len()
|
||||
if l > 0 {
|
||||
dump.WriteString("=================================")
|
||||
l = dump.Len()
|
||||
}
|
||||
|
||||
r.dumpResponse(dump)
|
||||
|
||||
return dump.String()
|
||||
}
|
||||
688
backend/vendor/github.com/imroc/req/req.go
generated
vendored
Normal file
688
backend/vendor/github.com/imroc/req/req.go
generated
vendored
Normal file
@@ -0,0 +1,688 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// default *Req
|
||||
var std = New()
|
||||
|
||||
// flags to decide which part can be outputed
|
||||
const (
|
||||
LreqHead = 1 << iota // output request head (request line and request header)
|
||||
LreqBody // output request body
|
||||
LrespHead // output response head (response line and response header)
|
||||
LrespBody // output response body
|
||||
Lcost // output time costed by the request
|
||||
LstdFlags = LreqHead | LreqBody | LrespHead | LrespBody
|
||||
)
|
||||
|
||||
// Header represents http request header
|
||||
type Header map[string]string
|
||||
|
||||
func (h Header) Clone() Header {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
hh := Header{}
|
||||
for k, v := range h {
|
||||
hh[k] = v
|
||||
}
|
||||
return hh
|
||||
}
|
||||
|
||||
// Param represents http request param
|
||||
type Param map[string]interface{}
|
||||
|
||||
// QueryParam is used to force append http request param to the uri
|
||||
type QueryParam map[string]interface{}
|
||||
|
||||
// Host is used for set request's Host
|
||||
type Host string
|
||||
|
||||
// FileUpload represents a file to upload
|
||||
type FileUpload struct {
|
||||
// filename in multipart form.
|
||||
FileName string
|
||||
// form field name
|
||||
FieldName string
|
||||
// file to uplaod, required
|
||||
File io.ReadCloser
|
||||
}
|
||||
|
||||
type DownloadProgress func(current, total int64)
|
||||
|
||||
type UploadProgress func(current, total int64)
|
||||
|
||||
// File upload files matching the name pattern such as
|
||||
// /usr/*/bin/go* (assuming the Separator is '/')
|
||||
func File(patterns ...string) interface{} {
|
||||
matches := []string{}
|
||||
for _, pattern := range patterns {
|
||||
m, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
matches = append(matches, m...)
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return errors.New("req: no file have been matched")
|
||||
}
|
||||
uploads := []FileUpload{}
|
||||
for _, match := range matches {
|
||||
if s, e := os.Stat(match); e != nil || s.IsDir() {
|
||||
continue
|
||||
}
|
||||
file, _ := os.Open(match)
|
||||
uploads = append(uploads, FileUpload{
|
||||
File: file,
|
||||
FileName: filepath.Base(match),
|
||||
FieldName: "media",
|
||||
})
|
||||
}
|
||||
|
||||
return uploads
|
||||
}
|
||||
|
||||
type bodyJson struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
type bodyXml struct {
|
||||
v interface{}
|
||||
}
|
||||
|
||||
// BodyJSON make the object be encoded in json format and set it to the request body
|
||||
func BodyJSON(v interface{}) *bodyJson {
|
||||
return &bodyJson{v: v}
|
||||
}
|
||||
|
||||
// BodyXML make the object be encoded in xml format and set it to the request body
|
||||
func BodyXML(v interface{}) *bodyXml {
|
||||
return &bodyXml{v: v}
|
||||
}
|
||||
|
||||
// Req is a convenient client for initiating requests
|
||||
type Req struct {
|
||||
client *http.Client
|
||||
jsonEncOpts *jsonEncOpts
|
||||
xmlEncOpts *xmlEncOpts
|
||||
flag int
|
||||
}
|
||||
|
||||
// New create a new *Req
|
||||
func New() *Req {
|
||||
return &Req{flag: LstdFlags}
|
||||
}
|
||||
|
||||
type param struct {
|
||||
url.Values
|
||||
}
|
||||
|
||||
func (p *param) getValues() url.Values {
|
||||
if p.Values == nil {
|
||||
p.Values = make(url.Values)
|
||||
}
|
||||
return p.Values
|
||||
}
|
||||
|
||||
func (p *param) Copy(pp param) {
|
||||
if pp.Values == nil {
|
||||
return
|
||||
}
|
||||
vs := p.getValues()
|
||||
for key, values := range pp.Values {
|
||||
for _, value := range values {
|
||||
vs.Add(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
func (p *param) Adds(m map[string]interface{}) {
|
||||
if len(m) == 0 {
|
||||
return
|
||||
}
|
||||
vs := p.getValues()
|
||||
for k, v := range m {
|
||||
vs.Add(k, fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *param) Empty() bool {
|
||||
return p.Values == nil
|
||||
}
|
||||
|
||||
// Do execute a http request with sepecify method and url,
|
||||
// and it can also have some optional params, depending on your needs.
|
||||
func (r *Req) Do(method, rawurl string, vs ...interface{}) (resp *Resp, err error) {
|
||||
if rawurl == "" {
|
||||
return nil, errors.New("req: url not specified")
|
||||
}
|
||||
req := &http.Request{
|
||||
Method: method,
|
||||
Header: make(http.Header),
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
}
|
||||
resp = &Resp{req: req, r: r}
|
||||
|
||||
var queryParam param
|
||||
var formParam param
|
||||
var uploads []FileUpload
|
||||
var uploadProgress UploadProgress
|
||||
var progress func(int64, int64)
|
||||
var delayedFunc []func()
|
||||
var lastFunc []func()
|
||||
|
||||
for _, v := range vs {
|
||||
switch vv := v.(type) {
|
||||
case Header:
|
||||
for key, value := range vv {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
case http.Header:
|
||||
for key, values := range vv {
|
||||
for _, value := range values {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
case *bodyJson:
|
||||
fn, err := setBodyJson(req, resp, r.jsonEncOpts, vv.v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delayedFunc = append(delayedFunc, fn)
|
||||
case *bodyXml:
|
||||
fn, err := setBodyXml(req, resp, r.xmlEncOpts, vv.v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delayedFunc = append(delayedFunc, fn)
|
||||
case url.Values:
|
||||
p := param{vv}
|
||||
if method == "GET" || method == "HEAD" {
|
||||
queryParam.Copy(p)
|
||||
} else {
|
||||
formParam.Copy(p)
|
||||
}
|
||||
case Param:
|
||||
if method == "GET" || method == "HEAD" {
|
||||
queryParam.Adds(vv)
|
||||
} else {
|
||||
formParam.Adds(vv)
|
||||
}
|
||||
case QueryParam:
|
||||
queryParam.Adds(vv)
|
||||
case string:
|
||||
setBodyBytes(req, resp, []byte(vv))
|
||||
case []byte:
|
||||
setBodyBytes(req, resp, vv)
|
||||
case bytes.Buffer:
|
||||
setBodyBytes(req, resp, vv.Bytes())
|
||||
case *http.Client:
|
||||
resp.client = vv
|
||||
case FileUpload:
|
||||
uploads = append(uploads, vv)
|
||||
case []FileUpload:
|
||||
uploads = append(uploads, vv...)
|
||||
case *http.Cookie:
|
||||
req.AddCookie(vv)
|
||||
case Host:
|
||||
req.Host = string(vv)
|
||||
case io.Reader:
|
||||
fn := setBodyReader(req, resp, vv)
|
||||
lastFunc = append(lastFunc, fn)
|
||||
case UploadProgress:
|
||||
uploadProgress = vv
|
||||
case DownloadProgress:
|
||||
resp.downloadProgress = vv
|
||||
case func(int64, int64):
|
||||
progress = vv
|
||||
case context.Context:
|
||||
req = req.WithContext(vv)
|
||||
resp.req = req
|
||||
case error:
|
||||
return nil, vv
|
||||
}
|
||||
}
|
||||
|
||||
if length := req.Header.Get("Content-Length"); length != "" {
|
||||
if l, err := strconv.ParseInt(length, 10, 64); err == nil {
|
||||
req.ContentLength = l
|
||||
}
|
||||
}
|
||||
|
||||
if len(uploads) > 0 && (req.Method == "POST" || req.Method == "PUT") { // multipart
|
||||
var up UploadProgress
|
||||
if uploadProgress != nil {
|
||||
up = uploadProgress
|
||||
} else if progress != nil {
|
||||
up = UploadProgress(progress)
|
||||
}
|
||||
multipartHelper := &multipartHelper{
|
||||
form: formParam.Values,
|
||||
uploads: uploads,
|
||||
uploadProgress: up,
|
||||
}
|
||||
multipartHelper.Upload(req)
|
||||
resp.multipartHelper = multipartHelper
|
||||
} else {
|
||||
if progress != nil {
|
||||
resp.downloadProgress = DownloadProgress(progress)
|
||||
}
|
||||
if !formParam.Empty() {
|
||||
if req.Body != nil {
|
||||
queryParam.Copy(formParam)
|
||||
} else {
|
||||
setBodyBytes(req, resp, []byte(formParam.Encode()))
|
||||
setContentType(req, "application/x-www-form-urlencoded; charset=UTF-8")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !queryParam.Empty() {
|
||||
paramStr := queryParam.Encode()
|
||||
if strings.IndexByte(rawurl, '?') == -1 {
|
||||
rawurl = rawurl + "?" + paramStr
|
||||
} else {
|
||||
rawurl = rawurl + "&" + paramStr
|
||||
}
|
||||
}
|
||||
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.URL = u
|
||||
|
||||
if host := req.Header.Get("Host"); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
|
||||
for _, fn := range delayedFunc {
|
||||
fn()
|
||||
}
|
||||
|
||||
if resp.client == nil {
|
||||
resp.client = r.Client()
|
||||
}
|
||||
|
||||
var response *http.Response
|
||||
if r.flag&Lcost != 0 {
|
||||
before := time.Now()
|
||||
response, err = resp.client.Do(req)
|
||||
after := time.Now()
|
||||
resp.cost = after.Sub(before)
|
||||
} else {
|
||||
response, err = resp.client.Do(req)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, fn := range lastFunc {
|
||||
fn()
|
||||
}
|
||||
|
||||
resp.resp = response
|
||||
|
||||
if _, ok := resp.client.Transport.(*http.Transport); ok && response.Header.Get("Content-Encoding") == "gzip" && req.Header.Get("Accept-Encoding") != "" {
|
||||
body, err := gzip.NewReader(response.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Body = body
|
||||
}
|
||||
|
||||
// output detail if Debug is enabled
|
||||
if Debug {
|
||||
fmt.Println(resp.Dump())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setBodyBytes(req *http.Request, resp *Resp, data []byte) {
|
||||
resp.reqBody = data
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(data))
|
||||
req.ContentLength = int64(len(data))
|
||||
}
|
||||
|
||||
func setBodyJson(req *http.Request, resp *Resp, opts *jsonEncOpts, v interface{}) (func(), error) {
|
||||
var data []byte
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
data = []byte(vv)
|
||||
case []byte:
|
||||
data = vv
|
||||
case *bytes.Buffer:
|
||||
data = vv.Bytes()
|
||||
default:
|
||||
if opts != nil {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetIndent(opts.indentPrefix, opts.indentValue)
|
||||
enc.SetEscapeHTML(opts.escapeHTML)
|
||||
err := enc.Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = buf.Bytes()
|
||||
} else {
|
||||
var err error
|
||||
data, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
setBodyBytes(req, resp, data)
|
||||
delayedFunc := func() {
|
||||
setContentType(req, "application/json; charset=UTF-8")
|
||||
}
|
||||
return delayedFunc, nil
|
||||
}
|
||||
|
||||
func setBodyXml(req *http.Request, resp *Resp, opts *xmlEncOpts, v interface{}) (func(), error) {
|
||||
var data []byte
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
data = []byte(vv)
|
||||
case []byte:
|
||||
data = vv
|
||||
case *bytes.Buffer:
|
||||
data = vv.Bytes()
|
||||
default:
|
||||
if opts != nil {
|
||||
var buf bytes.Buffer
|
||||
enc := xml.NewEncoder(&buf)
|
||||
enc.Indent(opts.prefix, opts.indent)
|
||||
err := enc.Encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = buf.Bytes()
|
||||
} else {
|
||||
var err error
|
||||
data, err = xml.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
setBodyBytes(req, resp, data)
|
||||
delayedFunc := func() {
|
||||
setContentType(req, "application/xml; charset=UTF-8")
|
||||
}
|
||||
return delayedFunc, nil
|
||||
}
|
||||
|
||||
func setContentType(req *http.Request, contentType string) {
|
||||
if req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
}
|
||||
}
|
||||
|
||||
func setBodyReader(req *http.Request, resp *Resp, rd io.Reader) func() {
|
||||
var rc io.ReadCloser
|
||||
switch r := rd.(type) {
|
||||
case *os.File:
|
||||
stat, err := r.Stat()
|
||||
if err == nil {
|
||||
req.ContentLength = stat.Size()
|
||||
}
|
||||
rc = r
|
||||
|
||||
case io.ReadCloser:
|
||||
rc = r
|
||||
default:
|
||||
rc = ioutil.NopCloser(rd)
|
||||
}
|
||||
bw := &bodyWrapper{
|
||||
ReadCloser: rc,
|
||||
limit: 102400,
|
||||
}
|
||||
req.Body = bw
|
||||
lastFunc := func() {
|
||||
resp.reqBody = bw.buf.Bytes()
|
||||
}
|
||||
return lastFunc
|
||||
}
|
||||
|
||||
type bodyWrapper struct {
|
||||
io.ReadCloser
|
||||
buf bytes.Buffer
|
||||
limit int
|
||||
}
|
||||
|
||||
func (b *bodyWrapper) Read(p []byte) (n int, err error) {
|
||||
n, err = b.ReadCloser.Read(p)
|
||||
if left := b.limit - b.buf.Len(); left > 0 && n > 0 {
|
||||
if n <= left {
|
||||
b.buf.Write(p[:n])
|
||||
} else {
|
||||
b.buf.Write(p[:left])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type multipartHelper struct {
|
||||
form url.Values
|
||||
uploads []FileUpload
|
||||
dump []byte
|
||||
uploadProgress UploadProgress
|
||||
}
|
||||
|
||||
func (m *multipartHelper) Upload(req *http.Request) {
|
||||
pr, pw := io.Pipe()
|
||||
bodyWriter := multipart.NewWriter(pw)
|
||||
go func() {
|
||||
for key, values := range m.form {
|
||||
for _, value := range values {
|
||||
bodyWriter.WriteField(key, value)
|
||||
}
|
||||
}
|
||||
var upload func(io.Writer, io.Reader) error
|
||||
if m.uploadProgress != nil {
|
||||
var total int64
|
||||
for _, up := range m.uploads {
|
||||
if file, ok := up.File.(*os.File); ok {
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
total += stat.Size()
|
||||
}
|
||||
}
|
||||
var current int64
|
||||
buf := make([]byte, 1024)
|
||||
var lastTime time.Time
|
||||
upload = func(w io.Writer, r io.Reader) error {
|
||||
for {
|
||||
n, err := r.Read(buf)
|
||||
if n > 0 {
|
||||
_, _err := w.Write(buf[:n])
|
||||
if _err != nil {
|
||||
return _err
|
||||
}
|
||||
current += int64(n)
|
||||
if now := time.Now(); now.Sub(lastTime) > 200*time.Millisecond {
|
||||
lastTime = now
|
||||
m.uploadProgress(current, total)
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for _, up := range m.uploads {
|
||||
if up.FieldName == "" {
|
||||
i++
|
||||
up.FieldName = "file" + strconv.Itoa(i)
|
||||
}
|
||||
fileWriter, err := bodyWriter.CreateFormFile(up.FieldName, up.FileName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
//iocopy
|
||||
if upload == nil {
|
||||
io.Copy(fileWriter, up.File)
|
||||
} else {
|
||||
if _, ok := up.File.(*os.File); ok {
|
||||
upload(fileWriter, up.File)
|
||||
} else {
|
||||
io.Copy(fileWriter, up.File)
|
||||
}
|
||||
}
|
||||
up.File.Close()
|
||||
}
|
||||
bodyWriter.Close()
|
||||
pw.Close()
|
||||
}()
|
||||
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
|
||||
req.Body = ioutil.NopCloser(pr)
|
||||
}
|
||||
|
||||
func (m *multipartHelper) Dump() []byte {
|
||||
if m.dump != nil {
|
||||
return m.dump
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
bodyWriter := multipart.NewWriter(&buf)
|
||||
for key, values := range m.form {
|
||||
for _, value := range values {
|
||||
m.writeField(bodyWriter, key, value)
|
||||
}
|
||||
}
|
||||
for _, up := range m.uploads {
|
||||
m.writeFile(bodyWriter, up.FieldName, up.FileName)
|
||||
}
|
||||
bodyWriter.Close()
|
||||
m.dump = buf.Bytes()
|
||||
return m.dump
|
||||
}
|
||||
|
||||
func (m *multipartHelper) writeField(w *multipart.Writer, fieldname, value string) error {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"`, fieldname))
|
||||
p, err := w.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.Write([]byte(value))
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *multipartHelper) writeFile(w *multipart.Writer, fieldname, filename string) error {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
|
||||
fieldname, filename))
|
||||
h.Set("Content-Type", "application/octet-stream")
|
||||
p, err := w.CreatePart(h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.Write([]byte("******"))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get execute a http GET request
|
||||
func (r *Req) Get(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("GET", url, v...)
|
||||
}
|
||||
|
||||
// Post execute a http POST request
|
||||
func (r *Req) Post(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("POST", url, v...)
|
||||
}
|
||||
|
||||
// Put execute a http PUT request
|
||||
func (r *Req) Put(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("PUT", url, v...)
|
||||
}
|
||||
|
||||
// Patch execute a http PATCH request
|
||||
func (r *Req) Patch(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("PATCH", url, v...)
|
||||
}
|
||||
|
||||
// Delete execute a http DELETE request
|
||||
func (r *Req) Delete(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("DELETE", url, v...)
|
||||
}
|
||||
|
||||
// Head execute a http HEAD request
|
||||
func (r *Req) Head(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("HEAD", url, v...)
|
||||
}
|
||||
|
||||
// Options execute a http OPTIONS request
|
||||
func (r *Req) Options(url string, v ...interface{}) (*Resp, error) {
|
||||
return r.Do("OPTIONS", url, v...)
|
||||
}
|
||||
|
||||
// Get execute a http GET request
|
||||
func Get(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Get(url, v...)
|
||||
}
|
||||
|
||||
// Post execute a http POST request
|
||||
func Post(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Post(url, v...)
|
||||
}
|
||||
|
||||
// Put execute a http PUT request
|
||||
func Put(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Put(url, v...)
|
||||
}
|
||||
|
||||
// Head execute a http HEAD request
|
||||
func Head(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Head(url, v...)
|
||||
}
|
||||
|
||||
// Options execute a http OPTIONS request
|
||||
func Options(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Options(url, v...)
|
||||
}
|
||||
|
||||
// Delete execute a http DELETE request
|
||||
func Delete(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Delete(url, v...)
|
||||
}
|
||||
|
||||
// Patch execute a http PATCH request
|
||||
func Patch(url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Patch(url, v...)
|
||||
}
|
||||
|
||||
// Do execute request.
|
||||
func Do(method, url string, v ...interface{}) (*Resp, error) {
|
||||
return std.Do(method, url, v...)
|
||||
}
|
||||
215
backend/vendor/github.com/imroc/req/resp.go
generated
vendored
Normal file
215
backend/vendor/github.com/imroc/req/resp.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Resp represents a request with it's response
|
||||
type Resp struct {
|
||||
r *Req
|
||||
req *http.Request
|
||||
resp *http.Response
|
||||
client *http.Client
|
||||
cost time.Duration
|
||||
*multipartHelper
|
||||
reqBody []byte
|
||||
respBody []byte
|
||||
downloadProgress DownloadProgress
|
||||
err error // delayed error
|
||||
}
|
||||
|
||||
// Request returns *http.Request
|
||||
func (r *Resp) Request() *http.Request {
|
||||
return r.req
|
||||
}
|
||||
|
||||
// Response returns *http.Response
|
||||
func (r *Resp) Response() *http.Response {
|
||||
return r.resp
|
||||
}
|
||||
|
||||
// Bytes returns response body as []byte
|
||||
func (r *Resp) Bytes() []byte {
|
||||
data, _ := r.ToBytes()
|
||||
return data
|
||||
}
|
||||
|
||||
// ToBytes returns response body as []byte,
|
||||
// return error if error happend when reading
|
||||
// the response body
|
||||
func (r *Resp) ToBytes() ([]byte, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
if r.respBody != nil {
|
||||
return r.respBody, nil
|
||||
}
|
||||
defer r.resp.Body.Close()
|
||||
respBody, err := ioutil.ReadAll(r.resp.Body)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return nil, err
|
||||
}
|
||||
r.respBody = respBody
|
||||
return r.respBody, nil
|
||||
}
|
||||
|
||||
// String returns response body as string
|
||||
func (r *Resp) String() string {
|
||||
data, _ := r.ToBytes()
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// ToString returns response body as string,
|
||||
// return error if error happend when reading
|
||||
// the response body
|
||||
func (r *Resp) ToString() (string, error) {
|
||||
data, err := r.ToBytes()
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// ToJSON convert json response body to struct or map
|
||||
func (r *Resp) ToJSON(v interface{}) error {
|
||||
data, err := r.ToBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// ToXML convert xml response body to struct or map
|
||||
func (r *Resp) ToXML(v interface{}) error {
|
||||
data, err := r.ToBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return xml.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
// ToFile download the response body to file with optional download callback
|
||||
func (r *Resp) ToFile(name string) error {
|
||||
//TODO set name to the suffix of url path if name == ""
|
||||
file, err := os.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if r.respBody != nil {
|
||||
_, err = file.Write(r.respBody)
|
||||
return err
|
||||
}
|
||||
|
||||
if r.downloadProgress != nil && r.resp.ContentLength > 0 {
|
||||
return r.download(file)
|
||||
}
|
||||
|
||||
defer r.resp.Body.Close()
|
||||
_, err = io.Copy(file, r.resp.Body)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Resp) download(file *os.File) error {
|
||||
p := make([]byte, 1024)
|
||||
b := r.resp.Body
|
||||
defer b.Close()
|
||||
total := r.resp.ContentLength
|
||||
var current int64
|
||||
var lastTime time.Time
|
||||
for {
|
||||
l, err := b.Read(p)
|
||||
if l > 0 {
|
||||
_, _err := file.Write(p[:l])
|
||||
if _err != nil {
|
||||
return _err
|
||||
}
|
||||
current += int64(l)
|
||||
if now := time.Now(); now.Sub(lastTime) > 200*time.Millisecond {
|
||||
lastTime = now
|
||||
r.downloadProgress(current, total)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var regNewline = regexp.MustCompile(`\n|\r`)
|
||||
|
||||
func (r *Resp) autoFormat(s fmt.State) {
|
||||
req := r.req
|
||||
if r.r.flag&Lcost != 0 {
|
||||
fmt.Fprint(s, req.Method, " ", req.URL.String(), " ", r.cost)
|
||||
} else {
|
||||
fmt.Fprint(s, req.Method, " ", req.URL.String())
|
||||
}
|
||||
|
||||
// test if it is should be outputed pretty
|
||||
var pretty bool
|
||||
var parts []string
|
||||
addPart := func(part string) {
|
||||
if part == "" {
|
||||
return
|
||||
}
|
||||
parts = append(parts, part)
|
||||
if !pretty && regNewline.MatchString(part) {
|
||||
pretty = true
|
||||
}
|
||||
}
|
||||
if r.r.flag&LreqBody != 0 { // request body
|
||||
addPart(string(r.reqBody))
|
||||
}
|
||||
if r.r.flag&LrespBody != 0 { // response body
|
||||
addPart(r.String())
|
||||
}
|
||||
|
||||
for _, part := range parts {
|
||||
if pretty {
|
||||
fmt.Fprint(s, "\n")
|
||||
}
|
||||
fmt.Fprint(s, " ", part)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resp) miniFormat(s fmt.State) {
|
||||
req := r.req
|
||||
if r.r.flag&Lcost != 0 {
|
||||
fmt.Fprint(s, req.Method, " ", req.URL.String(), " ", r.cost)
|
||||
} else {
|
||||
fmt.Fprint(s, req.Method, " ", req.URL.String())
|
||||
}
|
||||
if r.r.flag&LreqBody != 0 && len(r.reqBody) > 0 { // request body
|
||||
str := regNewline.ReplaceAllString(string(r.reqBody), " ")
|
||||
fmt.Fprint(s, " ", str)
|
||||
}
|
||||
if r.r.flag&LrespBody != 0 && r.String() != "" { // response body
|
||||
str := regNewline.ReplaceAllString(r.String(), " ")
|
||||
fmt.Fprint(s, " ", str)
|
||||
}
|
||||
}
|
||||
|
||||
// Format fort the response
|
||||
func (r *Resp) Format(s fmt.State, verb rune) {
|
||||
if r == nil || r.req == nil {
|
||||
return
|
||||
}
|
||||
if s.Flag('+') { // include header and format pretty.
|
||||
fmt.Fprint(s, r.Dump())
|
||||
} else if s.Flag('-') { // keep all informations in one line.
|
||||
r.miniFormat(s)
|
||||
} else { // auto
|
||||
r.autoFormat(s)
|
||||
}
|
||||
}
|
||||
236
backend/vendor/github.com/imroc/req/setting.go
generated
vendored
Normal file
236
backend/vendor/github.com/imroc/req/setting.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// create a default client
|
||||
func newClient() *http.Client {
|
||||
jar, _ := cookiejar.New(nil)
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
return &http.Client{
|
||||
Jar: jar,
|
||||
Transport: transport,
|
||||
Timeout: 2 * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
// Client return the default underlying http client
|
||||
func (r *Req) Client() *http.Client {
|
||||
if r.client == nil {
|
||||
r.client = newClient()
|
||||
}
|
||||
return r.client
|
||||
}
|
||||
|
||||
// Client return the default underlying http client
|
||||
func Client() *http.Client {
|
||||
return std.Client()
|
||||
}
|
||||
|
||||
// SetClient sets the underlying http.Client.
|
||||
func (r *Req) SetClient(client *http.Client) {
|
||||
r.client = client // use default if client == nil
|
||||
}
|
||||
|
||||
// SetClient sets the default http.Client for requests.
|
||||
func SetClient(client *http.Client) {
|
||||
std.SetClient(client)
|
||||
}
|
||||
|
||||
// SetFlags control display format of *Resp
|
||||
func (r *Req) SetFlags(flags int) {
|
||||
r.flag = flags
|
||||
}
|
||||
|
||||
// SetFlags control display format of *Resp
|
||||
func SetFlags(flags int) {
|
||||
std.SetFlags(flags)
|
||||
}
|
||||
|
||||
// Flags return output format for the *Resp
|
||||
func (r *Req) Flags() int {
|
||||
return r.flag
|
||||
}
|
||||
|
||||
// Flags return output format for the *Resp
|
||||
func Flags() int {
|
||||
return std.Flags()
|
||||
}
|
||||
|
||||
func (r *Req) getTransport() *http.Transport {
|
||||
trans, _ := r.Client().Transport.(*http.Transport)
|
||||
return trans
|
||||
}
|
||||
|
||||
// EnableInsecureTLS allows insecure https
|
||||
func (r *Req) EnableInsecureTLS(enable bool) {
|
||||
trans := r.getTransport()
|
||||
if trans == nil {
|
||||
return
|
||||
}
|
||||
if trans.TLSClientConfig == nil {
|
||||
trans.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
trans.TLSClientConfig.InsecureSkipVerify = enable
|
||||
}
|
||||
|
||||
func EnableInsecureTLS(enable bool) {
|
||||
std.EnableInsecureTLS(enable)
|
||||
}
|
||||
|
||||
// EnableCookieenable or disable cookie manager
|
||||
func (r *Req) EnableCookie(enable bool) {
|
||||
if enable {
|
||||
jar, _ := cookiejar.New(nil)
|
||||
r.Client().Jar = jar
|
||||
} else {
|
||||
r.Client().Jar = nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableCookieenable or disable cookie manager
|
||||
func EnableCookie(enable bool) {
|
||||
std.EnableCookie(enable)
|
||||
}
|
||||
|
||||
// SetTimeout sets the timeout for every request
|
||||
func (r *Req) SetTimeout(d time.Duration) {
|
||||
r.Client().Timeout = d
|
||||
}
|
||||
|
||||
// SetTimeout sets the timeout for every request
|
||||
func SetTimeout(d time.Duration) {
|
||||
std.SetTimeout(d)
|
||||
}
|
||||
|
||||
// SetProxyUrl set the simple proxy with fixed proxy url
|
||||
func (r *Req) SetProxyUrl(rawurl string) error {
|
||||
trans := r.getTransport()
|
||||
if trans == nil {
|
||||
return errors.New("req: no transport")
|
||||
}
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trans.Proxy = http.ProxyURL(u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetProxyUrl set the simple proxy with fixed proxy url
|
||||
func SetProxyUrl(rawurl string) error {
|
||||
return std.SetProxyUrl(rawurl)
|
||||
}
|
||||
|
||||
// SetProxy sets the proxy for every request
|
||||
func (r *Req) SetProxy(proxy func(*http.Request) (*url.URL, error)) error {
|
||||
trans := r.getTransport()
|
||||
if trans == nil {
|
||||
return errors.New("req: no transport")
|
||||
}
|
||||
trans.Proxy = proxy
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetProxy sets the proxy for every request
|
||||
func SetProxy(proxy func(*http.Request) (*url.URL, error)) error {
|
||||
return std.SetProxy(proxy)
|
||||
}
|
||||
|
||||
type jsonEncOpts struct {
|
||||
indentPrefix string
|
||||
indentValue string
|
||||
escapeHTML bool
|
||||
}
|
||||
|
||||
func (r *Req) getJSONEncOpts() *jsonEncOpts {
|
||||
if r.jsonEncOpts == nil {
|
||||
r.jsonEncOpts = &jsonEncOpts{escapeHTML: true}
|
||||
}
|
||||
return r.jsonEncOpts
|
||||
}
|
||||
|
||||
// SetJSONEscapeHTML specifies whether problematic HTML characters
|
||||
// should be escaped inside JSON quoted strings.
|
||||
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
|
||||
// to avoid certain safety problems that can arise when embedding JSON in HTML.
|
||||
//
|
||||
// In non-HTML settings where the escaping interferes with the readability
|
||||
// of the output, SetEscapeHTML(false) disables this behavior.
|
||||
func (r *Req) SetJSONEscapeHTML(escape bool) {
|
||||
opts := r.getJSONEncOpts()
|
||||
opts.escapeHTML = escape
|
||||
}
|
||||
|
||||
// SetJSONEscapeHTML specifies whether problematic HTML characters
|
||||
// should be escaped inside JSON quoted strings.
|
||||
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
|
||||
// to avoid certain safety problems that can arise when embedding JSON in HTML.
|
||||
//
|
||||
// In non-HTML settings where the escaping interferes with the readability
|
||||
// of the output, SetEscapeHTML(false) disables this behavior.
|
||||
func SetJSONEscapeHTML(escape bool) {
|
||||
std.SetJSONEscapeHTML(escape)
|
||||
}
|
||||
|
||||
// SetJSONIndent instructs the encoder to format each subsequent encoded
|
||||
// value as if indented by the package-level function Indent(dst, src, prefix, indent).
|
||||
// Calling SetIndent("", "") disables indentation.
|
||||
func (r *Req) SetJSONIndent(prefix, indent string) {
|
||||
opts := r.getJSONEncOpts()
|
||||
opts.indentPrefix = prefix
|
||||
opts.indentValue = indent
|
||||
}
|
||||
|
||||
// SetJSONIndent instructs the encoder to format each subsequent encoded
|
||||
// value as if indented by the package-level function Indent(dst, src, prefix, indent).
|
||||
// Calling SetIndent("", "") disables indentation.
|
||||
func SetJSONIndent(prefix, indent string) {
|
||||
std.SetJSONIndent(prefix, indent)
|
||||
}
|
||||
|
||||
type xmlEncOpts struct {
|
||||
prefix string
|
||||
indent string
|
||||
}
|
||||
|
||||
func (r *Req) getXMLEncOpts() *xmlEncOpts {
|
||||
if r.xmlEncOpts == nil {
|
||||
r.xmlEncOpts = &xmlEncOpts{}
|
||||
}
|
||||
return r.xmlEncOpts
|
||||
}
|
||||
|
||||
// SetXMLIndent sets the encoder to generate XML in which each element
|
||||
// begins on a new indented line that starts with prefix and is followed by
|
||||
// one or more copies of indent according to the nesting depth.
|
||||
func (r *Req) SetXMLIndent(prefix, indent string) {
|
||||
opts := r.getXMLEncOpts()
|
||||
opts.prefix = prefix
|
||||
opts.indent = indent
|
||||
}
|
||||
|
||||
// SetXMLIndent sets the encoder to generate XML in which each element
|
||||
// begins on a new indented line that starts with prefix and is followed by
|
||||
// one or more copies of indent according to the nesting depth.
|
||||
func SetXMLIndent(prefix, indent string) {
|
||||
std.SetXMLIndent(prefix, indent)
|
||||
}
|
||||
Reference in New Issue
Block a user