mirror of
https://github.com/eiblog/eiblog.git
synced 2026-02-04 13:52:26 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd69c62254 | ||
|
|
970c6068d5 | ||
|
|
27a1f600de | ||
|
|
edf0fbac51 | ||
|
|
98ca570a36 | ||
|
|
9a526f97f8 | ||
|
|
33a29f5e57 | ||
|
|
30ebf76eda | ||
|
|
f5c0bcdb99 | ||
|
|
4b53da3801 | ||
|
|
1bdfb6abea | ||
|
|
33f47d8f3a | ||
|
|
cbd0cfaaf5 | ||
|
|
080c992a92 | ||
|
|
371b2326ea | ||
|
|
2720d11b23 | ||
|
|
b7751d7b9e | ||
|
|
24d81db8be | ||
|
|
010137ebf5 | ||
|
|
c6a2439c54 | ||
|
|
1d54ff3ac5 | ||
|
|
63a4d69209 | ||
|
|
b35d7de58a | ||
|
|
77ea01b7c1 | ||
|
|
5f608b638d | ||
|
|
52da8abceb | ||
|
|
f016b28cb6 | ||
|
|
01b7643ca5 | ||
|
|
375d43761b | ||
|
|
f3e9727947 | ||
|
|
911aa963c7 | ||
|
|
fb66b6871e | ||
|
|
5ae76f243e | ||
|
|
051b034e51 | ||
|
|
27439ecc71 | ||
|
|
d02c838447 | ||
|
|
d17acf5325 |
86
.github/workflows/release.yml
vendored
Normal file
86
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: release image & asset
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Golang env
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.15
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Cache mod
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
- name: Docker tag
|
||||
id: vars
|
||||
run: echo ::set-output name=tag::$(echo ${GITHUB_REF:10})
|
||||
- name: Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Docker login
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: registry.cn-hangzhou.aliyuncs.com
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
- name: Build binary
|
||||
run: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:latest
|
||||
registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:${{ steps.vars.outputs.tag }}
|
||||
|
||||
- name: Package tar
|
||||
run: ./dist.sh
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.vars.outputs.tag }}
|
||||
release_name: Release ${{ steps.vars.outputs.tag }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- name: Upload asset darwin
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: eiblog-${{ steps.vars.outputs.tag }}.darwin-amd64.tar.gz
|
||||
asset_name: eiblog-${{ steps.vars.outputs.tag }}.darwin-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
- name: Upload asset linux
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: eiblog-${{ steps.vars.outputs.tag }}.linux-amd64.tar.gz
|
||||
asset_name: eiblog-${{ steps.vars.outputs.tag }}.linux-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
- name: Upload asset windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: eiblog-${{ steps.vars.outputs.tag }}.windows-amd64.tar.gz
|
||||
asset_name: eiblog-${{ steps.vars.outputs.tag }}.windows-amd64.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
34
.travis.yml
34
.travis.yml
@@ -1,34 +0,0 @@
|
||||
sudo: required # 超级权限
|
||||
dist: trusty # 在ubuntu:trusty
|
||||
language: go # 声明构建语言环境
|
||||
go: # 只构建最新版本
|
||||
- tip
|
||||
services: # docker环境
|
||||
- docker
|
||||
branches: # 限定项目分支
|
||||
only:
|
||||
- /^v[0-9](\.[0-9]){2}(-rc[1-9])?$/
|
||||
install:
|
||||
- curl https://glide.sh/get | sh # 安装glide包管理
|
||||
script:
|
||||
- glide up
|
||||
- GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build # 编译版本
|
||||
- docker build -t registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog . # 构建镜像
|
||||
after_success:
|
||||
- docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" registry.cn-hangzhou.aliyuncs.com
|
||||
- docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog
|
||||
- docker tag registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:$TRAVIS_TAG
|
||||
- docker push registry.cn-hangzhou.aliyuncs.com/deepzz/eiblog:$TRAVIS_TAG
|
||||
before_deploy:
|
||||
- ./dist.sh
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: AGW05sQuQfjy77+JprSV+ohti/VVgFuh7UOTV0+hwxqsOVXSoIQz/ZPOlHWPP1iiSiGGEalspm+UtKRvADcDfllUaEwo7kebfFeMx4X//qxFxQSQ5LJYx7qxsTDpuQ4CF8zifCtND3ynnUAdx0P6FFkxE/67kN2n4CrhIxYCUb8gNPzDDRuS0ZNBC4zzNldJo/vtatbvc2btuFfwKoClYf+xPLy5luLqDvKF+hdjJ8NuZl8BWkWxXE+kk8fW4iUn2IV0qtLRZ3FQUyAF2CumzxqZfViX+rYTXsfbabYY5nYG6opT4mUEF58T4X3uRV0e3Q6Fe73nmLh9cyAoQl1BTSJ1XiyV4eJJWcKEMY7DqJ646lzoUT449YwvTK57klcfBbShpcjFf2alVdEbr9jbEXrCkuWKnssO9VfufhYF6t9h22c79evpexpIbsoncPD+b+n712MzufREtUF4kpUdkIir5n9CgQl/l7S+fV+n+gME+mcA44K7iPXkC80UfxJiw83QizT39OQhExq6SPIwrbt2vlAkBpSLMUS9iAHtTJYUsmH1SsmrxGK3WromKysWeTRJbcAJls2k6V313sn4TuYBWiHTUfsUBhv+objDFA2TsfO+g0g1JsdfZb5EsKrqNvs/2ta1xlzdE0+/TLG/YNKIOPkHnXswAM3DZm3zEss=
|
||||
skip_cleanup: true
|
||||
file: "*.tar.gz"
|
||||
file_glob: true
|
||||
on:
|
||||
tags: true
|
||||
repo: eiblog/eiblog
|
||||
all_branches: true
|
||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -1,6 +1,34 @@
|
||||
# Eiblog Changelog
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [1.4.9](https://github.com/eiblog/eiblog/compare/v1.4.8...v1.4.9) (2019-12-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **disqus:** connect reset by peer ([1bdfb6a](https://github.com/eiblog/eiblog/commit/1bdfb6a))
|
||||
|
||||
### v1.4.4 (2018-05-07)
|
||||
|
||||
* 修复基础评论分钟数计算错误
|
||||
* let's encrypt v2证书内嵌ct,故移除有关ct内容
|
||||
|
||||
### v1.4.3 (2018-02-09)
|
||||
|
||||
* 修复博客初始化后,about 页面不能够评论 #6
|
||||
* 修复编辑专题,按钮显示“添加专题”错误
|
||||
* 优化“添加文章”从同步改为异步推送:feed,es,disqus。速度显著提升
|
||||
* (**重要*)头像图片从 avatar.jpg 改为 avatar.png(透明)
|
||||
* docker-compose.yml mongodb 去掉端口映射,防止用户将端口暴露至外网
|
||||
* session key 每次重启随机生成等一些细节的修复
|
||||
|
||||
### v1.4.2 (2018-01-25)
|
||||
|
||||
* fix archive page bug
|
||||
|
||||
### v1.4.1 (2018-01-14)
|
||||
|
||||
## v1.4.1 (2018-01-14)
|
||||
* 修复创建新文章,disqus 不收录bug
|
||||
* 修复创建新文章,归档页面不刷新bug
|
||||
* 修复能够删除关于页面和友情链接页面bug
|
||||
@@ -9,29 +37,35 @@
|
||||
* 添加当月数大于12,归档页面使用年份归档
|
||||
* 优化代码逻辑
|
||||
|
||||
## v1.4.0 (2018-01-01)
|
||||
### v1.4.0 (2018-01-01)
|
||||
|
||||
* fix 搜索页面 bug
|
||||
* CGO_ENABLED=0 关闭 cgo
|
||||
* 更新Makefile ct log 服务器
|
||||
* 数据库数据终于可以备份了
|
||||
|
||||
## v1.3.4 (2017-11-29)
|
||||
### v1.3.4 (2017-11-29)
|
||||
|
||||
* fix page:admin/write-post autocomplete tag
|
||||
|
||||
## v1.3.3 (2017-11-27)
|
||||
### v1.3.3 (2017-11-27)
|
||||
|
||||
* fix docker image: exec user process caused "no such file or directory"
|
||||
|
||||
## v1.3.2 (2017-11-17)
|
||||
### v1.3.2 (2017-11-17)
|
||||
|
||||
* 修复文章自动保存引起的发布文章不成功的bug
|
||||
|
||||
## v1.3.1 (2017-11-05)
|
||||
### v1.3.1 (2017-11-05)
|
||||
|
||||
* 修复调整 关于、友情链接 创建时间出现文章乱序
|
||||
* 修复评论时间计算错误
|
||||
* 调整acme文件验证路径
|
||||
* 更改七牛SDK包为github包。
|
||||
* 调整七牛配置文件名称,app.yml: kodo -> qiniu,name -> bucket,请提高静态文件版本 staticversion
|
||||
|
||||
## v1.3.0 (2017-07-13)
|
||||
### v1.3.0 (2017-07-13)
|
||||
|
||||
* 更改 app.yml 配置项,将大部分配置归在 general 常规配置下。注意,部署时请先更新 app.yml。
|
||||
* 静态文件采用动态渲染,即用户不再需要管理 view、static 目录。
|
||||
* 通过 acme.sh 使用双证书啦,可到 Makefile 查看相关信息。
|
||||
@@ -39,28 +73,33 @@
|
||||
* 开启配置项 enablehttps, 将自动重定向 http 到 https 啦。
|
||||
* disqus.js 文件由配置指定,请看 app.yml 下的 disqus 相关配置。
|
||||
|
||||
## v1.2.0 (2017-06-14)
|
||||
### v1.2.0 (2017-06-14)
|
||||
|
||||
* 更新评论功能,基础评论 0 回复也可评论了。
|
||||
* disqus.js 文件由博主自行更新。
|
||||
* 更正描述 README.md 描述错误 [#4f996](https://github.com/eiblog/eiblog/commit/4f9965b6bdefe087dd0805c1840afcb2752cd155)。
|
||||
* docker 镜像版本化。
|
||||
|
||||
## v1.1.3 (2017-05-12)
|
||||
### v1.1.3 (2017-05-12)
|
||||
|
||||
* 更新 disqus_78bca4.js 到 disqus_921d24.js,具体请参考 docs/install.md
|
||||
* 更新 vendor
|
||||
|
||||
## v1.1.2 (2017-03-08)
|
||||
### v1.1.2 (2017-03-08)
|
||||
|
||||
* 解决添加文章描述错误的bug
|
||||
* 添加vendor目录
|
||||
* 添加文档docs目录
|
||||
* 删除多余注释
|
||||
|
||||
## v1.1.1 (2017-02-07)
|
||||
### v1.1.1 (2017-02-07)
|
||||
|
||||
* 添加文章描述功能。
|
||||
* 修复评论`jQuery`文件引用错误。
|
||||
* 修复`.travis.yml`描述错误。
|
||||
|
||||
## v1.0.0 (2016-01-09)
|
||||
### v1.0.0 (2016-01-09)
|
||||
|
||||
首次发布版本
|
||||
|
||||
* 全站`HTTPS`设计,安全、极速。
|
||||
|
||||
40
Makefile
40
Makefile
@@ -12,6 +12,15 @@ config?=/data/eiblog/conf
|
||||
|
||||
test:
|
||||
|
||||
mongodb:
|
||||
@if ! docker ps | grep mongodb; then \
|
||||
docker run -d --name mongodb -v mongo-data:/data/db -p 27018:27017 mongo:3.2; \
|
||||
fi
|
||||
|
||||
run: mongodb
|
||||
@echo "run eiblog..."
|
||||
@go build && ./eiblog
|
||||
|
||||
build:
|
||||
@echo "go build..."
|
||||
@CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build && \
|
||||
@@ -26,7 +35,7 @@ dist:
|
||||
gencert:makedir
|
||||
@if [ ! -n "$(sans)" ]; then \
|
||||
printf "Need one argument [sans=params]\n"; \
|
||||
printf "example: sans=\"-d domain -d domain\"\n"; \
|
||||
printf "example: sans=\"-d domain -d *.domain\"\n"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
if [ ! -n "$(cn)" ]; then \
|
||||
@@ -39,22 +48,18 @@ gencert:makedir
|
||||
fi
|
||||
|
||||
@echo "generate rsa cert..."
|
||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) --log \
|
||||
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/venafi.sct \
|
||||
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.rsa.pem > $(config)/scts/rsa/wosign.sct"
|
||||
@$(acme.sh) --install-cert -d $(cn) \
|
||||
--key-file $(config)/ssl/domain.rsa.key \
|
||||
--fullchain-file $(config)/ssl/domain.rsa.pem \
|
||||
--reloadcmd "service nginx force-reload"
|
||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) \
|
||||
--renew-hook "$(acme.sh) --install-cert -d $(cn) \
|
||||
--key-file $(config)/ssl/domain.rsa.key \
|
||||
--fullchain-file $(config)/ssl/domain.rsa.pem \
|
||||
--reloadcmd \"service nginx force-reload\""
|
||||
|
||||
@echo "generate ecc cert..."
|
||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 --log \
|
||||
--renew-hook "ct-submit ctlog-gen2.api.venafi.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/venafi.sct \
|
||||
&& ct-submit ctlog.wosign.com < $(config)/ssl/domain.ecc.pem > $(config)/scts/ecc/wosign.sct"
|
||||
@$(acme.sh) --install-cert -d $(cn) --ecc \
|
||||
--key-file $(config)/ssl/domain.ecc.key \
|
||||
--fullchain-file $(config)/ssl/domain.ecc.pem \
|
||||
--reloadcmd "service nginx force-reload"
|
||||
@$(acme.sh) --force --issue --dns dns_ali $(sans) -k ec-256 \
|
||||
--renew-hook "$(acme.sh) --install-cert -d $(cn) --ecc \
|
||||
--key-file $(config)/ssl/domain.ecc.key \
|
||||
--fullchain-file $(config)/ssl/domain.ecc.pem \
|
||||
--reloadcmd \"service nginx force-reload\""
|
||||
|
||||
dhparams:
|
||||
@openssl dhparam -out $(config)/ssl/dhparams.pem 2048
|
||||
@@ -63,7 +68,6 @@ ssticket:
|
||||
@openssl rand 48 > $(config)/ssl/session_ticket.key
|
||||
|
||||
makedir:
|
||||
@mkdir -p $(config)/ssl $(config)/scts/rsa $(config)/scts/ecc
|
||||
|
||||
clean:
|
||||
@mkdir -p $(config)/ssl
|
||||
|
||||
clean:
|
||||
39
README.md
39
README.md
@@ -25,33 +25,36 @@
|
||||
可以容易的看到 [httpsecurityreport](https://httpsecurityreport.com/?report=deepzz.com) 评分`96`,[ssllabs](https://www.ssllabs.com/ssltest/analyze.html?d=deepzz.com&latest) 评分`A+`,[myssl](https://myssl.com/deepzz.com) 评分`A+`,堪称完美。这些安全的相关配置会在后面的部署过程中接触到。
|
||||
|
||||
相关图片展示:
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
> `注`:图片1,图片2是博客界面,图片3是后台界面,图片4是性能展示。
|
||||
> `注`:图片1,图片2是博客界面,图片3是后台界面,图片4是内存占用。
|
||||
|
||||
### 极速体验
|
||||
1. 到 [这里](https://github.com/eiblog/eiblog/releases) 下载对应平台 `.tar.gz` 文件。
|
||||
`eiblog` 默认监听 `:9000` 端口,默认连接 `MongoDB` 地址 `mongodb:27017`,默认连接 `Elasticsearch` 地址 `http://elasticsearch:9200`。
|
||||
|
||||
2. 搭建 `MongoDB`(必须)和 `Elasticsearch`(可选)服务,正式部署需要。
|
||||
1、手动启动执行
|
||||
|
||||
3. 修改 `/etc/hosts` 文件,添加 `MongoDB` 数据库 IP 地址,如:`127.0.0.1 mongodb`。
|
||||
|
||||
4. 执行 `./eiblog`,运行博客系统。看到:
|
||||
```
|
||||
...
|
||||
...
|
||||
[GIN-debug] Listening and serving HTTP on :9000
|
||||
```
|
||||
代表运行成功了。
|
||||
1. 到 [这里](https://github.com/eiblog/eiblog/releases) 下载对应平台 `.tar.gz` 文件。
|
||||
2. 搭建 `MongoDB`(必须)和 `Elasticsearch`(可选)服务,正式部署需要。
|
||||
3. `MongoDB` 服务地址也可通过环境变量指定连接地址如:`export EIBLOG_MGO_ADDR=127.0.0.1:27017`。
|
||||
4. 执行 `./eiblog`,运行博客系统。看到 `...Listening and serving HTTP on :9000` 代表运行成功了。
|
||||
|
||||
默认监听 `HTTP 9000` 端口,后台 `/admin/login`,默认账号密码均为 `deepzz`。更多详细请查阅 [安装部署](https://github.com/eiblog/eiblog/blob/master/docs/install.md) 文档。
|
||||
|
||||
2、Docker 与 Go 环境
|
||||
|
||||
如果你有 `docker` 和 `go` 环境,可直接使用如下命令启动:
|
||||
```
|
||||
$ EIBLOG_MGO_ADDR=127.0.0.1:27017 make run
|
||||
```
|
||||
请提前指定 `mongodb` 的连接地址。该命令会启动一个 `mognodb` 容器,然后编译 `eiblog` 并运行。
|
||||
|
||||
### 特色功能
|
||||
|
||||
作为博主之心血之作,`Eiblog` 实现了什么功能,有什么特点,做了什么优化呢?
|
||||
@@ -73,7 +76,7 @@
|
||||
7. 针对 `disqus` 被墙原因,实现 [Jerry Qu](https://imququ.com) 的另类评论方式,保证评论的流畅。
|
||||
8. 开源 `Typecho` 完整后台系统,全功能 `markdown` 编辑器,让你体验什么是简洁清爽。
|
||||
9. 博客后台直接对接 `七牛 SDK`,实现后台上传文件和删除文件的简单功能。
|
||||
10. 采用 `elasticsearch` 作为站内搜索,添加 `google opensearch` 功能,搜索更加自然。
|
||||
10. 采用 `Elasticsearch` 作为站内搜索,结合 `google opensearch` 功能,搜索更加自然。
|
||||
11. 自动备份数据库数据到七牛云。
|
||||
|
||||
### 文档
|
||||
@@ -86,6 +89,6 @@
|
||||
|
||||
### 成功搭建者博客
|
||||
|
||||
* [https://razeen.me](https://razeen.me) - Razeen's Blog
|
||||
* [https://blog.netcj.com](https://blog.netcj.com) - Razeen's Blog
|
||||
|
||||
如果你的博客使用`Eiblog`搭建,你可以在 [这里](https://github.com/eiblog/eiblog/issues/1) 提交网址。
|
||||
|
||||
34
api.go
34
api.go
@@ -231,12 +231,15 @@ func apiPostAdd(c *gin.Context) {
|
||||
}
|
||||
cid = int(artc.ID)
|
||||
if !artc.IsDraft {
|
||||
// elastic
|
||||
ElasticIndex(artc)
|
||||
// rss
|
||||
DoPings(slug)
|
||||
// disqus
|
||||
ThreadCreate(artc)
|
||||
// 异步执行,快
|
||||
go func() {
|
||||
// elastic
|
||||
ElasticIndex(artc)
|
||||
// rss
|
||||
DoPings(slug)
|
||||
// disqus
|
||||
ThreadCreate(artc)
|
||||
}()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -260,14 +263,17 @@ func apiPostAdd(c *gin.Context) {
|
||||
}
|
||||
if !artc.IsDraft {
|
||||
ReplaceArticle(a, artc)
|
||||
// elastic
|
||||
ElasticIndex(artc)
|
||||
// rss
|
||||
DoPings(slug)
|
||||
// disqus
|
||||
if a == nil {
|
||||
ThreadCreate(artc)
|
||||
}
|
||||
// 异步执行,快
|
||||
go func() {
|
||||
// elastic
|
||||
ElasticIndex(artc)
|
||||
// rss
|
||||
DoPings(slug)
|
||||
// disqus
|
||||
if a == nil {
|
||||
ThreadCreate(artc)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@ disqus:
|
||||
postcreate: https://disqus.com/api/3.0/posts/create.json
|
||||
postapprove: https://disqus.com/api/3.0/posts/approve.json
|
||||
threadcreate: https://disqus.com/api/3.0/threads/create.json
|
||||
# disqus.js 文件名
|
||||
embed: disqus_7d3cf2.js
|
||||
# 获取评论数量间隔
|
||||
interval: 5
|
||||
# 谷歌统计
|
||||
@@ -74,9 +72,9 @@ mode:
|
||||
# twitter地址: twitter.com/chenqijing2
|
||||
twitter:
|
||||
card: summary
|
||||
site: chenqijing2
|
||||
site: deepzz02
|
||||
image: st.deepzz.com/static/img/avatar.jpg
|
||||
address: twitter.com/chenqijing2
|
||||
address: twitter.com/deepzz02
|
||||
|
||||
# 数据初始化操作,可到博客后台修改
|
||||
account:
|
||||
|
||||
@@ -6,40 +6,40 @@ server {
|
||||
|
||||
access_log /data/eiblog/logdata/nginx.log;
|
||||
|
||||
# ip 黑名单
|
||||
# IP黑名单.
|
||||
include /data/eiblog/conf/nginx/ip.blacklist;
|
||||
|
||||
# 现在一般证书是内置的。letsencrypt 暂未
|
||||
# letsencrypt v2已内置, 忽略.
|
||||
# https://imququ.com/post/certificate-transparency.html#toc-2
|
||||
ssl_ct on;
|
||||
#ssl_ct on;
|
||||
#ssl_ct_static_scts /data/eiblog/conf/scts/rsa/;
|
||||
#ssl_ct_static_scts /data/eiblog/conf/scts/ecc/;
|
||||
|
||||
# 中间证书 + 根证书
|
||||
# 中间证书 + 根证书.
|
||||
# https://imququ.com/post/why-can-not-turn-on-ocsp-stapling.html
|
||||
ssl_trusted_certificate /data/eiblog/conf/ssl/full_chained.pem;
|
||||
|
||||
# 站点证书 + 中间证书,私钥
|
||||
# 站点证书 + 中间证书, 私钥.
|
||||
ssl_certificate /data/eiblog/conf/ssl/domain.rsa.pem;
|
||||
ssl_certificate_key /data/eiblog/conf/ssl/domain.rsa.key;
|
||||
ssl_ct_static_scts /data/eiblog/conf/scts/rsa/;
|
||||
# ssl_certificate /data/eiblog/conf/ssl/domain.ecc.pem;
|
||||
# ssl_certificate_key /data/eiblog/conf/ssl/domain.ecc.key;
|
||||
# ssl_ct_static_scts /data/eiblog/conf/scts/ecc/;
|
||||
|
||||
# openssl dhparam -out dhparams.pem 2048
|
||||
# https://weakdh.org/sysadmin.html
|
||||
ssl_dhparam /data/eiblog/conf/ssl/dhparams.pem;
|
||||
|
||||
# 单机部署可以不指定.
|
||||
# openssl rand 48 > session_ticket.key
|
||||
# 单机部署可以不指定 ssl_session_ticket_key
|
||||
# ssl_session_ticket_key /data/eiblog/conf/ssl/session_ticket.key;
|
||||
|
||||
ssl_prefer_server_ciphers on;
|
||||
# https://github.com/cloudflare/sslconfig/blob/master/conf
|
||||
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
|
||||
|
||||
# 如果启用了 RSA + ECDSA 双证书,Cipher Suite 可以参考以下配置:
|
||||
# 如果启用 RSA + ECDSA 双证书, Cipher Suite 可以参考以下配置.
|
||||
# ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
|
||||
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_session_timeout 1d;
|
||||
@@ -48,6 +48,7 @@ server {
|
||||
# ssl stapling
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
resolver 114.114.114.114 8.8.8.8 valid=300s;
|
||||
resolver_timeout 10s;
|
||||
|
||||
@@ -59,7 +60,7 @@ server {
|
||||
rewrite ^/(.*)$ https://deepzz.com/$1 permanent;
|
||||
}
|
||||
|
||||
# webmaster 站点验证相关
|
||||
# webmaster 站点验证相关.
|
||||
location ~* (google4c90d18e696bdcf8\.html|BingSiteAuth\.xml)$ {
|
||||
root /data/eiblog/static;
|
||||
expires 1d;
|
||||
@@ -70,7 +71,7 @@ server {
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
|
||||
|
||||
# deny 将完全不允许页面被嵌套,可能会导致一些异常。如果遇到这样的问题,建议改成 SAMEORIGIN
|
||||
# deny 将完全不允许页面被嵌套,可能会导致一些异常。如果遇到这样的问题,建议改成 SAMEORIGIN.
|
||||
# https://imququ.com/post/web-security-and-response-header.html#toc-1
|
||||
add_header X-Frame-Options deny;
|
||||
add_header X-Powered-By eiblog/1.3.0;
|
||||
@@ -78,7 +79,7 @@ server {
|
||||
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host deepzz.com;
|
||||
proxy_set_header X-Real_IP $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
@@ -90,11 +91,14 @@ server {
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
|
||||
add_header X-Frame-Options deny;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
# 改deepzz相关的
|
||||
# 改deepzz相关的.
|
||||
add_header Content-Security-Policy "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' blob: https:; img-src data: https: https://st.deepzz.com; media-src https://st.deepzz.com; style-src 'unsafe-inline' https:; child-src https:; connect-src 'self' https://translate.googleapis.com; frame-src https://disqus.com https://www.slideshare.net";
|
||||
# 中间证书证书指纹
|
||||
# 中间证书证书指纹, chrome69 将忽略, 用 expect-ct 替代.
|
||||
# https://imququ.com/post/http-public-key-pinning.html
|
||||
add_header Public-Key-Pins 'pin-sha256="IiSbZ4pMDEyXvtl7Lg8K3FNmJcTAhKUTrB2FQOaAO/s="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000;';
|
||||
#add_header Public-Key-Pins 'pin-sha256="IiSbZ4pMDEyXvtl7Lg8K3FNmJcTAhKUTrB2FQOaAO/s="; pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; max-age=2592000;';
|
||||
# 期望 ct.
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT
|
||||
add_header Expect-CT "max-age=180";
|
||||
add_header Cache-Control no-cache;
|
||||
add_header X-Via Aliyun.QingDao;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
@@ -105,7 +109,7 @@ server {
|
||||
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host deepzz.com;
|
||||
proxy_set_header X-Real_IP $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_pass http://127.0.0.1:9000;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
61
db.go
61
db.go
@@ -75,9 +75,9 @@ func init() {
|
||||
logd.Fatal(err)
|
||||
}
|
||||
// 读取帐号信息
|
||||
Ei = loadAccount()
|
||||
loadAccount()
|
||||
// 获取文章数据
|
||||
Ei.Articles = loadArticles()
|
||||
loadArticles()
|
||||
// 生成markdown文档
|
||||
go generateMarkdown()
|
||||
// 启动定时器
|
||||
@@ -87,12 +87,13 @@ func init() {
|
||||
}
|
||||
|
||||
// 读取或初始化帐号信息
|
||||
func loadAccount() (a *Account) {
|
||||
a = &Account{}
|
||||
err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, a)
|
||||
func loadAccount() {
|
||||
Ei = &Account{}
|
||||
err := mgo.FindOne(DB, COLLECTION_ACCOUNT, mgo.M{"username": setting.Conf.Account.Username}, Ei)
|
||||
// 初始化用户数据
|
||||
if err == mgo.ErrNotFound {
|
||||
a = &Account{
|
||||
logd.Printf("Initializing account: %s\n", setting.Conf.Account.Username)
|
||||
Ei = &Account{
|
||||
Username: setting.Conf.Account.Username,
|
||||
Password: EncryptPasswd(setting.Conf.Account.Username, setting.Conf.Account.Password),
|
||||
Email: setting.Conf.Account.Email,
|
||||
@@ -100,29 +101,28 @@ func loadAccount() (a *Account) {
|
||||
Address: setting.Conf.Account.Address,
|
||||
CreateTime: time.Now(),
|
||||
}
|
||||
a.BlogName = setting.Conf.Blogger.BlogName
|
||||
a.SubTitle = setting.Conf.Blogger.SubTitle
|
||||
a.BeiAn = setting.Conf.Blogger.BeiAn
|
||||
a.BTitle = setting.Conf.Blogger.BTitle
|
||||
a.Copyright = setting.Conf.Blogger.Copyright
|
||||
err = mgo.Insert(DB, COLLECTION_ACCOUNT, a)
|
||||
Ei.BlogName = setting.Conf.Blogger.BlogName
|
||||
Ei.SubTitle = setting.Conf.Blogger.SubTitle
|
||||
Ei.BeiAn = setting.Conf.Blogger.BeiAn
|
||||
Ei.BTitle = setting.Conf.Blogger.BTitle
|
||||
Ei.Copyright = setting.Conf.Blogger.Copyright
|
||||
err = mgo.Insert(DB, COLLECTION_ACCOUNT, Ei)
|
||||
generateTopic()
|
||||
} else if err != nil {
|
||||
logd.Fatal(err)
|
||||
}
|
||||
a.CH = make(chan string, 2)
|
||||
a.MapArticles = make(map[string]*Article)
|
||||
a.Tags = make(map[string]SortArticles)
|
||||
return
|
||||
Ei.CH = make(chan string, 2)
|
||||
Ei.MapArticles = make(map[string]*Article)
|
||||
Ei.Tags = make(map[string]SortArticles)
|
||||
}
|
||||
|
||||
func loadArticles() (artcs SortArticles) {
|
||||
err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &artcs)
|
||||
func loadArticles() {
|
||||
err := mgo.FindAll(DB, COLLECTION_ARTICLE, mgo.M{"isdraft": false, "deletetime": mgo.M{"$eq": time.Time{}}}, &Ei.Articles)
|
||||
if err != nil {
|
||||
logd.Fatal(err)
|
||||
}
|
||||
sort.Sort(artcs)
|
||||
for i, v := range artcs {
|
||||
sort.Sort(Ei.Articles)
|
||||
for i, v := range Ei.Articles {
|
||||
// 渲染文章
|
||||
GenerateExcerptAndRender(v)
|
||||
Ei.MapArticles[v.Slug] = v
|
||||
@@ -131,16 +131,15 @@ func loadArticles() (artcs SortArticles) {
|
||||
continue
|
||||
}
|
||||
if i > 0 {
|
||||
v.Prev = artcs[i-1]
|
||||
v.Prev = Ei.Articles[i-1]
|
||||
}
|
||||
if artcs[i+1].ID >= setting.Conf.General.StartID {
|
||||
v.Next = artcs[i+1]
|
||||
if Ei.Articles[i+1].ID >= setting.Conf.General.StartID {
|
||||
v.Next = Ei.Articles[i+1]
|
||||
}
|
||||
upArticle(v, false)
|
||||
}
|
||||
Ei.CH <- SERIES_MD
|
||||
Ei.CH <- ARCHIVE_MD
|
||||
return
|
||||
}
|
||||
|
||||
// generate series,archive markdown
|
||||
@@ -182,7 +181,7 @@ func generateMarkdown() {
|
||||
buffer.WriteString(fmt.Sprintf("\n### %s\n\n", archive.Time.Format("2006 年")))
|
||||
}
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("\n### %s\n\n", archive.Time.Format("2006年2月")))
|
||||
buffer.WriteString(fmt.Sprintf("\n### %s\n\n", archive.Time.Format("2006年1月")))
|
||||
}
|
||||
for i, artc := range archive.Articles {
|
||||
if i == 0 && gt12Month {
|
||||
@@ -209,6 +208,9 @@ func generateTopic() {
|
||||
CreateTime: time.Time{},
|
||||
UpdateTime: time.Time{},
|
||||
}
|
||||
// 推送到 disqus
|
||||
go func() { ThreadCreate(about) }()
|
||||
|
||||
blogroll := &Article{
|
||||
ID: mgo.NextVal(DB, COUNTER_ARTICLE),
|
||||
Author: setting.Conf.Account.Username,
|
||||
@@ -401,20 +403,21 @@ func dropArticle(artc *Article) {
|
||||
|
||||
// 替换文章
|
||||
func ReplaceArticle(oldArtc *Article, newArtc *Article) {
|
||||
Ei.MapArticles[newArtc.Slug] = newArtc
|
||||
GenerateExcerptAndRender(newArtc)
|
||||
if newArtc.ID < setting.Conf.General.StartID {
|
||||
return
|
||||
}
|
||||
if oldArtc != nil {
|
||||
i, artc := GetArticle(oldArtc.ID)
|
||||
DelFromLinkedList(artc)
|
||||
Ei.Articles = append(Ei.Articles[:i], Ei.Articles[i+1:]...)
|
||||
delete(Ei.MapArticles, artc.Slug)
|
||||
|
||||
dropArticle(oldArtc)
|
||||
}
|
||||
|
||||
Ei.MapArticles[newArtc.Slug] = newArtc
|
||||
Ei.Articles = append(Ei.Articles, newArtc)
|
||||
sort.Sort(Ei.Articles)
|
||||
|
||||
GenerateExcerptAndRender(newArtc)
|
||||
AddToLinkedList(newArtc.ID)
|
||||
|
||||
upArticle(newArtc, true)
|
||||
|
||||
109
disqus.go
109
disqus.go
@@ -3,10 +3,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -58,7 +60,7 @@ func PostsCount() error {
|
||||
count++
|
||||
}
|
||||
count = 0
|
||||
resp, err := http.Get(setting.Conf.Disqus.PostsCount + "?" + vals.Encode())
|
||||
resp, err := Get(setting.Conf.Disqus.PostsCount + "?" + vals.Encode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -128,7 +130,7 @@ func PostsList(slug, cursor string) (*postsListResp, error) {
|
||||
vals.Set("cursor", cursor)
|
||||
vals.Set("limit", "50")
|
||||
|
||||
resp, err := http.Get(setting.Conf.Disqus.PostsList + "?" + vals.Encode())
|
||||
resp, err := Get(setting.Conf.Disqus.PostsList + "?" + vals.Encode())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -181,12 +183,8 @@ func PostCreate(pc *PostComment) (*postCreateResp, error) {
|
||||
vals.Set("author_name", pc.AuthorName)
|
||||
// vals.Set("state", "approved")
|
||||
|
||||
request, err := http.NewRequest("POST", setting.Conf.Disqus.PostCreate, strings.NewReader(vals.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Set("Referer", "https://disqus.com")
|
||||
resp, err := http.DefaultClient.Do(request)
|
||||
header := http.Header{"Referer": {"https://disqus.com"}}
|
||||
resp, err := PostWithHeader(setting.Conf.Disqus.PostCreate, vals, header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -225,12 +223,8 @@ func PostApprove(post string) error {
|
||||
vals.Set("access_token", setting.Conf.Disqus.AccessToken)
|
||||
vals.Set("post", post)
|
||||
|
||||
request, err := http.NewRequest("POST", setting.Conf.Disqus.PostApprove, strings.NewReader(vals.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.Header.Set("Referer", "https://disqus.com")
|
||||
resp, err := http.DefaultClient.Do(request)
|
||||
header := http.Header{"Referer": {"https://disqus.com"}}
|
||||
resp, err := PostWithHeader(setting.Conf.Disqus.PostApprove, vals, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -276,7 +270,7 @@ func ThreadCreate(artc *Article) error {
|
||||
urlPath := fmt.Sprintf("https://%s/post/%s.html", setting.Conf.Mode.Domain, artc.Slug)
|
||||
vals.Set("url", urlPath)
|
||||
|
||||
resp, err := http.PostForm(setting.Conf.Disqus.ThreadCreate, vals)
|
||||
resp, err := PostForm(setting.Conf.Disqus.ThreadCreate, vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -299,3 +293,88 @@ func ThreadCreate(artc *Article) error {
|
||||
artc.Thread = result.Response.Id
|
||||
return nil
|
||||
}
|
||||
|
||||
///////////////////////////// HTTP 请求 /////////////////////////////
|
||||
|
||||
var httpClient = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
func newRequest(method, rawurl string, vals url.Values) (*http.Request, error) {
|
||||
u, err := url.Parse(rawurl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host := u.Host
|
||||
// 获取主机IP
|
||||
ips, err := net.LookupHost(u.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
return nil, errors.New("not found ip: " + u.Host)
|
||||
}
|
||||
// 设置ServerName
|
||||
httpClient.Transport.(*http.Transport).TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
u.Host = ips[0]
|
||||
// 创建HTTP Request
|
||||
var req *http.Request
|
||||
if vals != nil {
|
||||
req, err = http.NewRequest(method, u.String(), strings.NewReader(vals.Encode()))
|
||||
} else {
|
||||
req, err = http.NewRequest(method, u.String(), nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 改变Host
|
||||
req.Host = host
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Get HTTP Get请求
|
||||
func Get(rawurl string) (*http.Response, error) {
|
||||
req, err := newRequest(http.MethodGet, rawurl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 发起请求
|
||||
return httpClient.Do(req)
|
||||
}
|
||||
|
||||
// PostForm HTTP Post请求
|
||||
func PostForm(rawurl string, vals url.Values) (*http.Response, error) {
|
||||
req, err := newRequest(http.MethodPost, rawurl, vals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
// 发起请求
|
||||
return httpClient.Do(req)
|
||||
}
|
||||
|
||||
// PostWithHeader HTTP Post请求,自定义Header
|
||||
func PostWithHeader(rawurl string, vals url.Values, header http.Header) (*http.Response, error) {
|
||||
req, err := newRequest(http.MethodPost, rawurl, vals)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// set header
|
||||
req.Header = header
|
||||
// 发起请求
|
||||
return httpClient.Do(req)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ services:
|
||||
volumes:
|
||||
- /data/eiblog/mgodb:/data/db
|
||||
restart: always
|
||||
ports:
|
||||
- 27017:27017
|
||||
elasticsearch:
|
||||
image: elasticsearch:2.4.1
|
||||
container_name: eisearch
|
||||
|
||||
1
docs/_config.yml
Normal file
1
docs/_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-cayman
|
||||
@@ -18,3 +18,7 @@ twitter:
|
||||

|
||||
|
||||
可以看到`,`之前是没有内容的,该内容是我们文章的描述。
|
||||
|
||||
### Google OpenSearch
|
||||
在 Chrome 浏览器上,你可以在输入网站后按 TAB 键进入搜索模式,如:
|
||||

|
||||
|
||||
@@ -86,14 +86,13 @@ $ docker run -d --name eisearch \
|
||||
#### 文件准备
|
||||
博主是一个有强迫症的人,一些文件的路径我使用了固定的路径,请大家见谅。假如你的 cdn 域名为 `st.example.com`,你需要确定这些文件已经在你的 cdn 中,它们路径分别是:
|
||||
|
||||
| 文件 | 地址 | 描述 |
|
||||
| ------------------ | ---------------------------------------- | ---------------------------------------- |
|
||||
| favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 |
|
||||
| bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 |
|
||||
| avatar.jpg | st.example.com/static/img/avatar.jpg | 头像 |
|
||||
| blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) |
|
||||
| 文件 | 地址 | 描述 |
|
||||
| ------------------ | -------------------------------------------- | ------------------------------------------------------------ |
|
||||
| favicon.ico | st.example.com/static/img/favicon.ico | cdn 中的文件名为 `static/img/favicon.ico`。你也可以复制 favicon.ico 到 static 文件夹下,通过 example.com/favicon.ico 也是能够访问到。docker 用户可能需要重新打包镜像。 |
|
||||
| bg04.jpg | st.example.com/static/img/bg04.jpg | 首页左侧的大背景图,需要更名请到 views/st_blog.css 修改。 |
|
||||
| avatar.png | st.example.com/static/img/avatar.png | 头像 |
|
||||
| blank.gif | st.example.com/static/img/blank.gif | 空白图片,[下载](https://st.deepzz.com/static/img/blank.gif) |
|
||||
| default_avatar.png | st.example.com/static/img/default_avatar.png | disqus 默认图片,[下载](https://st.deepzz.com/static/img/default_avatar.png) |
|
||||
| disqus.js | st.example.com/static/js/disqus_xxx.js | disqus 文件,你可以通过 https://short_name.disqus.com/embed.js 下载你的专属文件,并上传到七牛。更新配置文件 app.yml。 |
|
||||
|
||||
> 注意,cdn 提到的文件下载,请复制链接进行下载,因为博主使用了防盗链功能,还有:
|
||||
1、每次修改 app.yml 文件(如:更换 cdn 域名或更新头像),如果你不知道是否应该提高 staticversion 一个版本,那么最好提高一个 +1。
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -23,10 +24,20 @@ const (
|
||||
ES_DATE = `{"range":{"date":{"gte":"%s","lte": "%s","format": "yyyy-MM-dd||yyyy-MM||yyyy"}}}` // 2016-10||/M
|
||||
)
|
||||
|
||||
var es *ElasticService
|
||||
var (
|
||||
ErrUninitializedES = errors.New("uninitialized elasticsearch")
|
||||
|
||||
es *ElasticService
|
||||
)
|
||||
|
||||
// 初始化 Elasticsearch 服务器
|
||||
func init() {
|
||||
_, err := net.LookupIP("elasticsearch")
|
||||
if err != nil {
|
||||
logd.Info(err)
|
||||
return
|
||||
}
|
||||
|
||||
es = &ElasticService{url: "http://elasticsearch:9200", c: new(http.Client)}
|
||||
initIndex()
|
||||
}
|
||||
@@ -41,7 +52,11 @@ func initIndex() {
|
||||
}
|
||||
|
||||
// 查询
|
||||
func Elasticsearch(qStr string, size, from int) *ESSearchResult {
|
||||
func Elasticsearch(qStr string, size, from int) (*ESSearchResult, error) {
|
||||
if es == nil {
|
||||
return nil, ErrUninitializedES
|
||||
}
|
||||
|
||||
// 分析查询字符串
|
||||
reg := regexp.MustCompile(`(tag|slug|date):`)
|
||||
indexs := reg.FindAllStringIndex(qStr, -1)
|
||||
@@ -92,14 +107,17 @@ func Elasticsearch(qStr string, size, from int) *ESSearchResult {
|
||||
}
|
||||
docs, err := IndexQueryDSL(INDEX, TYPE, size, from, []byte(dsl))
|
||||
if err != nil {
|
||||
logd.Error(err)
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
return docs
|
||||
return docs, nil
|
||||
}
|
||||
|
||||
// 添加或更新索引
|
||||
func ElasticIndex(artc *Article) error {
|
||||
if es == nil {
|
||||
return ErrUninitializedES
|
||||
}
|
||||
|
||||
img := PickFirstImage(artc.Content)
|
||||
mapping := map[string]interface{}{
|
||||
"title": artc.Title,
|
||||
@@ -115,6 +133,10 @@ func ElasticIndex(artc *Article) error {
|
||||
|
||||
// 删除索引
|
||||
func ElasticDelIndex(ids []int32) error {
|
||||
if es == nil {
|
||||
return ErrUninitializedES
|
||||
}
|
||||
|
||||
var target []string
|
||||
for _, id := range ids {
|
||||
target = append(target, fmt.Sprint(id))
|
||||
|
||||
9
front.go
9
front.go
@@ -199,10 +199,12 @@ func HandleSearchPage(c *gin.Context) {
|
||||
start = 1
|
||||
}
|
||||
h["Word"] = q
|
||||
var result *ESSearchResult
|
||||
|
||||
vals := c.Request.URL.Query()
|
||||
result = Elasticsearch(q, setting.Conf.General.PageNum, start-1)
|
||||
if result != nil {
|
||||
result, err := Elasticsearch(q, setting.Conf.General.PageNum, start-1)
|
||||
if err != nil {
|
||||
logd.Error(err)
|
||||
} else {
|
||||
result.Took /= 1000
|
||||
for i, v := range result.Hits.Hits {
|
||||
if artc := Ei.MapArticles[result.Hits.Hits[i].Source.Slug]; len(v.Highlight.Content) == 0 && artc != nil {
|
||||
@@ -401,6 +403,7 @@ func HandleDisqusCreate(c *gin.Context) {
|
||||
resp.ErrMsg = "参数错误"
|
||||
return
|
||||
}
|
||||
fmt.Println("disqus: author: ", email)
|
||||
pc := &PostComment{
|
||||
Message: msg,
|
||||
Parent: c.PostForm("parent"),
|
||||
|
||||
105
glide.lock
generated
105
glide.lock
generated
@@ -1,105 +0,0 @@
|
||||
hash: c733fa4abeda21b59b001578b37a168bd33038d337b61198cc5fd94be8bfdf77
|
||||
updated: 2018-01-13T18:22:28.620808+08:00
|
||||
imports:
|
||||
- name: github.com/boj/redistore
|
||||
version: 4562487a4bee9a7c272b72bfaeda4917d0a47ab9
|
||||
- name: github.com/deepzz0/logd
|
||||
version: f91dd8c6316f0e156e93895a96739b67577b6a63
|
||||
- name: github.com/eiblog/blackfriday
|
||||
version: c0ec111761ae784fe31cc076f2fa0e2d2216d623
|
||||
- name: github.com/eiblog/utils
|
||||
version: e8f16268dae939f920ddc55f1c9e46a97a5e3559
|
||||
subpackages:
|
||||
- logd
|
||||
- mgo
|
||||
- tmpl
|
||||
- uuid
|
||||
- name: github.com/garyburd/redigo
|
||||
version: d1ed5c67e5794de818ea85e6b522fda02623a484
|
||||
subpackages:
|
||||
- internal
|
||||
- redis
|
||||
- name: github.com/gin-gonic/autotls
|
||||
version: 8ca25fbde72bb72a00466215b94b489c71fcb815
|
||||
- name: github.com/gin-gonic/contrib
|
||||
version: 88aede40372d4bcb11e45168a8c30d99e44cf617
|
||||
subpackages:
|
||||
- sessions
|
||||
- name: github.com/gin-gonic/gin
|
||||
version: e2212d40c62a98b388a5eb48ecbdcf88534688ba
|
||||
subpackages:
|
||||
- binding
|
||||
- render
|
||||
- name: github.com/golang/protobuf
|
||||
version: 2402d76f3d41f928c7902a765dfc872356dd3aad
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gorilla/context
|
||||
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
|
||||
- name: github.com/gorilla/securecookie
|
||||
version: e59506cc896acb7f7bf732d4fdf5e25f7ccd8983
|
||||
- name: github.com/gorilla/sessions
|
||||
version: a3acf13e802c358d65f249324d14ed24aac11370
|
||||
- name: github.com/manucorporat/sse
|
||||
version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: 6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c
|
||||
- name: github.com/qiniu/api.v7
|
||||
version: b7c7d6a2ce0aff8e5e7d14c39c3cde867efa1123
|
||||
subpackages:
|
||||
- auth/qbox
|
||||
- conf
|
||||
- storage
|
||||
- name: github.com/qiniu/x
|
||||
version: f512abcf45ab4e2ba0fd4784c57b53d495997d66
|
||||
subpackages:
|
||||
- bytes.v7
|
||||
- bytes.v7/seekable
|
||||
- ctype.v7
|
||||
- rpc.v7
|
||||
- xlog.v7
|
||||
- name: github.com/shurcooL/sanitized_anchor_name
|
||||
version: 86672fcb3f950f35f2e675df2240550f2a50762f
|
||||
- name: golang.org/x/crypto
|
||||
version: 13931e22f9e72ea58bb73048bc752b48c6d4d4ac
|
||||
subpackages:
|
||||
- acme
|
||||
- acme/autocert
|
||||
- name: golang.org/x/net
|
||||
version: f315505cf3349909cdf013ea56690da34e96a451
|
||||
subpackages:
|
||||
- context
|
||||
- name: golang.org/x/sys
|
||||
version: 810d7000345868fc619eb81f46307107118f4ae1
|
||||
subpackages:
|
||||
- unix
|
||||
- name: gopkg.in/go-playground/validator.v8
|
||||
version: c193cecd124b5cc722d7ee5538e945bdb3348435
|
||||
- name: gopkg.in/mgo.v2
|
||||
version: 3f83fa5005286a7fe593b055f0d7771a7dce4655
|
||||
subpackages:
|
||||
- bson
|
||||
- internal/json
|
||||
- internal/sasl
|
||||
- internal/scram
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: d670f9405373e636a5a2765eea47fac0c9bc91a4
|
||||
- name: qiniupkg.com/x
|
||||
version: 946c4a16076d6d98aeb78619e2bd4012357f7228
|
||||
subpackages:
|
||||
- bytes.v7
|
||||
- log.v7
|
||||
- reqid.v7
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/stretchr/testify
|
||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||
subpackages:
|
||||
- assert
|
||||
27
glide.yaml
27
glide.yaml
@@ -1,27 +0,0 @@
|
||||
package: github.com/eiblog/eiblog
|
||||
import:
|
||||
- package: github.com/deepzz0/logd
|
||||
- package: github.com/eiblog/blackfriday
|
||||
- package: github.com/eiblog/utils
|
||||
subpackages:
|
||||
- logd
|
||||
- mgo
|
||||
- tmpl
|
||||
- uuid
|
||||
- package: github.com/gin-gonic/autotls
|
||||
- package: github.com/gin-gonic/contrib
|
||||
subpackages:
|
||||
- sessions
|
||||
- package: github.com/gin-gonic/gin
|
||||
- package: github.com/qiniu/api.v7
|
||||
subpackages:
|
||||
- auth/qbox
|
||||
- storage
|
||||
- package: gopkg.in/mgo.v2
|
||||
subpackages:
|
||||
- bson
|
||||
- package: gopkg.in/yaml.v2
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
||||
28
go.mod
Normal file
28
go.mod
Normal file
@@ -0,0 +1,28 @@
|
||||
module github.com/eiblog/eiblog
|
||||
|
||||
require (
|
||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
|
||||
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f
|
||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae
|
||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b
|
||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134
|
||||
github.com/gin-gonic/gin v1.3.0
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/json-iterator/go v1.1.10 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/qiniu/api.v7/v7 v7.8.0
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/ugorji/go v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 // indirect
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
)
|
||||
|
||||
go 1.13
|
||||
99
go.sum
Normal file
99
go.sum
Normal file
@@ -0,0 +1,99 @@
|
||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
|
||||
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f h1:hjWy8ptp0ggYgv/3A8dixSB9KTRgDcZH2D3Ap5hrwOs=
|
||||
github.com/deepzz0/logd v0.0.0-20171206094927-f91dd8c6316f/go.mod h1:8jMj6ab9czIU5udq3ovaK9/5sCIyQ1JWteFMn8w2QRI=
|
||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae h1:V6YC640Gs5jEUYfCimyuXsTW5gzNcIEESG4MGmOJCtA=
|
||||
github.com/eiblog/blackfriday v0.0.0-20161010144836-c0ec111761ae/go.mod h1:HzHqTCGEAkSSzBM3shBvQHsHRQYUvjNOIC4mHipZ6tI=
|
||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753 h1:Nygjtnh1nF5zejJF7pZnsoFh77wOPS+jlfhikjkJg60=
|
||||
github.com/eiblog/utils v0.0.0-20181119015747-92c93e218753/go.mod h1:mZHWnipRp41yw/rti2DgpbMiBE5i6ifqg7PKooEwRh4=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8Jc6zMvyRz3PCQrTTCXnVRvEzyBcM890=
|
||||
github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
|
||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134 h1:xgqFZVwmmtWiuq5LUZ/wa34hJR2Dm9NZAH+Cj9a7Hu0=
|
||||
github.com/gin-gonic/contrib v0.0.0-20180614032058-39cfb9727134/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
|
||||
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
|
||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qiniu/api.v7/v7 v7.8.0 h1:Ye9sHXwCpeDgKJ4BNSoDvXe4yEuU8a/HTT1jKRgkqe8=
|
||||
github.com/qiniu/api.v7/v7 v7.8.0/go.mod h1:J7pD9UsnxO7XxyRLUHpsWEQd/HgWJNwnn/Za9qEPdEA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/ugorji/go v1.2.2 h1:60ZHIOcsJlo3bJm9CbTVu7OSqT2mxaEmyQbK2NwCkn0=
|
||||
github.com/ugorji/go v1.2.2/go.mod h1:bitgyERdV7L7Db/Z5gfd5v2NQMNhhiFiZwpgMw2SP7k=
|
||||
github.com/ugorji/go/codec v1.2.2 h1:08Gah8d+dXj4cZNUHhtuD/S4PXD5WpVbj5B8/ClELAQ=
|
||||
github.com/ugorji/go/codec v1.2.2/go.mod h1:OM8g7OAy52uYl3Yk+RE/3AS1nXFn1Wh4PPLtupCxbuU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -125,7 +125,7 @@ func ConvertStr(str string) string {
|
||||
} else if h := d*24 + h2 - h1; h > 1 || (h == 1 && mi2-mi1 >= 0) {
|
||||
return fmt.Sprintf(HOURS_AGO, h)
|
||||
} else if mi := h*60 + mi2 - mi1; mi > 1 || (mi == 1 && s2-s1 >= 0) {
|
||||
return fmt.Sprintf(MINUTES_AGO, m)
|
||||
return fmt.Sprintf(MINUTES_AGO, mi)
|
||||
}
|
||||
return JUST_NOW
|
||||
}
|
||||
|
||||
@@ -44,15 +44,21 @@ func TestPickFirstImage(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCovertStr(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
testStr := []string{
|
||||
time.Now().Format("2006-01-02T15:04:05"),
|
||||
now.Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Second * 20).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Minute).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Minute * 2).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Minute * 20).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Hour).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Hour * 2).Format("2006-01-02T15:04:05"),
|
||||
now.Add(-time.Hour * 24).Format("2006-01-02T15:04:05"),
|
||||
}
|
||||
|
||||
expectStr := []string{
|
||||
JUST_NOW,
|
||||
}
|
||||
|
||||
for i, v := range testStr {
|
||||
assert.Equal(t, expectStr[i], ConvertStr(v))
|
||||
time.Sleep(time.Second)
|
||||
t.Log(now.Format("2006-01-02T15:04:05"))
|
||||
for _, v := range testStr {
|
||||
t.Log(v, ConvertStr(v))
|
||||
}
|
||||
}
|
||||
|
||||
11
qiniu.go
11
qiniu.go
@@ -1,14 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/eiblog/eiblog/setting"
|
||||
"github.com/qiniu/api.v7/auth/qbox"
|
||||
"github.com/qiniu/api.v7/storage"
|
||||
"github.com/qiniu/api.v7/v7/auth/qbox"
|
||||
"github.com/qiniu/api.v7/v7/storage"
|
||||
)
|
||||
|
||||
// 进度条
|
||||
@@ -48,7 +49,7 @@ func FileUpload(name string, size int64, data io.Reader) (string, error) {
|
||||
ret := new(storage.PutRet)
|
||||
putExtra := &storage.PutExtra{}
|
||||
|
||||
err := uploader.Put(nil, ret, upToken, key, data, size, putExtra)
|
||||
err := uploader.Put(context.Background(), ret, upToken, key, data, size, putExtra)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func getKey(name string) string {
|
||||
ext := filepath.Ext(name)
|
||||
var key string
|
||||
switch ext {
|
||||
case ".bmp", ".png", ".jpg", ".gif", ".ico":
|
||||
case ".bmp", ".png", ".jpg", ".gif", ".ico", ".jpeg":
|
||||
key = "blog/img/" + name
|
||||
case ".mov", ".mp4":
|
||||
key = "blog/video/" + name
|
||||
@@ -95,6 +96,8 @@ func getKey(name string) string {
|
||||
key = "blog/document/" + name
|
||||
case ".zip", ".rar", ".tar", ".gz":
|
||||
key = "blog/archive/" + name
|
||||
default:
|
||||
key = "blog/other/" + name
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
10
router.go
10
router.go
@@ -2,6 +2,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"text/template"
|
||||
"time"
|
||||
@@ -27,7 +28,12 @@ func init() {
|
||||
}
|
||||
|
||||
router = gin.Default()
|
||||
store := sessions.NewCookieStore([]byte("eiblog321"))
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
logd.Fatal(err)
|
||||
}
|
||||
store := sessions.NewCookieStore(b)
|
||||
store.Options(sessions.Options{
|
||||
MaxAge: 86400 * 7,
|
||||
Path: "/",
|
||||
@@ -43,7 +49,7 @@ func init() {
|
||||
}
|
||||
return false
|
||||
})
|
||||
_, err := Tmpl.ParseFiles(files...)
|
||||
_, err = Tmpl.ParseFiles(files...)
|
||||
if err != nil {
|
||||
logd.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ type Config struct {
|
||||
PostCreate string
|
||||
PostApprove string
|
||||
ThreadCreate string
|
||||
Embed string
|
||||
Interval int
|
||||
}
|
||||
Google struct { // 谷歌统计
|
||||
|
||||
25
vendor/github.com/boj/redistore/.travis.yml
generated
vendored
Normal file
25
vendor/github.com/boj/redistore/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
language: go
|
||||
sudo: false
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.5
|
||||
- go: 1.6
|
||||
- go: 1.7
|
||||
- go: 1.8
|
||||
- go: 1.9
|
||||
- go: tip
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
install:
|
||||
- # skip
|
||||
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- go tool vet .
|
||||
- go test -v -race ./...
|
||||
5
vendor/github.com/boj/redistore/README.md
generated
vendored
5
vendor/github.com/boj/redistore/README.md
generated
vendored
@@ -1,12 +1,13 @@
|
||||
# redistore
|
||||
|
||||
[](https://drone.io/github.com/boj/redistore/latest)
|
||||
[](https://godoc.org/github.com/boj/redistore)
|
||||
[](https://travis-ci.org/boj/redistore)
|
||||
|
||||
A session store backend for [gorilla/sessions](http://www.gorillatoolkit.org/pkg/sessions) - [src](https://github.com/gorilla/sessions).
|
||||
|
||||
## Requirements
|
||||
|
||||
Depends on the [Redigo](https://github.com/garyburd/redigo) Redis library.
|
||||
Depends on the [Redigo](https://github.com/gomodule/redigo) Redis library.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
7
vendor/github.com/boj/redistore/go.mod
generated
vendored
Normal file
7
vendor/github.com/boj/redistore/go.mod
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
module github.com/boj/redistore
|
||||
|
||||
require (
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.1.1
|
||||
)
|
||||
4
vendor/github.com/boj/redistore/redistore.go
generated
vendored
4
vendor/github.com/boj/redistore/redistore.go
generated
vendored
@@ -15,7 +15,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
@@ -252,7 +252,7 @@ func (s *RediStore) New(r *http.Request, name string) (*sessions.Session, error)
|
||||
// Save adds a single session to the response.
|
||||
func (s *RediStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
|
||||
// Marked for deletion.
|
||||
if session.Options.MaxAge < 0 {
|
||||
if session.Options.MaxAge <= 0 {
|
||||
if err := s.delete(session); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
404
vendor/github.com/boj/redistore/redistore_test.go
generated
vendored
404
vendor/github.com/boj/redistore/redistore_test.go
generated
vendored
@@ -1,404 +0,0 @@
|
||||
package redistore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/gob"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// ResponseRecorder
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// ResponseRecorder is an implementation of http.ResponseWriter that
|
||||
// records its mutations for later inspection in tests.
|
||||
type ResponseRecorder struct {
|
||||
Code int // the HTTP response code from WriteHeader
|
||||
HeaderMap http.Header // the HTTP response headers
|
||||
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
||||
Flushed bool
|
||||
}
|
||||
|
||||
// NewRecorder returns an initialized ResponseRecorder.
|
||||
func NewRecorder() *ResponseRecorder {
|
||||
return &ResponseRecorder{
|
||||
HeaderMap: make(http.Header),
|
||||
Body: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRemoteAddr is the default remote address to return in RemoteAddr if
|
||||
// an explicit DefaultRemoteAddr isn't set on ResponseRecorder.
|
||||
const DefaultRemoteAddr = "1.2.3.4"
|
||||
|
||||
// Header returns the response headers.
|
||||
func (rw *ResponseRecorder) Header() http.Header {
|
||||
return rw.HeaderMap
|
||||
}
|
||||
|
||||
// Write always succeeds and writes to rw.Body, if not nil.
|
||||
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
|
||||
if rw.Body != nil {
|
||||
rw.Body.Write(buf)
|
||||
}
|
||||
if rw.Code == 0 {
|
||||
rw.Code = http.StatusOK
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// WriteHeader sets rw.Code.
|
||||
func (rw *ResponseRecorder) WriteHeader(code int) {
|
||||
rw.Code = code
|
||||
}
|
||||
|
||||
// Flush sets rw.Flushed to true.
|
||||
func (rw *ResponseRecorder) Flush() {
|
||||
rw.Flushed = true
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type FlashMessage struct {
|
||||
Type int
|
||||
Message string
|
||||
}
|
||||
|
||||
func TestRediStore(t *testing.T) {
|
||||
var req *http.Request
|
||||
var rsp *ResponseRecorder
|
||||
var hdr http.Header
|
||||
var err error
|
||||
var ok bool
|
||||
var cookies []string
|
||||
var session *sessions.Session
|
||||
var flashes []interface{}
|
||||
|
||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Round 1 ----------------------------------------------------------------
|
||||
|
||||
// RedisStore
|
||||
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
rsp = NewRecorder()
|
||||
// Get a session.
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Get a flash.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected empty flashes; Got %v", flashes)
|
||||
}
|
||||
// Add some flashes.
|
||||
session.AddFlash("foo")
|
||||
session.AddFlash("bar")
|
||||
// Custom key.
|
||||
session.AddFlash("baz", "custom_key")
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
hdr = rsp.Header()
|
||||
cookies, ok = hdr["Set-Cookie"]
|
||||
if !ok || len(cookies) != 1 {
|
||||
t.Fatalf("No cookies. Header:", hdr)
|
||||
}
|
||||
|
||||
// Round 2 ----------------------------------------------------------------
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
req.Header.Add("Cookie", cookies[0])
|
||||
rsp = NewRecorder()
|
||||
// Get a session.
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Check all saved values.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 2 {
|
||||
t.Fatalf("Expected flashes; Got %v", flashes)
|
||||
}
|
||||
if flashes[0] != "foo" || flashes[1] != "bar" {
|
||||
t.Errorf("Expected foo,bar; Got %v", flashes)
|
||||
}
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected dumped flashes; Got %v", flashes)
|
||||
}
|
||||
// Custom key.
|
||||
flashes = session.Flashes("custom_key")
|
||||
if len(flashes) != 1 {
|
||||
t.Errorf("Expected flashes; Got %v", flashes)
|
||||
} else if flashes[0] != "baz" {
|
||||
t.Errorf("Expected baz; Got %v", flashes)
|
||||
}
|
||||
flashes = session.Flashes("custom_key")
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected dumped flashes; Got %v", flashes)
|
||||
}
|
||||
|
||||
// RediStore specific
|
||||
// Set MaxAge to -1 to mark for deletion.
|
||||
session.Options.MaxAge = -1
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
|
||||
// Round 3 ----------------------------------------------------------------
|
||||
// Custom type
|
||||
|
||||
// RedisStore
|
||||
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
rsp = NewRecorder()
|
||||
// Get a session.
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Get a flash.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected empty flashes; Got %v", flashes)
|
||||
}
|
||||
// Add some flashes.
|
||||
session.AddFlash(&FlashMessage{42, "foo"})
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
hdr = rsp.Header()
|
||||
cookies, ok = hdr["Set-Cookie"]
|
||||
if !ok || len(cookies) != 1 {
|
||||
t.Fatalf("No cookies. Header:", hdr)
|
||||
}
|
||||
|
||||
// Round 4 ----------------------------------------------------------------
|
||||
// Custom type
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
req.Header.Add("Cookie", cookies[0])
|
||||
rsp = NewRecorder()
|
||||
// Get a session.
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Check all saved values.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 1 {
|
||||
t.Fatalf("Expected flashes; Got %v", flashes)
|
||||
}
|
||||
custom := flashes[0].(FlashMessage)
|
||||
if custom.Type != 42 || custom.Message != "foo" {
|
||||
t.Errorf("Expected %#v, got %#v", FlashMessage{42, "foo"}, custom)
|
||||
}
|
||||
|
||||
// RediStore specific
|
||||
// Set MaxAge to -1 to mark for deletion.
|
||||
session.Options.MaxAge = -1
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
|
||||
// Round 5 ----------------------------------------------------------------
|
||||
// RediStore Delete session (deprecated)
|
||||
|
||||
//req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
//req.Header.Add("Cookie", cookies[0])
|
||||
//rsp = NewRecorder()
|
||||
//// Get a session.
|
||||
//if session, err = store.Get(req, "session-key"); err != nil {
|
||||
// t.Fatalf("Error getting session: %v", err)
|
||||
//}
|
||||
//// Delete session.
|
||||
//if err = store.Delete(req, rsp, session); err != nil {
|
||||
// t.Fatalf("Error deleting session: %v", err)
|
||||
//}
|
||||
//// Get a flash.
|
||||
//flashes = session.Flashes()
|
||||
//if len(flashes) != 0 {
|
||||
// t.Errorf("Expected empty flashes; Got %v", flashes)
|
||||
//}
|
||||
//hdr = rsp.Header()
|
||||
//cookies, ok = hdr["Set-Cookie"]
|
||||
//if !ok || len(cookies) != 1 {
|
||||
// t.Fatalf("No cookies. Header:", hdr)
|
||||
//}
|
||||
|
||||
// Round 6 ----------------------------------------------------------------
|
||||
// RediStore change MaxLength of session
|
||||
|
||||
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
req, err = http.NewRequest("GET", "http://www.example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create request", err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
session, err = store.New(req, "my session")
|
||||
session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2))
|
||||
err = session.Save(req, w)
|
||||
if err == nil {
|
||||
t.Fatal("expected an error, got nil")
|
||||
}
|
||||
|
||||
store.SetMaxLength(4096 * 3) // A bit more than the value size to account for encoding overhead.
|
||||
err = session.Save(req, w)
|
||||
if err != nil {
|
||||
t.Fatal("failed to Save:", err)
|
||||
}
|
||||
|
||||
// Round 7 ----------------------------------------------------------------
|
||||
|
||||
// RedisStoreWithDB
|
||||
store, err = NewRediStoreWithDB(10, "tcp", ":6379", "", "1", []byte("secret-key"))
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
rsp = NewRecorder()
|
||||
// Get a session. Using the same key as previously, but on different DB
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Get a flash.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected empty flashes; Got %v", flashes)
|
||||
}
|
||||
// Add some flashes.
|
||||
session.AddFlash("foo")
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
hdr = rsp.Header()
|
||||
cookies, ok = hdr["Set-Cookie"]
|
||||
if !ok || len(cookies) != 1 {
|
||||
t.Fatalf("No cookies. Header:", hdr)
|
||||
}
|
||||
|
||||
// Get a session.
|
||||
req.Header.Add("Cookie", cookies[0])
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Check all saved values.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 1 {
|
||||
t.Fatalf("Expected flashes; Got %v", flashes)
|
||||
}
|
||||
if flashes[0] != "foo" {
|
||||
t.Errorf("Expected foo,bar; Got %v", flashes)
|
||||
}
|
||||
|
||||
// Round 8 ----------------------------------------------------------------
|
||||
// JSONSerializer
|
||||
|
||||
// RedisStore
|
||||
store, err = NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
store.SetSerializer(JSONSerializer{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://localhost:8080/", nil)
|
||||
rsp = NewRecorder()
|
||||
// Get a session.
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Get a flash.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 0 {
|
||||
t.Errorf("Expected empty flashes; Got %v", flashes)
|
||||
}
|
||||
// Add some flashes.
|
||||
session.AddFlash("foo")
|
||||
// Save.
|
||||
if err = sessions.Save(req, rsp); err != nil {
|
||||
t.Fatalf("Error saving session: %v", err)
|
||||
}
|
||||
hdr = rsp.Header()
|
||||
cookies, ok = hdr["Set-Cookie"]
|
||||
if !ok || len(cookies) != 1 {
|
||||
t.Fatalf("No cookies. Header:", hdr)
|
||||
}
|
||||
|
||||
// Get a session.
|
||||
req.Header.Add("Cookie", cookies[0])
|
||||
if session, err = store.Get(req, "session-key"); err != nil {
|
||||
t.Fatalf("Error getting session: %v", err)
|
||||
}
|
||||
// Check all saved values.
|
||||
flashes = session.Flashes()
|
||||
if len(flashes) != 1 {
|
||||
t.Fatalf("Expected flashes; Got %v", flashes)
|
||||
}
|
||||
if flashes[0] != "foo" {
|
||||
t.Errorf("Expected foo,bar; Got %v", flashes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPingGoodPort(t *testing.T) {
|
||||
store, _ := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
defer store.Close()
|
||||
ok, err := store.ping()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
if !ok {
|
||||
t.Error("Expected server to PONG")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPingBadPort(t *testing.T) {
|
||||
store, _ := NewRediStore(10, "tcp", ":6378", "", []byte("secret-key"))
|
||||
defer store.Close()
|
||||
_, err := store.ping()
|
||||
if err == nil {
|
||||
t.Error("Expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRediStore() {
|
||||
// RedisStore
|
||||
store, err := NewRediStore(10, "tcp", ":6379", "", []byte("secret-key"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer store.Close()
|
||||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(FlashMessage{})
|
||||
}
|
||||
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
@@ -1,22 +0,0 @@
|
||||
# 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
|
||||
11
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
11
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
@@ -1,11 +0,0 @@
|
||||
language: go
|
||||
go: 1.2
|
||||
install:
|
||||
- go get -v code.google.com/p/go.tools/cmd/cover
|
||||
script:
|
||||
- go test -v -tags=disableunsafe ./spew
|
||||
- go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
|
||||
after_success:
|
||||
- go get -v github.com/mattn/goveralls
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
6
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
6
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
@@ -1,6 +1,8 @@
|
||||
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||
ISC License
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
|
||||
194
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
194
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
@@ -1,194 +0,0 @@
|
||||
go-spew
|
||||
=======
|
||||
|
||||
[]
|
||||
(https://travis-ci.org/davecgh/go-spew) [![Coverage Status]
|
||||
(https://coveralls.io/repos/davecgh/go-spew/badge.png?branch=master)]
|
||||
(https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||
|
||||
Go-spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
||||
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
|
||||
report. Go-spew is licensed under the liberal ISC license, so it may be used in
|
||||
open source or commercial projects.
|
||||
|
||||
If you're interested in reading about how this package came to life and some
|
||||
of the challenges involved in providing a deep pretty printer, there is a blog
|
||||
post about it
|
||||
[here](https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
|
||||
|
||||
## Documentation
|
||||
|
||||
[]
|
||||
(http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||
|
||||
Full `go doc` style documentation for the project can be viewed online without
|
||||
installing this package by using the excellent GoDoc site here:
|
||||
http://godoc.org/github.com/davecgh/go-spew/spew
|
||||
|
||||
You can also view the documentation locally once the package is installed with
|
||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
||||
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/davecgh/go-spew/spew
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Add this import line to the file you're working in:
|
||||
|
||||
```Go
|
||||
import "github.com/davecgh/go-spew/spew"
|
||||
```
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
|
||||
```Go
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
```
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
|
||||
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
|
||||
and pointer addresses):
|
||||
|
||||
```Go
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
```
|
||||
|
||||
## Debugging a Web Application Example
|
||||
|
||||
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
|
||||
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Dump Output
|
||||
|
||||
```
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) {
|
||||
(string) "one": (bool) true
|
||||
}
|
||||
}
|
||||
([]uint8) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
```
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
```
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
```
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available via the
|
||||
spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
```
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables. This option
|
||||
relies on access to the unsafe package, so it will not have any effect when
|
||||
running in environments without access to the unsafe package such as Google
|
||||
App Engine or with the "disableunsafe" build tag specified.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are supported,
|
||||
with other types sorted according to the reflect.Value.String() output
|
||||
which guarantees display stability. Natural map order is used by
|
||||
default.
|
||||
|
||||
* SpewKeys
|
||||
SpewKeys specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only considered
|
||||
if SortKeys is true.
|
||||
|
||||
```
|
||||
|
||||
## Unsafe Package Dependency
|
||||
|
||||
This package relies on the unsafe package to perform some of the more advanced
|
||||
features, however it also supports a "limited" mode which allows it to work in
|
||||
environments where the unsafe package is not available. By default, it will
|
||||
operate in this mode on Google App Engine. The "disableunsafe" build tag may
|
||||
also be specified to force the package to build without using the unsafe
|
||||
package.
|
||||
|
||||
## License
|
||||
|
||||
Go-spew is licensed under the liberal ISC License.
|
||||
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script uses gocov to generate a test coverage report.
|
||||
# The gocov tool my be obtained with the following command:
|
||||
# go get github.com/axw/gocov/gocov
|
||||
#
|
||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
||||
|
||||
# Check for gocov.
|
||||
if ! type gocov >/dev/null 2>&1; then
|
||||
echo >&2 "This script requires the gocov tool."
|
||||
echo >&2 "You may obtain it with the following command:"
|
||||
echo >&2 "go get github.com/axw/gocov/gocov"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Only run the cgo tests if gcc is installed.
|
||||
if type gcc >/dev/null 2>&1; then
|
||||
(cd spew && gocov test -tags testcgo | gocov report)
|
||||
else
|
||||
(cd spew && gocov test | gocov report)
|
||||
fi
|
||||
194
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
194
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -13,9 +13,12 @@
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine and "-tags disableunsafe"
|
||||
// is not added to the go build command line.
|
||||
// +build !appengine,!disableunsafe
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
@@ -33,80 +36,49 @@ const (
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the
|
||||
// internal reflect.Value fields. These values are valid before golang
|
||||
// commit ecccf07e7f9d which changed the format. The are also valid
|
||||
// after commit 82f48826c6c7 which changed the format again to mirror
|
||||
// the original format. Code in the init function updates these offsets
|
||||
// as necessary.
|
||||
offsetPtr = uintptr(ptrSize)
|
||||
offsetScalar = uintptr(0)
|
||||
offsetFlag = uintptr(ptrSize * 2)
|
||||
type flag uintptr
|
||||
|
||||
// flagKindWidth and flagKindShift indicate various bits that the
|
||||
// reflect package uses internally to track kind information.
|
||||
//
|
||||
// flagRO indicates whether or not the value field of a reflect.Value is
|
||||
// read-only.
|
||||
//
|
||||
// flagIndir indicates whether the value field of a reflect.Value is
|
||||
// the actual data or a pointer to the data.
|
||||
//
|
||||
// These values are valid before golang commit 90a7c3c86944 which
|
||||
// changed their positions. Code in the init function updates these
|
||||
// flags as necessary.
|
||||
flagKindWidth = uintptr(5)
|
||||
flagKindShift = uintptr(flagKindWidth - 1)
|
||||
flagRO = uintptr(1 << 0)
|
||||
flagIndir = uintptr(1 << 1)
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Older versions of reflect.Value stored small integers directly in the
|
||||
// ptr field (which is named val in the older versions). Versions
|
||||
// between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
|
||||
// scalar for this purpose which unfortunately came before the flag
|
||||
// field, so the offset of the flag field is different for those
|
||||
// versions.
|
||||
//
|
||||
// This code constructs a new reflect.Value from a known small integer
|
||||
// and checks if the size of the reflect.Value struct indicates it has
|
||||
// the scalar field. When it does, the offsets are updated accordingly.
|
||||
vv := reflect.ValueOf(0xf00)
|
||||
if unsafe.Sizeof(vv) == (ptrSize * 4) {
|
||||
offsetScalar = ptrSize * 2
|
||||
offsetFlag = ptrSize * 3
|
||||
}
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Commit 90a7c3c86944 changed the flag positions such that the low
|
||||
// order bits are the kind. This code extracts the kind from the flags
|
||||
// field and ensures it's the correct type. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are updated
|
||||
// accordingly.
|
||||
upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
|
||||
upfv := *(*uintptr)(upf)
|
||||
flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
|
||||
flagKindShift = 0
|
||||
flagRO = 1 << 5
|
||||
flagIndir = 1 << 6
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
// Commit adf9b30e5594 modified the flags to separate the
|
||||
// flagRO flag into two bits which specifies whether or not the
|
||||
// field is embedded. This causes flagIndir to move over a bit
|
||||
// and means that flagRO is the combination of either of the
|
||||
// original flagRO bit and the new bit.
|
||||
//
|
||||
// This code detects the change by extracting what used to be
|
||||
// the indirect bit to ensure it's set. When it's not, the flag
|
||||
// order has been changed to the newer format, so the flags are
|
||||
// updated accordingly.
|
||||
if upfv&flagIndir == 0 {
|
||||
flagRO = 3 << 5
|
||||
flagIndir = 1 << 7
|
||||
}
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
@@ -118,34 +90,56 @@ func init() {
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
|
||||
indirects := 1
|
||||
vt := v.Type()
|
||||
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
|
||||
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
|
||||
if rvf&flagIndir != 0 {
|
||||
vt = reflect.PtrTo(v.Type())
|
||||
indirects++
|
||||
} else if offsetScalar != 0 {
|
||||
// The value is in the scalar field when it's not one of the
|
||||
// reference types.
|
||||
switch vt.Kind() {
|
||||
case reflect.Uintptr:
|
||||
case reflect.Chan:
|
||||
case reflect.Func:
|
||||
case reflect.Map:
|
||||
case reflect.Ptr:
|
||||
case reflect.UnsafePointer:
|
||||
default:
|
||||
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
|
||||
offsetScalar)
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pv := reflect.NewAt(vt, upv)
|
||||
rv = pv
|
||||
for i := 0; i < indirects; i++ {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
return rv
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
||||
|
||||
9
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
9
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Dave Collins <dave@davec.name>
|
||||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -13,9 +13,10 @@
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when either the code is running on Google App Engine or "-tags disableunsafe"
|
||||
// is added to the go build command line.
|
||||
// +build appengine disableunsafe
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
|
||||
4
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
4
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -180,7 +180,7 @@ func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
|
||||
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// custom type to test Stinger interface on non-pointer receiver.
|
||||
type stringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with non-pointer receivers.
|
||||
func (s stringer) String() string {
|
||||
return "stringer " + string(s)
|
||||
}
|
||||
|
||||
// custom type to test Stinger interface on pointer receiver.
|
||||
type pstringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with only pointer receivers.
|
||||
func (s *pstringer) String() string {
|
||||
return "stringer " + string(*s)
|
||||
}
|
||||
|
||||
// xref1 and xref2 are cross referencing structs for testing circular reference
|
||||
// detection.
|
||||
type xref1 struct {
|
||||
ps2 *xref2
|
||||
}
|
||||
type xref2 struct {
|
||||
ps1 *xref1
|
||||
}
|
||||
|
||||
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
|
||||
// reference for testing detection.
|
||||
type indirCir1 struct {
|
||||
ps2 *indirCir2
|
||||
}
|
||||
type indirCir2 struct {
|
||||
ps3 *indirCir3
|
||||
}
|
||||
type indirCir3 struct {
|
||||
ps1 *indirCir1
|
||||
}
|
||||
|
||||
// embed is used to test embedded structures.
|
||||
type embed struct {
|
||||
a string
|
||||
}
|
||||
|
||||
// embedwrap is used to test embedded structures.
|
||||
type embedwrap struct {
|
||||
*embed
|
||||
e *embed
|
||||
}
|
||||
|
||||
// panicer is used to intentionally cause a panic for testing spew properly
|
||||
// handles them
|
||||
type panicer int
|
||||
|
||||
func (p panicer) String() string {
|
||||
panic("test panic")
|
||||
}
|
||||
|
||||
// customError is used to test custom error interface invocation.
|
||||
type customError int
|
||||
|
||||
func (e customError) Error() string {
|
||||
return fmt.Sprintf("error: %d", int(e))
|
||||
}
|
||||
|
||||
// stringizeWants converts a slice of wanted test output into a format suitable
|
||||
// for a test error message.
|
||||
func stringizeWants(wants []string) string {
|
||||
s := ""
|
||||
for i, want := range wants {
|
||||
if i > 0 {
|
||||
s += fmt.Sprintf("want%d: %s", i+1, want)
|
||||
} else {
|
||||
s += "want: " + want
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// testFailed returns whether or not a test failed by checking if the result
|
||||
// of the test is in the slice of wanted strings.
|
||||
func testFailed(result string, wants []string) bool {
|
||||
for _, want := range wants {
|
||||
if result == want {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type sortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func (ss sortableStruct) String() string {
|
||||
return fmt.Sprintf("ss.%d", ss.x)
|
||||
}
|
||||
|
||||
type unsortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
type sortTestCase struct {
|
||||
input []reflect.Value
|
||||
expected []reflect.Value
|
||||
}
|
||||
|
||||
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
|
||||
getInterfaces := func(values []reflect.Value) []interface{} {
|
||||
interfaces := []interface{}{}
|
||||
for _, v := range values {
|
||||
interfaces = append(interfaces, v.Interface())
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
spew.SortValues(test.input, cs)
|
||||
// reflect.DeepEqual cannot really make sense of reflect.Value,
|
||||
// probably because of all the pointer tricks. For instance,
|
||||
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
|
||||
// instead.
|
||||
input := getInterfaces(test.input)
|
||||
expected := getInterfaces(test.expected)
|
||||
if !reflect.DeepEqual(input, expected) {
|
||||
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSortValues ensures the sort functionality for relect.Value based sorting
|
||||
// works as intended.
|
||||
func TestSortValues(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
embedA := v(embed{"a"})
|
||||
embedB := v(embed{"b"})
|
||||
embedC := v(embed{"c"})
|
||||
tests := []sortTestCase{
|
||||
// No values.
|
||||
{
|
||||
[]reflect.Value{},
|
||||
[]reflect.Value{},
|
||||
},
|
||||
// Bools.
|
||||
{
|
||||
[]reflect.Value{v(false), v(true), v(false)},
|
||||
[]reflect.Value{v(false), v(false), v(true)},
|
||||
},
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Uints.
|
||||
{
|
||||
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
|
||||
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
|
||||
},
|
||||
// Floats.
|
||||
{
|
||||
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
|
||||
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// Array
|
||||
{
|
||||
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
|
||||
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
|
||||
},
|
||||
// Uintptrs.
|
||||
{
|
||||
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
|
||||
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
// Note: not sorted - DisableMethods is set.
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
// Invalid.
|
||||
{
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using string methods.
|
||||
func TestSortValuesWithMethods(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using spew to stringify keys.
|
||||
func TestSortValuesWithSpew(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
13
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
13
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -64,9 +64,18 @@ type ConfigState struct {
|
||||
// inside these interface methods. As a result, this option relies on
|
||||
// access to the unsafe package, so it will not have any effect when
|
||||
// running in environments without access to the unsafe package such as
|
||||
// Google App Engine or with the "disableunsafe" build tag specified.
|
||||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// DisablePointerAddresses specifies whether to disable the printing of
|
||||
// pointer addresses. This is useful when diffing data structures in tests.
|
||||
DisablePointerAddresses bool
|
||||
|
||||
// DisableCapacities specifies whether to disable the printing of capacities
|
||||
// for arrays, slices, maps and channels. This is useful when diffing
|
||||
// data structures in tests.
|
||||
DisableCapacities bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
|
||||
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
11
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -91,6 +91,15 @@ The following configuration options are available:
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
18
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
18
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -35,16 +35,16 @@ var (
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
@@ -129,7 +129,7 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if len(pointerChain) > 0 {
|
||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
@@ -143,10 +143,10 @@ func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
@@ -282,13 +282,13 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || valueCap != 0 {
|
||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if valueCap != 0 {
|
||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
98
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
98
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
@@ -1,98 +0,0 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||
// command line. This means the cgo tests are only added (and hence run) when
|
||||
// specifially requested. This configuration is used because spew itself
|
||||
// does not require cgo to run even though it does handle certain cgo types
|
||||
// specially. Rather than forcing all clients to require cgo and an external
|
||||
// C compiler just to run the tests, this scheme makes them optional.
|
||||
// +build cgo,testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew/testdata"
|
||||
)
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// C char pointer.
|
||||
v := testdata.GetCgoCharPointer()
|
||||
nv := testdata.GetCgoNullCharPointer()
|
||||
pv := &v
|
||||
vcAddr := fmt.Sprintf("%p", v)
|
||||
vAddr := fmt.Sprintf("%p", pv)
|
||||
pvAddr := fmt.Sprintf("%p", &pv)
|
||||
vt := "*testdata._Ctype_char"
|
||||
vs := "116"
|
||||
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(nv, "("+vt+")(<nil>)\n")
|
||||
|
||||
// C char array.
|
||||
v2, v2l, v2c := testdata.GetCgoCharArray()
|
||||
v2Len := fmt.Sprintf("%d", v2l)
|
||||
v2Cap := fmt.Sprintf("%d", v2c)
|
||||
v2t := "[6]testdata._Ctype_char"
|
||||
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 32 00 " +
|
||||
" |test2.|\n}"
|
||||
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
|
||||
|
||||
// C unsigned char array.
|
||||
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
|
||||
v3Len := fmt.Sprintf("%d", v3l)
|
||||
v3Cap := fmt.Sprintf("%d", v3c)
|
||||
v3t := "[6]testdata._Ctype_unsignedchar"
|
||||
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 33 00 " +
|
||||
" |test3.|\n}"
|
||||
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
|
||||
|
||||
// C signed char array.
|
||||
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
|
||||
v4Len := fmt.Sprintf("%d", v4l)
|
||||
v4Cap := fmt.Sprintf("%d", v4c)
|
||||
v4t := "[6]testdata._Ctype_schar"
|
||||
v4t2 := "testdata._Ctype_schar"
|
||||
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
|
||||
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
|
||||
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
|
||||
") 0\n}"
|
||||
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
|
||||
|
||||
// C uint8_t array.
|
||||
v5, v5l, v5c := testdata.GetCgoUint8tArray()
|
||||
v5Len := fmt.Sprintf("%d", v5l)
|
||||
v5Cap := fmt.Sprintf("%d", v5c)
|
||||
v5t := "[6]testdata._Ctype_uint8_t"
|
||||
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 35 00 " +
|
||||
" |test5.|\n}"
|
||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
|
||||
|
||||
// C typedefed unsigned char array.
|
||||
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
||||
v6Len := fmt.Sprintf("%d", v6l)
|
||||
v6Cap := fmt.Sprintf("%d", v6c)
|
||||
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
||||
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 36 00 " +
|
||||
" |test6.|\n}"
|
||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
|
||||
}
|
||||
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when either cgo is not supported or "-tags testcgo" is not added to the go
|
||||
// test command line. This file intentionally does not setup any cgo tests in
|
||||
// this scenario.
|
||||
// +build !cgo !testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// Don't add any tests for cgo since this file is only compiled when
|
||||
// there should not be any cgo tests.
|
||||
}
|
||||
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Dump to dump variables to stdout.
|
||||
func ExampleDump() {
|
||||
// The following package level declarations are assumed for this example:
|
||||
/*
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
*/
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
f := Flag(5)
|
||||
b := []byte{
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32,
|
||||
}
|
||||
|
||||
// Dump!
|
||||
spew.Dump(s1, f, b)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Flag) Unknown flag (5)
|
||||
// ([]uint8) (len=34 cap=34) {
|
||||
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
// 00000020 31 32 |12|
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Printf to display a variable with a
|
||||
// format string and inline formatting.
|
||||
func ExamplePrintf() {
|
||||
// Create a double pointer to a uint 8.
|
||||
ui8 := uint8(5)
|
||||
pui8 := &ui8
|
||||
ppui8 := &pui8
|
||||
|
||||
// Create a circular data type.
|
||||
type circular struct {
|
||||
ui8 uint8
|
||||
c *circular
|
||||
}
|
||||
c := circular{ui8: 1}
|
||||
c.c = &c
|
||||
|
||||
// Print!
|
||||
spew.Printf("ppui8: %v\n", ppui8)
|
||||
spew.Printf("circular: %v\n", c)
|
||||
|
||||
// Output:
|
||||
// ppui8: <**>5
|
||||
// circular: {1 <*>{1 <*><shown>}}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use a ConfigState.
|
||||
func ExampleConfigState() {
|
||||
// Modify the indent level of the ConfigState only. The global
|
||||
// configuration is not modified.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
|
||||
// Output using the ConfigState instance.
|
||||
v := map[string]int{"one": 1}
|
||||
scs.Printf("v: %v\n", v)
|
||||
scs.Dump(v)
|
||||
|
||||
// Output:
|
||||
// v: map[one:1]
|
||||
// (map[string]int) (len=1) {
|
||||
// (string) (len=3) "one": (int) 1
|
||||
// }
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Dump to dump variables to
|
||||
// stdout
|
||||
func ExampleConfigState_Dump() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances with different indentation.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Dump(s1)
|
||||
scs2.Dump(s1)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Printf to display a variable
|
||||
// with a format string and inline formatting.
|
||||
func ExampleConfigState_Printf() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances and modify the method handling of the
|
||||
// first ConfigState only.
|
||||
scs := spew.NewDefaultConfig()
|
||||
scs2 := spew.NewDefaultConfig()
|
||||
scs.DisableMethods = true
|
||||
|
||||
// Alternatively
|
||||
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||
// scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// This is of type Flag which implements a Stringer and has raw value 1.
|
||||
f := flagTwo
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Printf("f: %v\n", f)
|
||||
scs2.Printf("f: %v\n", f)
|
||||
|
||||
// Output:
|
||||
// f: 1
|
||||
// f: flagTwo
|
||||
}
|
||||
6
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
6
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -182,10 +182,10 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound == true:
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound == true:
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
|
||||
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
1558
vendor/github.com/davecgh/go-spew/spew/format_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
87
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
87
vendor/github.com/davecgh/go-spew/spew/internal_test.go
generated
vendored
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This test file is part of the spew package rather than than the spew_test
|
||||
package because it needs access to internals to properly test certain cases
|
||||
which are not possible via the public interface since they should never happen.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// dummyFmtState implements a fake fmt.State to use for testing invalid
|
||||
// reflect.Value handling. This is necessary because the fmt package catches
|
||||
// invalid values before invoking the formatter on them.
|
||||
type dummyFmtState struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Flag(f int) bool {
|
||||
if f == int('+') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Precision() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (dfs *dummyFmtState) Width() (int, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// TestInvalidReflectValue ensures the dump and formatter code handles an
|
||||
// invalid reflect value properly. This needs access to internal state since it
|
||||
// should never happen in real code and therefore can't be tested via the public
|
||||
// API.
|
||||
func TestInvalidReflectValue(t *testing.T) {
|
||||
i := 1
|
||||
|
||||
// Dump invalid reflect value.
|
||||
v := new(reflect.Value)
|
||||
buf := new(bytes.Buffer)
|
||||
d := dumpState{w: buf, cs: &Config}
|
||||
d.dump(*v)
|
||||
s := buf.String()
|
||||
want := "<invalid>"
|
||||
if s != want {
|
||||
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter invalid reflect value.
|
||||
buf2 := new(dummyFmtState)
|
||||
f := formatState{value: *v, cs: &Config, fs: buf2}
|
||||
f.format(*v)
|
||||
s = buf2.String()
|
||||
want = "<invalid>"
|
||||
if s != want {
|
||||
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
}
|
||||
|
||||
// SortValues makes the internal sortValues function available to the test
|
||||
// package.
|
||||
func SortValues(values []reflect.Value, cs *ConfigState) {
|
||||
sortValues(values, cs)
|
||||
}
|
||||
101
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
101
vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
generated
vendored
@@ -1,101 +0,0 @@
|
||||
// Copyright (c) 2013-2015 Dave Collins <dave@davec.name>
|
||||
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine and "-tags disableunsafe"
|
||||
// is not added to the go build command line.
|
||||
// +build !appengine,!disableunsafe
|
||||
|
||||
/*
|
||||
This test file is part of the spew package rather than than the spew_test
|
||||
package because it needs access to internals to properly test certain cases
|
||||
which are not possible via the public interface since they should never happen.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
|
||||
// the maximum kind value which does not exist. This is needed to test the
|
||||
// fallback code which punts to the standard fmt library for new types that
|
||||
// might get added to the language.
|
||||
func changeKind(v *reflect.Value, readOnly bool) {
|
||||
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
|
||||
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
|
||||
if readOnly {
|
||||
*rvf |= flagRO
|
||||
} else {
|
||||
*rvf &= ^uintptr(flagRO)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddedReflectValue tests functionaly of the dump and formatter code which
|
||||
// falls back to the standard fmt library for new types that might get added to
|
||||
// the language.
|
||||
func TestAddedReflectValue(t *testing.T) {
|
||||
i := 1
|
||||
|
||||
// Dump using a reflect.Value that is exported.
|
||||
v := reflect.ValueOf(int8(5))
|
||||
changeKind(&v, false)
|
||||
buf := new(bytes.Buffer)
|
||||
d := dumpState{w: buf, cs: &Config}
|
||||
d.dump(v)
|
||||
s := buf.String()
|
||||
want := "(int8) 5"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Dump using a reflect.Value that is not exported.
|
||||
changeKind(&v, true)
|
||||
buf.Reset()
|
||||
d.dump(v)
|
||||
s = buf.String()
|
||||
want = "(int8) <int8 Value>"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter using a reflect.Value that is exported.
|
||||
changeKind(&v, false)
|
||||
buf2 := new(dummyFmtState)
|
||||
f := formatState{value: v, cs: &Config, fs: buf2}
|
||||
f.format(v)
|
||||
s = buf2.String()
|
||||
want = "5"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
i++
|
||||
|
||||
// Formatter using a reflect.Value that is not exported.
|
||||
changeKind(&v, true)
|
||||
buf2.Reset()
|
||||
f = formatState{value: v, cs: &Config, fs: buf2}
|
||||
f.format(v)
|
||||
s = buf2.String()
|
||||
want = "<int8 Value>"
|
||||
if s != want {
|
||||
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
|
||||
}
|
||||
}
|
||||
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
2
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
309
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
309
vendor/github.com/davecgh/go-spew/spew/spew_test.go
generated
vendored
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// spewFunc is used to identify which public function of the spew package or
|
||||
// ConfigState a test applies to.
|
||||
type spewFunc int
|
||||
|
||||
const (
|
||||
fCSFdump spewFunc = iota
|
||||
fCSFprint
|
||||
fCSFprintf
|
||||
fCSFprintln
|
||||
fCSPrint
|
||||
fCSPrintln
|
||||
fCSSdump
|
||||
fCSSprint
|
||||
fCSSprintf
|
||||
fCSSprintln
|
||||
fCSErrorf
|
||||
fCSNewFormatter
|
||||
fErrorf
|
||||
fFprint
|
||||
fFprintln
|
||||
fPrint
|
||||
fPrintln
|
||||
fSdump
|
||||
fSprint
|
||||
fSprintf
|
||||
fSprintln
|
||||
)
|
||||
|
||||
// Map of spewFunc values to names for pretty printing.
|
||||
var spewFuncStrings = map[spewFunc]string{
|
||||
fCSFdump: "ConfigState.Fdump",
|
||||
fCSFprint: "ConfigState.Fprint",
|
||||
fCSFprintf: "ConfigState.Fprintf",
|
||||
fCSFprintln: "ConfigState.Fprintln",
|
||||
fCSSdump: "ConfigState.Sdump",
|
||||
fCSPrint: "ConfigState.Print",
|
||||
fCSPrintln: "ConfigState.Println",
|
||||
fCSSprint: "ConfigState.Sprint",
|
||||
fCSSprintf: "ConfigState.Sprintf",
|
||||
fCSSprintln: "ConfigState.Sprintln",
|
||||
fCSErrorf: "ConfigState.Errorf",
|
||||
fCSNewFormatter: "ConfigState.NewFormatter",
|
||||
fErrorf: "spew.Errorf",
|
||||
fFprint: "spew.Fprint",
|
||||
fFprintln: "spew.Fprintln",
|
||||
fPrint: "spew.Print",
|
||||
fPrintln: "spew.Println",
|
||||
fSdump: "spew.Sdump",
|
||||
fSprint: "spew.Sprint",
|
||||
fSprintf: "spew.Sprintf",
|
||||
fSprintln: "spew.Sprintln",
|
||||
}
|
||||
|
||||
func (f spewFunc) String() string {
|
||||
if s, ok := spewFuncStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
|
||||
}
|
||||
|
||||
// spewTest is used to describe a test to be performed against the public
|
||||
// functions of the spew package or ConfigState.
|
||||
type spewTest struct {
|
||||
cs *spew.ConfigState
|
||||
f spewFunc
|
||||
format string
|
||||
in interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
// spewTests houses the tests to be performed against the public functions of
|
||||
// the spew package and ConfigState.
|
||||
//
|
||||
// These tests are only intended to ensure the public functions are exercised
|
||||
// and are intentionally not exhaustive of types. The exhaustive type
|
||||
// tests are handled in the dump and format tests.
|
||||
var spewTests []spewTest
|
||||
|
||||
// redirStdout is a helper function to return the standard output from f as a
|
||||
// byte slice.
|
||||
func redirStdout(f func()) ([]byte, error) {
|
||||
tempFile, err := ioutil.TempFile("", "ss-test")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileName := tempFile.Name()
|
||||
defer os.Remove(fileName) // Ignore error
|
||||
|
||||
origStdout := os.Stdout
|
||||
os.Stdout = tempFile
|
||||
f()
|
||||
os.Stdout = origStdout
|
||||
tempFile.Close()
|
||||
|
||||
return ioutil.ReadFile(fileName)
|
||||
}
|
||||
|
||||
func initSpewTests() {
|
||||
// Config states with various settings.
|
||||
scsDefault := spew.NewDefaultConfig()
|
||||
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
|
||||
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
|
||||
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
|
||||
|
||||
// Variables for tests on types which implement Stringer interface with and
|
||||
// without a pointer receiver.
|
||||
ts := stringer("test")
|
||||
tps := pstringer("test")
|
||||
|
||||
// depthTester is used to test max depth handling for structs, array, slices
|
||||
// and maps.
|
||||
type depthTester struct {
|
||||
ic indirCir1
|
||||
arr [1]string
|
||||
slice []string
|
||||
m map[string]int
|
||||
}
|
||||
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
|
||||
map[string]int{"one": 1}}
|
||||
|
||||
// Variable for tests on types which implement error interface.
|
||||
te := customError(10)
|
||||
|
||||
spewTests = []spewTest{
|
||||
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
|
||||
{scsDefault, fCSFprint, "", int16(32767), "32767"},
|
||||
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
|
||||
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
|
||||
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
|
||||
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
|
||||
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
|
||||
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
|
||||
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
|
||||
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
|
||||
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
|
||||
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
|
||||
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
|
||||
{scsDefault, fFprint, "", float32(3.14), "3.14"},
|
||||
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
|
||||
{scsDefault, fPrint, "", true, "true"},
|
||||
{scsDefault, fPrintln, "", false, "false\n"},
|
||||
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
|
||||
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
|
||||
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
|
||||
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
|
||||
{scsNoMethods, fCSFprint, "", ts, "test"},
|
||||
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
|
||||
{scsNoMethods, fCSFprint, "", tps, "test"},
|
||||
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
|
||||
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
|
||||
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
|
||||
{scsNoPmethods, fCSFprint, "", tps, "test"},
|
||||
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
|
||||
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
|
||||
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
|
||||
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
|
||||
" arr: ([1]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
|
||||
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
|
||||
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
|
||||
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
|
||||
"(len=4) (stringer test) \"test\"\n"},
|
||||
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
|
||||
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
|
||||
"(error: 10) 10\n"},
|
||||
}
|
||||
}
|
||||
|
||||
// TestSpew executes all of the tests described by spewTests.
|
||||
func TestSpew(t *testing.T) {
|
||||
initSpewTests()
|
||||
|
||||
t.Logf("Running %d tests", len(spewTests))
|
||||
for i, test := range spewTests {
|
||||
buf := new(bytes.Buffer)
|
||||
switch test.f {
|
||||
case fCSFdump:
|
||||
test.cs.Fdump(buf, test.in)
|
||||
|
||||
case fCSFprint:
|
||||
test.cs.Fprint(buf, test.in)
|
||||
|
||||
case fCSFprintf:
|
||||
test.cs.Fprintf(buf, test.format, test.in)
|
||||
|
||||
case fCSFprintln:
|
||||
test.cs.Fprintln(buf, test.in)
|
||||
|
||||
case fCSPrint:
|
||||
b, err := redirStdout(func() { test.cs.Print(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fCSPrintln:
|
||||
b, err := redirStdout(func() { test.cs.Println(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fCSSdump:
|
||||
str := test.cs.Sdump(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprint:
|
||||
str := test.cs.Sprint(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprintf:
|
||||
str := test.cs.Sprintf(test.format, test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSSprintln:
|
||||
str := test.cs.Sprintln(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fCSErrorf:
|
||||
err := test.cs.Errorf(test.format, test.in)
|
||||
buf.WriteString(err.Error())
|
||||
|
||||
case fCSNewFormatter:
|
||||
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
|
||||
|
||||
case fErrorf:
|
||||
err := spew.Errorf(test.format, test.in)
|
||||
buf.WriteString(err.Error())
|
||||
|
||||
case fFprint:
|
||||
spew.Fprint(buf, test.in)
|
||||
|
||||
case fFprintln:
|
||||
spew.Fprintln(buf, test.in)
|
||||
|
||||
case fPrint:
|
||||
b, err := redirStdout(func() { spew.Print(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fPrintln:
|
||||
b, err := redirStdout(func() { spew.Println(test.in) })
|
||||
if err != nil {
|
||||
t.Errorf("%v #%d %v", test.f, i, err)
|
||||
continue
|
||||
}
|
||||
buf.Write(b)
|
||||
|
||||
case fSdump:
|
||||
str := spew.Sdump(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprint:
|
||||
str := spew.Sprint(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprintf:
|
||||
str := spew.Sprintf(test.format, test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
case fSprintln:
|
||||
str := spew.Sprintln(test.in)
|
||||
buf.WriteString(str)
|
||||
|
||||
default:
|
||||
t.Errorf("%v #%d unrecognized function", test.f, i)
|
||||
continue
|
||||
}
|
||||
s := buf.String()
|
||||
if test.want != s {
|
||||
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
82
vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
generated
vendored
@@ -1,82 +0,0 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||
// command line. This code should really only be in the dumpcgo_test.go file,
|
||||
// but unfortunately Go will not allow cgo in test files, so this is a
|
||||
// workaround to allow cgo types to be tested. This configuration is used
|
||||
// because spew itself does not require cgo to run even though it does handle
|
||||
// certain cgo types specially. Rather than forcing all clients to require cgo
|
||||
// and an external C compiler just to run the tests, this scheme makes them
|
||||
// optional.
|
||||
// +build cgo,testcgo
|
||||
|
||||
package testdata
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
typedef unsigned char custom_uchar_t;
|
||||
|
||||
char *ncp = 0;
|
||||
char *cp = "test";
|
||||
char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
|
||||
unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
|
||||
signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
|
||||
uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
|
||||
custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
|
||||
// used for tests.
|
||||
func GetCgoNullCharPointer() interface{} {
|
||||
return C.ncp
|
||||
}
|
||||
|
||||
// GetCgoCharPointer returns a char pointer via cgo. This is only used for
|
||||
// tests.
|
||||
func GetCgoCharPointer() interface{} {
|
||||
return C.cp
|
||||
}
|
||||
|
||||
// GetCgoCharArray returns a char array via cgo and the array's len and cap.
|
||||
// This is only used for tests.
|
||||
func GetCgoCharArray() (interface{}, int, int) {
|
||||
return C.ca, len(C.ca), cap(C.ca)
|
||||
}
|
||||
|
||||
// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the
|
||||
// array's len and cap. This is only used for tests.
|
||||
func GetCgoUnsignedCharArray() (interface{}, int, int) {
|
||||
return C.uca, len(C.uca), cap(C.uca)
|
||||
}
|
||||
|
||||
// GetCgoSignedCharArray returns a signed char array via cgo and the array's len
|
||||
// and cap. This is only used for tests.
|
||||
func GetCgoSignedCharArray() (interface{}, int, int) {
|
||||
return C.sca, len(C.sca), cap(C.sca)
|
||||
}
|
||||
|
||||
// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and
|
||||
// cap. This is only used for tests.
|
||||
func GetCgoUint8tArray() (interface{}, int, int) {
|
||||
return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
|
||||
}
|
||||
|
||||
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
|
||||
// cgo and the array's len and cap. This is only used for tests.
|
||||
func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
|
||||
return C.tuca, len(C.tuca), cap(C.tuca)
|
||||
}
|
||||
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
61
vendor/github.com/davecgh/go-spew/test_coverage.txt
generated
vendored
@@ -1,61 +0,0 @@
|
||||
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
|
||||
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
|
||||
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
|
||||
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
|
||||
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
|
||||
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
|
||||
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
|
||||
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
|
||||
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
|
||||
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
|
||||
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
|
||||
|
||||
107
vendor/github.com/deepzz0/logd/logd_test.go
generated
vendored
107
vendor/github.com/deepzz0/logd/logd_test.go
generated
vendored
@@ -1,107 +0,0 @@
|
||||
package logd
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
Printf("Printf: foo\n")
|
||||
Print("Print: foo")
|
||||
|
||||
SetLevel(Ldebug)
|
||||
|
||||
Debugf("Debugf: foo\n")
|
||||
Debug("Debug: foo")
|
||||
|
||||
Infof("Infof: foo\n")
|
||||
Info("Info: foo")
|
||||
|
||||
Errorf("Errorf: foo\n")
|
||||
Error("Error: foo")
|
||||
|
||||
SetLevel(Lerror)
|
||||
|
||||
Debugf("Debugf: foo\n")
|
||||
Debug("Debug: foo")
|
||||
|
||||
Infof("Infof: foo\n")
|
||||
Info("Info: foo")
|
||||
|
||||
Errorf("Errorf: foo\n")
|
||||
Error("Error: foo")
|
||||
}
|
||||
|
||||
func BenchmarkLogFileChan(b *testing.B) {
|
||||
log := New(LogOption{
|
||||
Flag: LAsync | Ldate | Ltime | Lshortfile,
|
||||
LogDir: "testdata",
|
||||
ChannelLen: 1000,
|
||||
})
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
log.WaitFlush()
|
||||
}
|
||||
|
||||
func BenchmarkLogFile(b *testing.B) {
|
||||
f, _ := os.OpenFile("testdata/onlyfile.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
|
||||
log := New(LogOption{
|
||||
Out: f,
|
||||
Flag: Ldate | Ltime | Lshortfile,
|
||||
LogDir: "testdata",
|
||||
ChannelLen: 1000,
|
||||
})
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
log.WaitFlush()
|
||||
}
|
||||
|
||||
func BenchmarkStandardFile(b *testing.B) {
|
||||
f, _ := os.OpenFile("testdata/logfile.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
log := log.New(f, "", log.LstdFlags)
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLogFileChanMillion(b *testing.B) {
|
||||
log := New(LogOption{
|
||||
Flag: LAsync | Ldate | Ltime | Lshortfile,
|
||||
LogDir: "testdata",
|
||||
ChannelLen: 1000,
|
||||
})
|
||||
b.N = 1000000
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
log.WaitFlush()
|
||||
}
|
||||
|
||||
func BenchmarkLogFileMillion(b *testing.B) {
|
||||
f, _ := os.OpenFile("testdata/onlyfilemillion.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
|
||||
log := New(LogOption{
|
||||
Out: f,
|
||||
Flag: Ldate | Ltime | Lshortfile,
|
||||
LogDir: "testdata",
|
||||
ChannelLen: 1000,
|
||||
})
|
||||
b.N = 1000000
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
log.WaitFlush()
|
||||
}
|
||||
|
||||
func BenchmarkStandardFileMillion(b *testing.B) {
|
||||
f, _ := os.OpenFile("testdata/logfilemillion.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
log := log.New(f, "", log.LstdFlags)
|
||||
b.N = 1000000
|
||||
for i := 0; i < b.N; i++ {
|
||||
log.Print("testing this is a testing about benchmark")
|
||||
}
|
||||
}
|
||||
20
vendor/github.com/deepzz0/logd/mail_test.go
generated
vendored
20
vendor/github.com/deepzz0/logd/mail_test.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// Package logd provides ...
|
||||
package logd
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSmtpSendMail(t *testing.T) {
|
||||
s := &Smtp{
|
||||
From: "120735581@qq.com",
|
||||
Key: "peerdmnoqirqbiaa",
|
||||
Host: "smtp.qq.com",
|
||||
Port: "465",
|
||||
To: []string{"a120735581@foxmail.com"},
|
||||
Subject: "test email from logd",
|
||||
}
|
||||
|
||||
err := s.SendMail("test", []byte("hello world"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
5
vendor/github.com/garyburd/redigo/.github/CONTRIBUTING.md
generated
vendored
5
vendor/github.com/garyburd/redigo/.github/CONTRIBUTING.md
generated
vendored
@@ -1,5 +0,0 @@
|
||||
Ask questions at
|
||||
[StackOverflow](https://stackoverflow.com/questions/ask?tags=go+redis).
|
||||
|
||||
[Open an issue](https://github.com/garyburd/redigo/issues/new) to discuss your
|
||||
plans before doing any work on Redigo.
|
||||
1
vendor/github.com/garyburd/redigo/.github/ISSUE_TEMPLATE.md
generated
vendored
1
vendor/github.com/garyburd/redigo/.github/ISSUE_TEMPLATE.md
generated
vendored
@@ -1 +0,0 @@
|
||||
Ask questions at https://stackoverflow.com/questions/ask?tags=go+redis
|
||||
19
vendor/github.com/garyburd/redigo/.travis.yml
generated
vendored
19
vendor/github.com/garyburd/redigo/.travis.yml
generated
vendored
@@ -1,19 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go get -t -v ./...
|
||||
- diff -u <(echo -n) <(gofmt -d .)
|
||||
- go vet $(go list ./... | grep -v /vendor/)
|
||||
- go test -v -race ./...
|
||||
51
vendor/github.com/garyburd/redigo/README.markdown
generated
vendored
51
vendor/github.com/garyburd/redigo/README.markdown
generated
vendored
@@ -1,51 +0,0 @@
|
||||
Redigo
|
||||
======
|
||||
|
||||
[](https://travis-ci.org/garyburd/redigo)
|
||||
[](https://godoc.org/github.com/garyburd/redigo/redis)
|
||||
|
||||
Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database.
|
||||
|
||||
Features
|
||||
-------
|
||||
|
||||
* A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands.
|
||||
* [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions.
|
||||
* [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe).
|
||||
* [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool).
|
||||
* [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA.
|
||||
* [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
- [API Reference](http://godoc.org/github.com/garyburd/redigo/redis)
|
||||
- [FAQ](https://github.com/garyburd/redigo/wiki/FAQ)
|
||||
- [Examples](https://godoc.org/github.com/garyburd/redigo/redis#pkg-examples)
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install Redigo using the "go get" command:
|
||||
|
||||
go get github.com/garyburd/redigo/redis
|
||||
|
||||
The Go distribution is Redigo's only dependency.
|
||||
|
||||
Related Projects
|
||||
----------------
|
||||
|
||||
- [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo.
|
||||
- [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation.
|
||||
- [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo
|
||||
- [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/garyburd/redigo/blob/master/.github/CONTRIBUTING.md).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||
27
vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
generated
vendored
27
vendor/github.com/garyburd/redigo/internal/commandinfo_test.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package internal
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLookupCommandInfo(t *testing.T) {
|
||||
for _, n := range []string{"watch", "WATCH", "wAtch"} {
|
||||
if LookupCommandInfo(n) == (CommandInfo{}) {
|
||||
t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkLookupCommandInfo(b *testing.B, names ...string) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, c := range names {
|
||||
LookupCommandInfo(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) {
|
||||
benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR")
|
||||
}
|
||||
|
||||
func BenchmarkLookupCommandInfoMixedCase(b *testing.B) {
|
||||
benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR")
|
||||
}
|
||||
68
vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
generated
vendored
68
vendor/github.com/garyburd/redigo/internal/redistest/testdb.go
generated
vendored
@@ -1,68 +0,0 @@
|
||||
// Copyright 2014 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Package redistest contains utilities for writing Redigo tests.
|
||||
package redistest
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type testConn struct {
|
||||
redis.Conn
|
||||
}
|
||||
|
||||
func (t testConn) Close() error {
|
||||
_, err := t.Conn.Do("SELECT", "9")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err = t.Conn.Do("FLUSHDB")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.Conn.Close()
|
||||
}
|
||||
|
||||
// Dial dials the local Redis server and selects database 9. To prevent
|
||||
// stomping on real data, DialTestDB fails if database 9 contains data. The
|
||||
// returned connection flushes database 9 on close.
|
||||
func Dial() (redis.Conn, error) {
|
||||
c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = c.Do("SELECT", "9")
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, err := redis.Int(c.Do("DBSIZE"))
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n != 0 {
|
||||
c.Close()
|
||||
return nil, errors.New("database #9 is not empty, test can not continue")
|
||||
}
|
||||
|
||||
return testConn{c}, nil
|
||||
}
|
||||
867
vendor/github.com/garyburd/redigo/redis/conn_test.go
generated
vendored
867
vendor/github.com/garyburd/redigo/redis/conn_test.go
generated
vendored
@@ -1,867 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type testConn struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
readDeadline time.Time
|
||||
writeDeadline time.Time
|
||||
}
|
||||
|
||||
func (*testConn) Close() error { return nil }
|
||||
func (*testConn) LocalAddr() net.Addr { return nil }
|
||||
func (*testConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *testConn) SetDeadline(t time.Time) error { c.readDeadline = t; c.writeDeadline = t; return nil }
|
||||
func (c *testConn) SetReadDeadline(t time.Time) error { c.readDeadline = t; return nil }
|
||||
func (c *testConn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t; return nil }
|
||||
|
||||
func dialTestConn(r string, w io.Writer) redis.DialOption {
|
||||
return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
|
||||
return &testConn{Reader: strings.NewReader(r), Writer: w}, nil
|
||||
})
|
||||
}
|
||||
|
||||
type tlsTestConn struct {
|
||||
net.Conn
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func (c *tlsTestConn) Close() error {
|
||||
c.Conn.Close()
|
||||
<-c.done
|
||||
return nil
|
||||
}
|
||||
|
||||
func dialTestConnTLS(r string, w io.Writer) redis.DialOption {
|
||||
return redis.DialNetDial(func(network, addr string) (net.Conn, error) {
|
||||
client, server := net.Pipe()
|
||||
tlsServer := tls.Server(server, &serverTLSConfig)
|
||||
go io.Copy(tlsServer, strings.NewReader(r))
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(w, tlsServer)
|
||||
close(done)
|
||||
}()
|
||||
return &tlsTestConn{Conn: client, done: done}, nil
|
||||
})
|
||||
}
|
||||
|
||||
type durationArg struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func (t durationArg) RedisArg() interface{} {
|
||||
return t.Seconds()
|
||||
}
|
||||
|
||||
type recursiveArg int
|
||||
|
||||
func (v recursiveArg) RedisArg() interface{} { return v }
|
||||
|
||||
var writeTests = []struct {
|
||||
args []interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
[]interface{}{"SET", "key", "value"},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", "value"},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", byte(100)},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", 100},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n100\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", int64(math.MinInt64)},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$20\r\n-9223372036854775808\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", float64(1349673917.939762)},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$21\r\n1.349673917939762e+09\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", ""},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", nil},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$0\r\n\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", durationArg{time.Minute}},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$2\r\n60\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "key", recursiveArg(123)},
|
||||
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$3\r\n123\r\n",
|
||||
},
|
||||
{
|
||||
[]interface{}{"ECHO", true, false},
|
||||
"*3\r\n$4\r\nECHO\r\n$1\r\n1\r\n$1\r\n0\r\n",
|
||||
},
|
||||
}
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
for _, tt := range writeTests {
|
||||
var buf bytes.Buffer
|
||||
c, _ := redis.Dial("", "", dialTestConn("", &buf))
|
||||
err := c.Send(tt.args[0].(string), tt.args[1:]...)
|
||||
if err != nil {
|
||||
t.Errorf("Send(%v) returned error %v", tt.args, err)
|
||||
continue
|
||||
}
|
||||
c.Flush()
|
||||
actual := buf.String()
|
||||
if actual != tt.expected {
|
||||
t.Errorf("Send(%v) = %q, want %q", tt.args, actual, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errorSentinel = &struct{}{}
|
||||
|
||||
var readTests = []struct {
|
||||
reply string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
"+OK\r\n",
|
||||
"OK",
|
||||
},
|
||||
{
|
||||
"+PONG\r\n",
|
||||
"PONG",
|
||||
},
|
||||
{
|
||||
"@OK\r\n",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
"$6\r\nfoobar\r\n",
|
||||
[]byte("foobar"),
|
||||
},
|
||||
{
|
||||
"$-1\r\n",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
":1\r\n",
|
||||
int64(1),
|
||||
},
|
||||
{
|
||||
":-2\r\n",
|
||||
int64(-2),
|
||||
},
|
||||
{
|
||||
"*0\r\n",
|
||||
[]interface{}{},
|
||||
},
|
||||
{
|
||||
"*-1\r\n",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n",
|
||||
[]interface{}{[]byte("foo"), []byte("bar"), []byte("Hello"), []byte("World")},
|
||||
},
|
||||
{
|
||||
"*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n",
|
||||
[]interface{}{[]byte("foo"), nil, []byte("bar")},
|
||||
},
|
||||
|
||||
{
|
||||
// "x" is not a valid length
|
||||
"$x\r\nfoobar\r\n",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
// -2 is not a valid length
|
||||
"$-2\r\n",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
// "x" is not a valid integer
|
||||
":x\r\n",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
// missing \r\n following value
|
||||
"$6\r\nfoobar",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
// short value
|
||||
"$6\r\nxx",
|
||||
errorSentinel,
|
||||
},
|
||||
{
|
||||
// long value
|
||||
"$6\r\nfoobarx\r\n",
|
||||
errorSentinel,
|
||||
},
|
||||
}
|
||||
|
||||
func TestRead(t *testing.T) {
|
||||
for _, tt := range readTests {
|
||||
c, _ := redis.Dial("", "", dialTestConn(tt.reply, nil))
|
||||
actual, err := c.Receive()
|
||||
if tt.expected == errorSentinel {
|
||||
if err == nil {
|
||||
t.Errorf("Receive(%q) did not return expected error", tt.reply)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Receive(%q) returned error %v", tt.reply, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tt.expected) {
|
||||
t.Errorf("Receive(%q) = %v, want %v", tt.reply, actual, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var testCommands = []struct {
|
||||
args []interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
[]interface{}{"PING"},
|
||||
"PONG",
|
||||
},
|
||||
{
|
||||
[]interface{}{"SET", "foo", "bar"},
|
||||
"OK",
|
||||
},
|
||||
{
|
||||
[]interface{}{"GET", "foo"},
|
||||
[]byte("bar"),
|
||||
},
|
||||
{
|
||||
[]interface{}{"GET", "nokey"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
[]interface{}{"MGET", "nokey", "foo"},
|
||||
[]interface{}{nil, []byte("bar")},
|
||||
},
|
||||
{
|
||||
[]interface{}{"INCR", "mycounter"},
|
||||
int64(1),
|
||||
},
|
||||
{
|
||||
[]interface{}{"LPUSH", "mylist", "foo"},
|
||||
int64(1),
|
||||
},
|
||||
{
|
||||
[]interface{}{"LPUSH", "mylist", "bar"},
|
||||
int64(2),
|
||||
},
|
||||
{
|
||||
[]interface{}{"LRANGE", "mylist", 0, -1},
|
||||
[]interface{}{[]byte("bar"), []byte("foo")},
|
||||
},
|
||||
{
|
||||
[]interface{}{"MULTI"},
|
||||
"OK",
|
||||
},
|
||||
{
|
||||
[]interface{}{"LRANGE", "mylist", 0, -1},
|
||||
"QUEUED",
|
||||
},
|
||||
{
|
||||
[]interface{}{"PING"},
|
||||
"QUEUED",
|
||||
},
|
||||
{
|
||||
[]interface{}{"EXEC"},
|
||||
[]interface{}{
|
||||
[]interface{}{[]byte("bar"), []byte("foo")},
|
||||
"PONG",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestDoCommands(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
for _, cmd := range testCommands {
|
||||
actual, err := c.Do(cmd.args[0].(string), cmd.args[1:]...)
|
||||
if err != nil {
|
||||
t.Errorf("Do(%v) returned error %v", cmd.args, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(actual, cmd.expected) {
|
||||
t.Errorf("Do(%v) = %v, want %v", cmd.args, actual, cmd.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipelineCommands(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
for _, cmd := range testCommands {
|
||||
if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
|
||||
t.Fatalf("Send(%v) returned error %v", cmd.args, err)
|
||||
}
|
||||
}
|
||||
if err := c.Flush(); err != nil {
|
||||
t.Errorf("Flush() returned error %v", err)
|
||||
}
|
||||
for _, cmd := range testCommands {
|
||||
actual, err := c.Receive()
|
||||
if err != nil {
|
||||
t.Fatalf("Receive(%v) returned error %v", cmd.args, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, cmd.expected) {
|
||||
t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlankCommmand(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
for _, cmd := range testCommands {
|
||||
if err := c.Send(cmd.args[0].(string), cmd.args[1:]...); err != nil {
|
||||
t.Fatalf("Send(%v) returned error %v", cmd.args, err)
|
||||
}
|
||||
}
|
||||
reply, err := redis.Values(c.Do(""))
|
||||
if err != nil {
|
||||
t.Fatalf("Do() returned error %v", err)
|
||||
}
|
||||
if len(reply) != len(testCommands) {
|
||||
t.Fatalf("len(reply)=%d, want %d", len(reply), len(testCommands))
|
||||
}
|
||||
for i, cmd := range testCommands {
|
||||
actual := reply[i]
|
||||
if !reflect.DeepEqual(actual, cmd.expected) {
|
||||
t.Errorf("Receive(%v) = %v, want %v", cmd.args, actual, cmd.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecvBeforeSend(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
c.Receive()
|
||||
close(done)
|
||||
}()
|
||||
time.Sleep(time.Millisecond)
|
||||
c.Send("PING")
|
||||
c.Flush()
|
||||
<-done
|
||||
_, err = c.Do("")
|
||||
if err != nil {
|
||||
t.Fatalf("error=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("SET", "key", "val")
|
||||
_, err = c.Do("HSET", "key", "fld", "val")
|
||||
if err == nil {
|
||||
t.Errorf("Expected err for HSET on string key.")
|
||||
}
|
||||
if c.Err() != nil {
|
||||
t.Errorf("Conn has Err()=%v, expect nil", c.Err())
|
||||
}
|
||||
_, err = c.Do("SET", "key", "val")
|
||||
if err != nil {
|
||||
t.Errorf("Do(SET, key, val) returned error %v, expected nil.", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadTimeout(t *testing.T) {
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen returned %v", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
c.Write([]byte("+OK\r\n"))
|
||||
c.Close()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
// Do
|
||||
|
||||
c1, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatalf("redis.Dial returned %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
|
||||
_, err = c1.Do("PING")
|
||||
if err == nil {
|
||||
t.Fatalf("c1.Do() returned nil, expect error")
|
||||
}
|
||||
if c1.Err() == nil {
|
||||
t.Fatalf("c1.Err() = nil, expect error")
|
||||
}
|
||||
|
||||
// Send/Flush/Receive
|
||||
|
||||
c2, err := redis.Dial(l.Addr().Network(), l.Addr().String(), redis.DialReadTimeout(time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatalf("redis.Dial returned %v", err)
|
||||
}
|
||||
defer c2.Close()
|
||||
|
||||
c2.Send("PING")
|
||||
c2.Flush()
|
||||
_, err = c2.Receive()
|
||||
if err == nil {
|
||||
t.Fatalf("c2.Receive() returned nil, expect error")
|
||||
}
|
||||
if c2.Err() == nil {
|
||||
t.Fatalf("c2.Err() = nil, expect error")
|
||||
}
|
||||
}
|
||||
|
||||
var dialErrors = []struct {
|
||||
rawurl string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
"localhost",
|
||||
"invalid redis URL scheme",
|
||||
},
|
||||
// The error message for invalid hosts is different in different
|
||||
// versions of Go, so just check that there is an error message.
|
||||
{
|
||||
"redis://weird url",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"redis://foo:bar:baz",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"http://www.google.com",
|
||||
"invalid redis URL scheme: http",
|
||||
},
|
||||
{
|
||||
"redis://localhost:6379/abc123",
|
||||
"invalid database: abc123",
|
||||
},
|
||||
}
|
||||
|
||||
func TestDialURLErrors(t *testing.T) {
|
||||
for _, d := range dialErrors {
|
||||
_, err := redis.DialURL(d.rawurl)
|
||||
if err == nil || !strings.Contains(err.Error(), d.expectedError) {
|
||||
t.Errorf("DialURL did not return expected error (expected %v to contain %s)", err, d.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialURLPort(t *testing.T) {
|
||||
checkPort := func(network, address string) (net.Conn, error) {
|
||||
if address != "localhost:6379" {
|
||||
t.Errorf("DialURL did not set port to 6379 by default (got %v)", address)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
_, err := redis.DialURL("redis://localhost", redis.DialNetDial(checkPort))
|
||||
if err != nil {
|
||||
t.Error("dial error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialURLHost(t *testing.T) {
|
||||
checkHost := func(network, address string) (net.Conn, error) {
|
||||
if address != "localhost:6379" {
|
||||
t.Errorf("DialURL did not set host to localhost by default (got %v)", address)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
_, err := redis.DialURL("redis://:6379", redis.DialNetDial(checkHost))
|
||||
if err != nil {
|
||||
t.Error("dial error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
var dialURLTests = []struct {
|
||||
description string
|
||||
url string
|
||||
r string
|
||||
w string
|
||||
}{
|
||||
{"password", "redis://x:abc123@localhost", "+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
|
||||
{"database 3", "redis://localhost/3", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"},
|
||||
{"database 99", "redis://localhost/99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
|
||||
{"no database", "redis://localhost/", "+OK\r\n", ""},
|
||||
}
|
||||
|
||||
func TestDialURL(t *testing.T) {
|
||||
for _, tt := range dialURLTests {
|
||||
var buf bytes.Buffer
|
||||
// UseTLS should be ignored in all of these tests.
|
||||
_, err := redis.DialURL(tt.url, dialTestConn(tt.r, &buf), redis.DialUseTLS(true))
|
||||
if err != nil {
|
||||
t.Errorf("%s dial error: %v", tt.description, err)
|
||||
continue
|
||||
}
|
||||
if w := buf.String(); w != tt.w {
|
||||
t.Errorf("%s commands = %q, want %q", tt.description, w, tt.w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkPingPong(t *testing.T, buf *bytes.Buffer, c redis.Conn) {
|
||||
resp, err := c.Do("PING")
|
||||
if err != nil {
|
||||
t.Fatal("ping error:", err)
|
||||
}
|
||||
// Close connection to ensure that writes to buf are complete.
|
||||
c.Close()
|
||||
expected := "*1\r\n$4\r\nPING\r\n"
|
||||
actual := buf.String()
|
||||
if actual != expected {
|
||||
t.Errorf("commands = %q, want %q", actual, expected)
|
||||
}
|
||||
if resp != "PONG" {
|
||||
t.Errorf("resp = %v, want %v", resp, "PONG")
|
||||
}
|
||||
}
|
||||
|
||||
const pingResponse = "+PONG\r\n"
|
||||
|
||||
func TestDialURLTLS(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
c, err := redis.DialURL("rediss://example.com/",
|
||||
redis.DialTLSConfig(&clientTLSConfig),
|
||||
dialTestConnTLS(pingResponse, &buf))
|
||||
if err != nil {
|
||||
t.Fatal("dial error:", err)
|
||||
}
|
||||
checkPingPong(t, &buf, c)
|
||||
}
|
||||
|
||||
func TestDialUseTLS(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
c, err := redis.Dial("tcp", "example.com:6379",
|
||||
redis.DialTLSConfig(&clientTLSConfig),
|
||||
dialTestConnTLS(pingResponse, &buf),
|
||||
redis.DialUseTLS(true))
|
||||
if err != nil {
|
||||
t.Fatal("dial error:", err)
|
||||
}
|
||||
checkPingPong(t, &buf, c)
|
||||
}
|
||||
|
||||
func TestDialTLSSKipVerify(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
c, err := redis.Dial("tcp", "example.com:6379",
|
||||
dialTestConnTLS(pingResponse, &buf),
|
||||
redis.DialTLSSkipVerify(true),
|
||||
redis.DialUseTLS(true))
|
||||
if err != nil {
|
||||
t.Fatal("dial error:", err)
|
||||
}
|
||||
checkPingPong(t, &buf, c)
|
||||
}
|
||||
|
||||
// Connect to local instance of Redis running on the default port.
|
||||
func ExampleDial() {
|
||||
c, err := redis.Dial("tcp", ":6379")
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
// Connect to remote instance of Redis using a URL.
|
||||
func ExampleDialURL() {
|
||||
c, err := redis.DialURL(os.Getenv("REDIS_URL"))
|
||||
if err != nil {
|
||||
// handle connection error
|
||||
}
|
||||
defer c.Close()
|
||||
}
|
||||
|
||||
// TextExecError tests handling of errors in a transaction. See
|
||||
// http://redis.io/topics/transactions for information on how Redis handles
|
||||
// errors in a transaction.
|
||||
func TestExecError(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// Execute commands that fail before EXEC is called.
|
||||
|
||||
c.Do("DEL", "k0")
|
||||
c.Do("ZADD", "k0", 0, 0)
|
||||
c.Send("MULTI")
|
||||
c.Send("NOTACOMMAND", "k0", 0, 0)
|
||||
c.Send("ZINCRBY", "k0", 0, 0)
|
||||
v, err := c.Do("EXEC")
|
||||
if err == nil {
|
||||
t.Fatalf("EXEC returned values %v, expected error", v)
|
||||
}
|
||||
|
||||
// Execute commands that fail after EXEC is called. The first command
|
||||
// returns an error.
|
||||
|
||||
c.Do("DEL", "k1")
|
||||
c.Do("ZADD", "k1", 0, 0)
|
||||
c.Send("MULTI")
|
||||
c.Send("HSET", "k1", 0, 0)
|
||||
c.Send("ZINCRBY", "k1", 0, 0)
|
||||
v, err = c.Do("EXEC")
|
||||
if err != nil {
|
||||
t.Fatalf("EXEC returned error %v", err)
|
||||
}
|
||||
|
||||
vs, err := redis.Values(v, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Values(v) returned error %v", err)
|
||||
}
|
||||
|
||||
if len(vs) != 2 {
|
||||
t.Fatalf("len(vs) == %d, want 2", len(vs))
|
||||
}
|
||||
|
||||
if _, ok := vs[0].(error); !ok {
|
||||
t.Fatalf("first result is type %T, expected error", vs[0])
|
||||
}
|
||||
|
||||
if _, ok := vs[1].([]byte); !ok {
|
||||
t.Fatalf("second result is type %T, expected []byte", vs[1])
|
||||
}
|
||||
|
||||
// Execute commands that fail after EXEC is called. The second command
|
||||
// returns an error.
|
||||
|
||||
c.Do("ZADD", "k2", 0, 0)
|
||||
c.Send("MULTI")
|
||||
c.Send("ZINCRBY", "k2", 0, 0)
|
||||
c.Send("HSET", "k2", 0, 0)
|
||||
v, err = c.Do("EXEC")
|
||||
if err != nil {
|
||||
t.Fatalf("EXEC returned error %v", err)
|
||||
}
|
||||
|
||||
vs, err = redis.Values(v, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Values(v) returned error %v", err)
|
||||
}
|
||||
|
||||
if len(vs) != 2 {
|
||||
t.Fatalf("len(vs) == %d, want 2", len(vs))
|
||||
}
|
||||
|
||||
if _, ok := vs[0].([]byte); !ok {
|
||||
t.Fatalf("first result is type %T, expected []byte", vs[0])
|
||||
}
|
||||
|
||||
if _, ok := vs[1].(error); !ok {
|
||||
t.Fatalf("second result is type %T, expected error", vs[2])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDoEmpty(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := c.Do(""); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDoPing(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var clientTLSConfig, serverTLSConfig tls.Config
|
||||
|
||||
func init() {
|
||||
// The certificate and key for testing TLS dial options was created
|
||||
// using the command
|
||||
//
|
||||
// go run GOROOT/src/crypto/tls/generate_cert.go \
|
||||
// --rsa-bits 1024 \
|
||||
// --host 127.0.0.1,::1,example.com --ca \
|
||||
// --start-date "Jan 1 00:00:00 1970" \
|
||||
// --duration=1000000h
|
||||
//
|
||||
// where GOROOT is the value of GOROOT reported by go env.
|
||||
localhostCert := []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICFDCCAX2gAwIBAgIRAJfBL4CUxkXcdlFurb3K+iowDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2
|
||||
MDAwMFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||
gYkCgYEArizw8WxMUQ3bGHLeuJ4fDrEpy+L2pqrbYRlKk1DasJ/VkB8bImzIpe6+
|
||||
LGjiYIxvnDCOJ3f3QplcQuiuMyl6f2irJlJsbFT8Lo/3obnuTKAIaqUdJUqBg6y+
|
||||
JaL8Auk97FvunfKFv8U1AIhgiLzAfQ/3Eaq1yi87Ra6pMjGbTtcCAwEAAaNoMGYw
|
||||
DgYDVR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAA
|
||||
AAAAAAEwDQYJKoZIhvcNAQELBQADgYEAdZ8daIVkyhVwflt5I19m0oq1TycbGO1+
|
||||
ach7T6cZiBQeNR/SJtxr/wKPEpmvUgbv2BfFrKJ8QoIHYsbNSURTWSEa02pfw4k9
|
||||
6RQhij3ZkG79Ituj5OYRORV6Z0HUW32r670BtcuHuAhq7YA6Nxy4FtSt7bAlVdRt
|
||||
rrKgNsltzMk=
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
localhostKey := []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQCuLPDxbExRDdsYct64nh8OsSnL4vamqtthGUqTUNqwn9WQHxsi
|
||||
bMil7r4saOJgjG+cMI4nd/dCmVxC6K4zKXp/aKsmUmxsVPwuj/ehue5MoAhqpR0l
|
||||
SoGDrL4lovwC6T3sW+6d8oW/xTUAiGCIvMB9D/cRqrXKLztFrqkyMZtO1wIDAQAB
|
||||
AoGACrc5G6FOEK6JjDeE/Fa+EmlT6PdNtXNNi+vCas3Opo8u1G8VfEi1D4BgstrB
|
||||
Eq+RLkrOdB8tVyuYQYWPMhabMqF+hhKJN72j0OwfuPlVvTInwb/cKjo/zbH1IA+Y
|
||||
HenHNK4ywv7/p/9/MvQPJ3I32cQBCgGUW5chVSH5M1sj5gECQQDabQAI1X0uDqCm
|
||||
KbX9gXVkAgxkFddrt6LBHt57xujFcqEKFE7nwKhDh7DweVs/VEJ+kpid4z+UnLOw
|
||||
KjtP9JolAkEAzCNBphQ//IsbH5rNs10wIUw3Ks/Oepicvr6kUFbIv+neRzi1iJHa
|
||||
m6H7EayK3PWgax6BAsR/t0Jc9XV7r2muSwJAVzN09BHnK+ADGtNEKLTqXMbEk6B0
|
||||
pDhn7ZmZUOkUPN+Kky+QYM11X6Bob1jDqQDGmymDbGUxGO+GfSofC8inUQJAGfci
|
||||
Eo3g1a6b9JksMPRZeuLG4ZstGErxJRH6tH1Va5PDwitka8qhk8o2tTjNMO3NSdLH
|
||||
diKoXBcE2/Pll5pJoQJBAIMiiMIzXJhnN4mX8may44J/HvMlMf2xuVH2gNMwmZuc
|
||||
Bjqn3yoLHaoZVvbWOi0C2TCN4FjXjaLNZGifQPbIcaA=
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error creating key pair: %v", err))
|
||||
}
|
||||
serverTLSConfig.Certificates = []tls.Certificate{cert}
|
||||
|
||||
certificate, err := x509.ParseCertificate(serverTLSConfig.Certificates[0].Certificate[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error parsing x509 certificate: %v", err))
|
||||
}
|
||||
|
||||
clientTLSConfig.RootCAs = x509.NewCertPool()
|
||||
clientTLSConfig.RootCAs.AddCert(certificate)
|
||||
}
|
||||
|
||||
func TestWithTimeout(t *testing.T) {
|
||||
for _, recv := range []bool{true, false} {
|
||||
for _, defaultTimout := range []time.Duration{0, time.Minute} {
|
||||
var buf bytes.Buffer
|
||||
nc := &testConn{Reader: strings.NewReader("+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n+OK\r\n"), Writer: &buf}
|
||||
c, _ := redis.Dial("", "", redis.DialReadTimeout(defaultTimout), redis.DialNetDial(func(network, addr string) (net.Conn, error) { return nc, nil }))
|
||||
for i := 0; i < 4; i++ {
|
||||
var minDeadline, maxDeadline time.Time
|
||||
|
||||
// Alternate between default and specified timeout.
|
||||
if i%2 == 0 {
|
||||
if defaultTimout != 0 {
|
||||
minDeadline = time.Now().Add(defaultTimout)
|
||||
}
|
||||
if recv {
|
||||
c.Receive()
|
||||
} else {
|
||||
c.Do("PING")
|
||||
}
|
||||
if defaultTimout != 0 {
|
||||
maxDeadline = time.Now().Add(defaultTimout)
|
||||
}
|
||||
} else {
|
||||
timeout := 10 * time.Minute
|
||||
minDeadline = time.Now().Add(timeout)
|
||||
if recv {
|
||||
redis.ReceiveWithTimeout(c, timeout)
|
||||
} else {
|
||||
redis.DoWithTimeout(c, timeout, "PING")
|
||||
}
|
||||
maxDeadline = time.Now().Add(timeout)
|
||||
}
|
||||
|
||||
// Expect set deadline in expected range.
|
||||
if nc.readDeadline.Before(minDeadline) || nc.readDeadline.After(maxDeadline) {
|
||||
t.Errorf("recv %v, %d: do deadline error: %v, %v, %v", recv, i, minDeadline, nc.readDeadline, maxDeadline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
691
vendor/github.com/garyburd/redigo/redis/pool_test.go
generated
vendored
691
vendor/github.com/garyburd/redigo/redis/pool_test.go
generated
vendored
@@ -1,691 +0,0 @@
|
||||
// Copyright 2011 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type poolTestConn struct {
|
||||
d *poolDialer
|
||||
err error
|
||||
redis.Conn
|
||||
}
|
||||
|
||||
func (c *poolTestConn) Close() error {
|
||||
c.d.mu.Lock()
|
||||
c.d.open -= 1
|
||||
c.d.mu.Unlock()
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *poolTestConn) Err() error { return c.err }
|
||||
|
||||
func (c *poolTestConn) Do(commandName string, args ...interface{}) (interface{}, error) {
|
||||
if commandName == "ERR" {
|
||||
c.err = args[0].(error)
|
||||
commandName = "PING"
|
||||
}
|
||||
if commandName != "" {
|
||||
c.d.commands = append(c.d.commands, commandName)
|
||||
}
|
||||
return c.Conn.Do(commandName, args...)
|
||||
}
|
||||
|
||||
func (c *poolTestConn) Send(commandName string, args ...interface{}) error {
|
||||
c.d.commands = append(c.d.commands, commandName)
|
||||
return c.Conn.Send(commandName, args...)
|
||||
}
|
||||
|
||||
type poolDialer struct {
|
||||
mu sync.Mutex
|
||||
t *testing.T
|
||||
dialed int
|
||||
open int
|
||||
commands []string
|
||||
dialErr error
|
||||
}
|
||||
|
||||
func (d *poolDialer) dial() (redis.Conn, error) {
|
||||
d.mu.Lock()
|
||||
d.dialed += 1
|
||||
dialErr := d.dialErr
|
||||
d.mu.Unlock()
|
||||
if dialErr != nil {
|
||||
return nil, d.dialErr
|
||||
}
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.mu.Lock()
|
||||
d.open += 1
|
||||
d.mu.Unlock()
|
||||
return &poolTestConn{d: d, Conn: c}, nil
|
||||
}
|
||||
|
||||
func (d *poolDialer) check(message string, p *redis.Pool, dialed, open, inuse int) {
|
||||
d.mu.Lock()
|
||||
if d.dialed != dialed {
|
||||
d.t.Errorf("%s: dialed=%d, want %d", message, d.dialed, dialed)
|
||||
}
|
||||
if d.open != open {
|
||||
d.t.Errorf("%s: open=%d, want %d", message, d.open, open)
|
||||
}
|
||||
|
||||
stats := p.Stats()
|
||||
|
||||
if stats.ActiveCount != open {
|
||||
d.t.Errorf("%s: active=%d, want %d", message, stats.ActiveCount, open)
|
||||
}
|
||||
if stats.IdleCount != open-inuse {
|
||||
d.t.Errorf("%s: idle=%d, want %d", message, stats.IdleCount, open-inuse)
|
||||
}
|
||||
|
||||
d.mu.Unlock()
|
||||
}
|
||||
|
||||
func TestPoolReuse(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
c1 := p.Get()
|
||||
c1.Do("PING")
|
||||
c2 := p.Get()
|
||||
c2.Do("PING")
|
||||
c1.Close()
|
||||
c2.Close()
|
||||
}
|
||||
|
||||
d.check("before close", p, 2, 2, 0)
|
||||
p.Close()
|
||||
d.check("after close", p, 2, 0, 0)
|
||||
}
|
||||
|
||||
func TestPoolMaxIdle(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
c1 := p.Get()
|
||||
c1.Do("PING")
|
||||
c2 := p.Get()
|
||||
c2.Do("PING")
|
||||
c3 := p.Get()
|
||||
c3.Do("PING")
|
||||
c1.Close()
|
||||
c2.Close()
|
||||
c3.Close()
|
||||
}
|
||||
d.check("before close", p, 12, 2, 0)
|
||||
p.Close()
|
||||
d.check("after close", p, 12, 0, 0)
|
||||
}
|
||||
|
||||
func TestPoolError(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
c.Do("ERR", io.EOF)
|
||||
if c.Err() == nil {
|
||||
t.Errorf("expected c.Err() != nil")
|
||||
}
|
||||
c.Close()
|
||||
|
||||
c = p.Get()
|
||||
c.Do("ERR", io.EOF)
|
||||
c.Close()
|
||||
|
||||
d.check(".", p, 2, 0, 0)
|
||||
}
|
||||
|
||||
func TestPoolClose(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c1 := p.Get()
|
||||
c1.Do("PING")
|
||||
c2 := p.Get()
|
||||
c2.Do("PING")
|
||||
c3 := p.Get()
|
||||
c3.Do("PING")
|
||||
|
||||
c1.Close()
|
||||
if _, err := c1.Do("PING"); err == nil {
|
||||
t.Errorf("expected error after connection closed")
|
||||
}
|
||||
|
||||
c2.Close()
|
||||
c2.Close()
|
||||
|
||||
p.Close()
|
||||
|
||||
d.check("after pool close", p, 3, 1, 1)
|
||||
|
||||
if _, err := c1.Do("PING"); err == nil {
|
||||
t.Errorf("expected error after connection and pool closed")
|
||||
}
|
||||
|
||||
c3.Close()
|
||||
|
||||
d.check("after conn close", p, 3, 0, 0)
|
||||
|
||||
c1 = p.Get()
|
||||
if _, err := c1.Do("PING"); err == nil {
|
||||
t.Errorf("expected error after pool closed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoolTimeout(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
IdleTimeout: 300 * time.Second,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
now := time.Now()
|
||||
redis.SetNowFunc(func() time.Time { return now })
|
||||
defer redis.SetNowFunc(time.Now)
|
||||
|
||||
c := p.Get()
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
d.check("1", p, 1, 1, 0)
|
||||
|
||||
now = now.Add(p.IdleTimeout)
|
||||
|
||||
c = p.Get()
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
d.check("2", p, 2, 1, 0)
|
||||
}
|
||||
|
||||
func TestPoolConcurrenSendReceive(t *testing.T) {
|
||||
p := &redis.Pool{
|
||||
Dial: redis.DialDefaultServer,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := c.Receive()
|
||||
done <- err
|
||||
}()
|
||||
c.Send("PING")
|
||||
c.Flush()
|
||||
err := <-done
|
||||
if err != nil {
|
||||
t.Fatalf("Receive() returned error %v", err)
|
||||
}
|
||||
_, err = c.Do("")
|
||||
if err != nil {
|
||||
t.Fatalf("Do() returned error %v", err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
|
||||
func TestPoolBorrowCheck(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
Dial: d.dial,
|
||||
TestOnBorrow: func(redis.Conn, time.Time) error { return redis.Error("BLAH") },
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
c := p.Get()
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
}
|
||||
d.check("1", p, 10, 1, 0)
|
||||
}
|
||||
|
||||
func TestPoolMaxActive(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c1 := p.Get()
|
||||
c1.Do("PING")
|
||||
c2 := p.Get()
|
||||
c2.Do("PING")
|
||||
|
||||
d.check("1", p, 2, 2, 2)
|
||||
|
||||
c3 := p.Get()
|
||||
if _, err := c3.Do("PING"); err != redis.ErrPoolExhausted {
|
||||
t.Errorf("expected pool exhausted")
|
||||
}
|
||||
|
||||
c3.Close()
|
||||
d.check("2", p, 2, 2, 2)
|
||||
c2.Close()
|
||||
d.check("3", p, 2, 2, 1)
|
||||
|
||||
c3 = p.Get()
|
||||
if _, err := c3.Do("PING"); err != nil {
|
||||
t.Errorf("expected good channel, err=%v", err)
|
||||
}
|
||||
c3.Close()
|
||||
|
||||
d.check("4", p, 2, 2, 1)
|
||||
}
|
||||
|
||||
func TestPoolMonitorCleanup(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
c.Send("MONITOR")
|
||||
c.Close()
|
||||
|
||||
d.check("", p, 1, 0, 0)
|
||||
}
|
||||
|
||||
func TestPoolPubSubCleanup(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
c.Send("SUBSCRIBE", "x")
|
||||
c.Close()
|
||||
|
||||
want := []string{"SUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
|
||||
c = p.Get()
|
||||
c.Send("PSUBSCRIBE", "x*")
|
||||
c.Close()
|
||||
|
||||
want = []string{"PSUBSCRIBE", "UNSUBSCRIBE", "PUNSUBSCRIBE", "ECHO"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
}
|
||||
|
||||
func TestPoolTransactionCleanup(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 2,
|
||||
MaxActive: 2,
|
||||
Dial: d.dial,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
c.Do("WATCH", "key")
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
want := []string{"WATCH", "PING", "UNWATCH"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
|
||||
c = p.Get()
|
||||
c.Do("WATCH", "key")
|
||||
c.Do("UNWATCH")
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
want = []string{"WATCH", "UNWATCH", "PING"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
|
||||
c = p.Get()
|
||||
c.Do("WATCH", "key")
|
||||
c.Do("MULTI")
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
want = []string{"WATCH", "MULTI", "PING", "DISCARD"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
|
||||
c = p.Get()
|
||||
c.Do("WATCH", "key")
|
||||
c.Do("MULTI")
|
||||
c.Do("DISCARD")
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
want = []string{"WATCH", "MULTI", "DISCARD", "PING"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
|
||||
c = p.Get()
|
||||
c.Do("WATCH", "key")
|
||||
c.Do("MULTI")
|
||||
c.Do("EXEC")
|
||||
c.Do("PING")
|
||||
c.Close()
|
||||
|
||||
want = []string{"WATCH", "MULTI", "EXEC", "PING"}
|
||||
if !reflect.DeepEqual(d.commands, want) {
|
||||
t.Errorf("got commands %v, want %v", d.commands, want)
|
||||
}
|
||||
d.commands = nil
|
||||
}
|
||||
|
||||
func startGoroutines(p *redis.Pool, cmd string, args ...interface{}) chan error {
|
||||
errs := make(chan error, 10)
|
||||
for i := 0; i < cap(errs); i++ {
|
||||
go func() {
|
||||
c := p.Get()
|
||||
_, err := c.Do(cmd, args...)
|
||||
c.Close()
|
||||
errs <- err
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait for goroutines to block.
|
||||
time.Sleep(time.Second / 4)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func TestWaitPool(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 1,
|
||||
MaxActive: 1,
|
||||
Dial: d.dial,
|
||||
Wait: true,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
errs := startGoroutines(p, "PING")
|
||||
d.check("before close", p, 1, 1, 1)
|
||||
c.Close()
|
||||
timeout := time.After(2 * time.Second)
|
||||
for i := 0; i < cap(errs); i++ {
|
||||
select {
|
||||
case err := <-errs:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-timeout:
|
||||
t.Fatalf("timeout waiting for blocked goroutine %d", i)
|
||||
}
|
||||
}
|
||||
d.check("done", p, 1, 1, 0)
|
||||
}
|
||||
|
||||
func TestWaitPoolClose(t *testing.T) {
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 1,
|
||||
MaxActive: 1,
|
||||
Dial: d.dial,
|
||||
Wait: true,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
errs := startGoroutines(p, "PING")
|
||||
d.check("before close", p, 1, 1, 1)
|
||||
p.Close()
|
||||
timeout := time.After(2 * time.Second)
|
||||
for i := 0; i < cap(errs); i++ {
|
||||
select {
|
||||
case err := <-errs:
|
||||
switch err {
|
||||
case nil:
|
||||
t.Fatal("blocked goroutine did not get error")
|
||||
case redis.ErrPoolExhausted:
|
||||
t.Fatal("blocked goroutine got pool exhausted error")
|
||||
}
|
||||
case <-timeout:
|
||||
t.Fatal("timeout waiting for blocked goroutine")
|
||||
}
|
||||
}
|
||||
c.Close()
|
||||
d.check("done", p, 1, 0, 0)
|
||||
}
|
||||
|
||||
func TestWaitPoolCommandError(t *testing.T) {
|
||||
testErr := errors.New("test")
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 1,
|
||||
MaxActive: 1,
|
||||
Dial: d.dial,
|
||||
Wait: true,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
errs := startGoroutines(p, "ERR", testErr)
|
||||
d.check("before close", p, 1, 1, 1)
|
||||
c.Close()
|
||||
timeout := time.After(2 * time.Second)
|
||||
for i := 0; i < cap(errs); i++ {
|
||||
select {
|
||||
case err := <-errs:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-timeout:
|
||||
t.Fatalf("timeout waiting for blocked goroutine %d", i)
|
||||
}
|
||||
}
|
||||
d.check("done", p, cap(errs), 0, 0)
|
||||
}
|
||||
|
||||
func TestWaitPoolDialError(t *testing.T) {
|
||||
testErr := errors.New("test")
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: 1,
|
||||
MaxActive: 1,
|
||||
Dial: d.dial,
|
||||
Wait: true,
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
c := p.Get()
|
||||
errs := startGoroutines(p, "ERR", testErr)
|
||||
d.check("before close", p, 1, 1, 1)
|
||||
|
||||
d.dialErr = errors.New("dial")
|
||||
c.Close()
|
||||
|
||||
nilCount := 0
|
||||
errCount := 0
|
||||
timeout := time.After(2 * time.Second)
|
||||
for i := 0; i < cap(errs); i++ {
|
||||
select {
|
||||
case err := <-errs:
|
||||
switch err {
|
||||
case nil:
|
||||
nilCount++
|
||||
case d.dialErr:
|
||||
errCount++
|
||||
default:
|
||||
t.Fatalf("expected dial error or nil, got %v", err)
|
||||
}
|
||||
case <-timeout:
|
||||
t.Fatalf("timeout waiting for blocked goroutine %d", i)
|
||||
}
|
||||
}
|
||||
if nilCount != 1 {
|
||||
t.Errorf("expected one nil error, got %d", nilCount)
|
||||
}
|
||||
if errCount != cap(errs)-1 {
|
||||
t.Errorf("expected %d dial errors, got %d", cap(errs)-1, errCount)
|
||||
}
|
||||
d.check("done", p, cap(errs), 0, 0)
|
||||
}
|
||||
|
||||
// Borrowing requires us to iterate over the idle connections, unlock the pool,
|
||||
// and perform a blocking operation to check the connection still works. If
|
||||
// TestOnBorrow fails, we must reacquire the lock and continue iteration. This
|
||||
// test ensures that iteration will work correctly if multiple threads are
|
||||
// iterating simultaneously.
|
||||
func TestLocking_TestOnBorrowFails_PoolDoesntCrash(t *testing.T) {
|
||||
const count = 100
|
||||
|
||||
// First we'll Create a pool where the pilfering of idle connections fails.
|
||||
d := poolDialer{t: t}
|
||||
p := &redis.Pool{
|
||||
MaxIdle: count,
|
||||
MaxActive: count,
|
||||
Dial: d.dial,
|
||||
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
||||
return errors.New("No way back into the real world.")
|
||||
},
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
// Fill the pool with idle connections.
|
||||
conns := make([]redis.Conn, count)
|
||||
for i := range conns {
|
||||
conns[i] = p.Get()
|
||||
}
|
||||
for i := range conns {
|
||||
conns[i].Close()
|
||||
}
|
||||
|
||||
// Spawn a bunch of goroutines to thrash the pool.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(count)
|
||||
for i := 0; i < count; i++ {
|
||||
go func() {
|
||||
c := p.Get()
|
||||
if c.Err() != nil {
|
||||
t.Errorf("pool get failed: %v", c.Err())
|
||||
}
|
||||
c.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if d.dialed != count*2 {
|
||||
t.Errorf("Expected %d dials, got %d", count*2, d.dialed)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPoolGet(b *testing.B) {
|
||||
b.StopTimer()
|
||||
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
|
||||
c := p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
defer p.Close()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c = p.Get()
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPoolGetErr(b *testing.B) {
|
||||
b.StopTimer()
|
||||
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
|
||||
c := p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
defer p.Close()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c = p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPoolGetPing(b *testing.B) {
|
||||
b.StopTimer()
|
||||
p := redis.Pool{Dial: redis.DialDefaultServer, MaxIdle: 2}
|
||||
c := p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
defer p.Close()
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c = p.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
165
vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go
generated
vendored
165
vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go
generated
vendored
@@ -1,165 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// 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.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
// listenPubSubChannels listens for messages on Redis pubsub channels. The
|
||||
// onStart function is called after the channels are subscribed. The onMessage
|
||||
// function is called for each message.
|
||||
func listenPubSubChannels(ctx context.Context, redisServerAddr string,
|
||||
onStart func() error,
|
||||
onMessage func(channel string, data []byte) error,
|
||||
channels ...string) error {
|
||||
// A ping is set to the server with this period to test for the health of
|
||||
// the connection and server.
|
||||
const healthCheckPeriod = time.Minute
|
||||
|
||||
c, err := redis.Dial("tcp", redisServerAddr,
|
||||
// Read timeout on server should be greater than ping period.
|
||||
redis.DialReadTimeout(healthCheckPeriod+10*time.Second),
|
||||
redis.DialWriteTimeout(10*time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
psc := redis.PubSubConn{Conn: c}
|
||||
|
||||
if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done := make(chan error, 1)
|
||||
|
||||
// Start a goroutine to receive notifications from the server.
|
||||
go func() {
|
||||
for {
|
||||
switch n := psc.Receive().(type) {
|
||||
case error:
|
||||
done <- n
|
||||
return
|
||||
case redis.Message:
|
||||
if err := onMessage(n.Channel, n.Data); err != nil {
|
||||
done <- err
|
||||
return
|
||||
}
|
||||
case redis.Subscription:
|
||||
switch n.Count {
|
||||
case len(channels):
|
||||
// Notify application when all channels are subscribed.
|
||||
if err := onStart(); err != nil {
|
||||
done <- err
|
||||
return
|
||||
}
|
||||
case 0:
|
||||
// Return from the goroutine when all channels are unsubscribed.
|
||||
done <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(healthCheckPeriod)
|
||||
defer ticker.Stop()
|
||||
loop:
|
||||
for err == nil {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Send ping to test health of connection and server. If
|
||||
// corresponding pong is not received, then receive on the
|
||||
// connection will timeout and the receive goroutine will exit.
|
||||
if err = psc.Ping(""); err != nil {
|
||||
break loop
|
||||
}
|
||||
case <-ctx.Done():
|
||||
break loop
|
||||
case err := <-done:
|
||||
// Return error from the receive goroutine.
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Signal the receiving goroutine to exit by unsubscribing from all channels.
|
||||
psc.Unsubscribe()
|
||||
|
||||
// Wait for goroutine to complete.
|
||||
return <-done
|
||||
}
|
||||
|
||||
func publish() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("PUBLISH", "c1", "hello")
|
||||
c.Do("PUBLISH", "c2", "world")
|
||||
c.Do("PUBLISH", "c1", "goodbye")
|
||||
}
|
||||
|
||||
// This example shows how receive pubsub notifications with cancelation and
|
||||
// health checks.
|
||||
func ExamplePubSubConn() {
|
||||
redisServerAddr, err := serverAddr()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
err = listenPubSubChannels(ctx,
|
||||
redisServerAddr,
|
||||
func() error {
|
||||
// The start callback is a good place to backfill missed
|
||||
// notifications. For the purpose of this example, a goroutine is
|
||||
// started to send notifications.
|
||||
go publish()
|
||||
return nil
|
||||
},
|
||||
func(channel string, message []byte) error {
|
||||
fmt.Printf("channel: %s, message: %s\n", channel, message)
|
||||
|
||||
// For the purpose of this example, cancel the listener's context
|
||||
// after receiving last message sent by publish().
|
||||
if string(message) == "goodbye" {
|
||||
cancel()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"c1", "c2")
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Output:
|
||||
// channel: c1, message: hello
|
||||
// channel: c2, message: world
|
||||
// channel: c1, message: goodbye
|
||||
}
|
||||
74
vendor/github.com/garyburd/redigo/redis/pubsub_test.go
generated
vendored
74
vendor/github.com/garyburd/redigo/redis/pubsub_test.go
generated
vendored
@@ -1,74 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) {
|
||||
actual := c.Receive()
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("%s = %v, want %v", message, actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushed(t *testing.T) {
|
||||
pc, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer pc.Close()
|
||||
|
||||
sc, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer sc.Close()
|
||||
|
||||
c := redis.PubSubConn{Conn: sc}
|
||||
|
||||
c.Subscribe("c1")
|
||||
expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1})
|
||||
c.Subscribe("c2")
|
||||
expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2})
|
||||
c.PSubscribe("p1")
|
||||
expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3})
|
||||
c.PSubscribe("p2")
|
||||
expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4})
|
||||
c.PUnsubscribe()
|
||||
expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3})
|
||||
expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2})
|
||||
|
||||
pc.Do("PUBLISH", "c1", "hello")
|
||||
expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")})
|
||||
|
||||
c.Ping("hello")
|
||||
expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"})
|
||||
|
||||
c.Conn.Send("PING")
|
||||
c.Conn.Flush()
|
||||
expectPushed(t, c, `Send("PING")`, redis.Pong{})
|
||||
|
||||
c.Ping("timeout")
|
||||
got := c.ReceiveWithTimeout(time.Minute)
|
||||
if want := (redis.Pong{Data: "timeout"}); want != got {
|
||||
t.Errorf("recv /w timeout got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
71
vendor/github.com/garyburd/redigo/redis/redis_test.go
generated
vendored
71
vendor/github.com/garyburd/redigo/redis/redis_test.go
generated
vendored
@@ -1,71 +0,0 @@
|
||||
// Copyright 2017 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type timeoutTestConn int
|
||||
|
||||
func (tc timeoutTestConn) Do(string, ...interface{}) (interface{}, error) {
|
||||
return time.Duration(-1), nil
|
||||
}
|
||||
func (tc timeoutTestConn) DoWithTimeout(timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
|
||||
return timeout, nil
|
||||
}
|
||||
|
||||
func (tc timeoutTestConn) Receive() (interface{}, error) {
|
||||
return time.Duration(-1), nil
|
||||
}
|
||||
func (tc timeoutTestConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) {
|
||||
return timeout, nil
|
||||
}
|
||||
|
||||
func (tc timeoutTestConn) Send(string, ...interface{}) error { return nil }
|
||||
func (tc timeoutTestConn) Err() error { return nil }
|
||||
func (tc timeoutTestConn) Close() error { return nil }
|
||||
func (tc timeoutTestConn) Flush() error { return nil }
|
||||
|
||||
func testTimeout(t *testing.T, c redis.Conn) {
|
||||
r, err := c.Do("PING")
|
||||
if r != time.Duration(-1) || err != nil {
|
||||
t.Errorf("Do() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil)
|
||||
}
|
||||
r, err = redis.DoWithTimeout(c, time.Minute, "PING")
|
||||
if r != time.Minute || err != nil {
|
||||
t.Errorf("DoWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil)
|
||||
}
|
||||
r, err = c.Receive()
|
||||
if r != time.Duration(-1) || err != nil {
|
||||
t.Errorf("Receive() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil)
|
||||
}
|
||||
r, err = redis.ReceiveWithTimeout(c, time.Minute)
|
||||
if r != time.Minute || err != nil {
|
||||
t.Errorf("ReceiveWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnTimeout(t *testing.T) {
|
||||
testTimeout(t, timeoutTestConn(0))
|
||||
}
|
||||
|
||||
func TestPoolConnTimeout(t *testing.T) {
|
||||
p := &redis.Pool{Dial: func() (redis.Conn, error) { return timeoutTestConn(0), nil }}
|
||||
testTimeout(t, p.Get())
|
||||
}
|
||||
209
vendor/github.com/garyburd/redigo/redis/reply_test.go
generated
vendored
209
vendor/github.com/garyburd/redigo/redis/reply_test.go
generated
vendored
@@ -1,209 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type valueError struct {
|
||||
v interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
func ve(v interface{}, err error) valueError {
|
||||
return valueError{v, err}
|
||||
}
|
||||
|
||||
var replyTests = []struct {
|
||||
name interface{}
|
||||
actual valueError
|
||||
expected valueError
|
||||
}{
|
||||
{
|
||||
"ints([[]byte, []byte])",
|
||||
ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)),
|
||||
ve([]int{4, 5}, nil),
|
||||
},
|
||||
{
|
||||
"ints([nt64, int64])",
|
||||
ve(redis.Ints([]interface{}{int64(4), int64(5)}, nil)),
|
||||
ve([]int{4, 5}, nil),
|
||||
},
|
||||
{
|
||||
"ints([[]byte, nil, []byte])",
|
||||
ve(redis.Ints([]interface{}{[]byte("4"), nil, []byte("5")}, nil)),
|
||||
ve([]int{4, 0, 5}, nil),
|
||||
},
|
||||
{
|
||||
"ints(nil)",
|
||||
ve(redis.Ints(nil, nil)),
|
||||
ve([]int(nil), redis.ErrNil),
|
||||
},
|
||||
{
|
||||
"int64s([[]byte, []byte])",
|
||||
ve(redis.Int64s([]interface{}{[]byte("4"), []byte("5")}, nil)),
|
||||
ve([]int64{4, 5}, nil),
|
||||
},
|
||||
{
|
||||
"int64s([int64, int64])",
|
||||
ve(redis.Int64s([]interface{}{int64(4), int64(5)}, nil)),
|
||||
ve([]int64{4, 5}, nil),
|
||||
},
|
||||
{
|
||||
"strings([[]byte, []bytev2])",
|
||||
ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
|
||||
ve([]string{"v1", "v2"}, nil),
|
||||
},
|
||||
{
|
||||
"strings([string, string])",
|
||||
ve(redis.Strings([]interface{}{"v1", "v2"}, nil)),
|
||||
ve([]string{"v1", "v2"}, nil),
|
||||
},
|
||||
{
|
||||
"byteslices([v1, v2])",
|
||||
ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
|
||||
ve([][]byte{[]byte("v1"), []byte("v2")}, nil),
|
||||
},
|
||||
{
|
||||
"float64s([v1, v2])",
|
||||
ve(redis.Float64s([]interface{}{[]byte("1.234"), []byte("5.678")}, nil)),
|
||||
ve([]float64{1.234, 5.678}, nil),
|
||||
},
|
||||
{
|
||||
"values([v1, v2])",
|
||||
ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)),
|
||||
ve([]interface{}{[]byte("v1"), []byte("v2")}, nil),
|
||||
},
|
||||
{
|
||||
"values(nil)",
|
||||
ve(redis.Values(nil, nil)),
|
||||
ve([]interface{}(nil), redis.ErrNil),
|
||||
},
|
||||
{
|
||||
"float64(1.0)",
|
||||
ve(redis.Float64([]byte("1.0"), nil)),
|
||||
ve(float64(1.0), nil),
|
||||
},
|
||||
{
|
||||
"float64(nil)",
|
||||
ve(redis.Float64(nil, nil)),
|
||||
ve(float64(0.0), redis.ErrNil),
|
||||
},
|
||||
{
|
||||
"uint64(1)",
|
||||
ve(redis.Uint64(int64(1), nil)),
|
||||
ve(uint64(1), nil),
|
||||
},
|
||||
{
|
||||
"uint64(-1)",
|
||||
ve(redis.Uint64(int64(-1), nil)),
|
||||
ve(uint64(0), redis.ErrNegativeInt),
|
||||
},
|
||||
{
|
||||
"positions([[1, 2], nil, [3, 4]])",
|
||||
ve(redis.Positions([]interface{}{[]interface{}{[]byte("1"), []byte("2")}, nil, []interface{}{[]byte("3"), []byte("4")}}, nil)),
|
||||
ve([]*[2]float64{{1.0, 2.0}, nil, {3.0, 4.0}}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
func TestReply(t *testing.T) {
|
||||
for _, rt := range replyTests {
|
||||
if rt.actual.err != rt.expected.err {
|
||||
t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(rt.actual.v, rt.expected.v) {
|
||||
t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dial wraps DialDefaultServer() with a more suitable function name for examples.
|
||||
func dial() (redis.Conn, error) {
|
||||
return redis.DialDefaultServer()
|
||||
}
|
||||
|
||||
// serverAddr wraps DefaultServerAddr() with a more suitable function name for examples.
|
||||
func serverAddr() (string, error) {
|
||||
return redis.DefaultServerAddr()
|
||||
}
|
||||
|
||||
func ExampleBool() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("SET", "foo", 1)
|
||||
exists, _ := redis.Bool(c.Do("EXISTS", "foo"))
|
||||
fmt.Printf("%#v\n", exists)
|
||||
// Output:
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleInt() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("SET", "k1", 1)
|
||||
n, _ := redis.Int(c.Do("GET", "k1"))
|
||||
fmt.Printf("%#v\n", n)
|
||||
n, _ = redis.Int(c.Do("INCR", "k1"))
|
||||
fmt.Printf("%#v\n", n)
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleInts() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("SADD", "set_with_integers", 4, 5, 6)
|
||||
ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers"))
|
||||
fmt.Printf("%#v\n", ints)
|
||||
// Output:
|
||||
// []int{4, 5, 6}
|
||||
}
|
||||
|
||||
func ExampleString() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Do("SET", "hello", "world")
|
||||
s, err := redis.String(c.Do("GET", "hello"))
|
||||
fmt.Printf("%#v\n", s)
|
||||
// Output:
|
||||
// "world"
|
||||
}
|
||||
494
vendor/github.com/garyburd/redigo/redis/scan_test.go
generated
vendored
494
vendor/github.com/garyburd/redigo/redis/scan_test.go
generated
vendored
@@ -1,494 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
type durationScan struct {
|
||||
time.Duration `redis:"sd"`
|
||||
}
|
||||
|
||||
func (t *durationScan) RedisScan(src interface{}) (err error) {
|
||||
if t == nil {
|
||||
return fmt.Errorf("nil pointer")
|
||||
}
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
t.Duration, err = time.ParseDuration(src)
|
||||
case []byte:
|
||||
t.Duration, err = time.ParseDuration(string(src))
|
||||
case int64:
|
||||
t.Duration = time.Duration(src)
|
||||
default:
|
||||
err = fmt.Errorf("cannot convert from %T to %T", src, t)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var scanConversionTests = []struct {
|
||||
src interface{}
|
||||
dest interface{}
|
||||
}{
|
||||
{[]byte("-inf"), math.Inf(-1)},
|
||||
{[]byte("+inf"), math.Inf(1)},
|
||||
{[]byte("0"), float64(0)},
|
||||
{[]byte("3.14159"), float64(3.14159)},
|
||||
{[]byte("3.14"), float32(3.14)},
|
||||
{[]byte("-100"), int(-100)},
|
||||
{[]byte("101"), int(101)},
|
||||
{int64(102), int(102)},
|
||||
{[]byte("103"), uint(103)},
|
||||
{int64(104), uint(104)},
|
||||
{[]byte("105"), int8(105)},
|
||||
{int64(106), int8(106)},
|
||||
{[]byte("107"), uint8(107)},
|
||||
{int64(108), uint8(108)},
|
||||
{[]byte("0"), false},
|
||||
{int64(0), false},
|
||||
{[]byte("f"), false},
|
||||
{[]byte("1"), true},
|
||||
{int64(1), true},
|
||||
{[]byte("t"), true},
|
||||
{"hello", "hello"},
|
||||
{[]byte("hello"), "hello"},
|
||||
{[]byte("world"), []byte("world")},
|
||||
{[]interface{}{[]byte("foo")}, []interface{}{[]byte("foo")}},
|
||||
{[]interface{}{[]byte("foo")}, []string{"foo"}},
|
||||
{[]interface{}{[]byte("hello"), []byte("world")}, []string{"hello", "world"}},
|
||||
{[]interface{}{[]byte("bar")}, [][]byte{[]byte("bar")}},
|
||||
{[]interface{}{[]byte("1")}, []int{1}},
|
||||
{[]interface{}{[]byte("1"), []byte("2")}, []int{1, 2}},
|
||||
{[]interface{}{[]byte("1"), []byte("2")}, []float64{1, 2}},
|
||||
{[]interface{}{[]byte("1")}, []byte{1}},
|
||||
{[]interface{}{[]byte("1")}, []bool{true}},
|
||||
{"1m", durationScan{Duration: time.Minute}},
|
||||
{[]byte("1m"), durationScan{Duration: time.Minute}},
|
||||
{time.Minute.Nanoseconds(), durationScan{Duration: time.Minute}},
|
||||
{[]interface{}{[]byte("1m")}, []durationScan{durationScan{Duration: time.Minute}}},
|
||||
{[]interface{}{[]byte("1m")}, []*durationScan{&durationScan{Duration: time.Minute}}},
|
||||
}
|
||||
|
||||
func TestScanConversion(t *testing.T) {
|
||||
for _, tt := range scanConversionTests {
|
||||
values := []interface{}{tt.src}
|
||||
dest := reflect.New(reflect.TypeOf(tt.dest))
|
||||
values, err := redis.Scan(values, dest.Interface())
|
||||
if err != nil {
|
||||
t.Errorf("Scan(%v) returned error %v", tt, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.dest, dest.Elem().Interface()) {
|
||||
t.Errorf("Scan(%v) returned %v, want %v", tt, dest.Elem().Interface(), tt.dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var scanConversionErrorTests = []struct {
|
||||
src interface{}
|
||||
dest interface{}
|
||||
}{
|
||||
{[]byte("1234"), byte(0)},
|
||||
{int64(1234), byte(0)},
|
||||
{[]byte("-1"), byte(0)},
|
||||
{int64(-1), byte(0)},
|
||||
{[]byte("junk"), false},
|
||||
{redis.Error("blah"), false},
|
||||
{redis.Error("blah"), durationScan{Duration: time.Minute}},
|
||||
{"invalid", durationScan{Duration: time.Minute}},
|
||||
}
|
||||
|
||||
func TestScanConversionError(t *testing.T) {
|
||||
for _, tt := range scanConversionErrorTests {
|
||||
values := []interface{}{tt.src}
|
||||
dest := reflect.New(reflect.TypeOf(tt.dest))
|
||||
values, err := redis.Scan(values, dest.Interface())
|
||||
if err == nil {
|
||||
t.Errorf("Scan(%v) did not return error", tt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleScan() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
|
||||
c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
|
||||
c.Send("HMSET", "album:3", "title", "Beat")
|
||||
c.Send("LPUSH", "albums", "1")
|
||||
c.Send("LPUSH", "albums", "2")
|
||||
c.Send("LPUSH", "albums", "3")
|
||||
values, err := redis.Values(c.Do("SORT", "albums",
|
||||
"BY", "album:*->rating",
|
||||
"GET", "album:*->title",
|
||||
"GET", "album:*->rating"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for len(values) > 0 {
|
||||
var title string
|
||||
rating := -1 // initialize to illegal value to detect nil.
|
||||
values, err = redis.Scan(values, &title, &rating)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if rating == -1 {
|
||||
fmt.Println(title, "not-rated")
|
||||
} else {
|
||||
fmt.Println(title, rating)
|
||||
}
|
||||
}
|
||||
// Output:
|
||||
// Beat not-rated
|
||||
// Earthbound 1
|
||||
// Red 5
|
||||
}
|
||||
|
||||
type s0 struct {
|
||||
X int
|
||||
Y int `redis:"y"`
|
||||
Bt bool
|
||||
}
|
||||
|
||||
type s1 struct {
|
||||
X int `redis:"-"`
|
||||
I int `redis:"i"`
|
||||
U uint `redis:"u"`
|
||||
S string `redis:"s"`
|
||||
P []byte `redis:"p"`
|
||||
B bool `redis:"b"`
|
||||
Bt bool
|
||||
Bf bool
|
||||
s0
|
||||
Sd durationScan `redis:"sd"`
|
||||
Sdp *durationScan `redis:"sdp"`
|
||||
}
|
||||
|
||||
var scanStructTests = []struct {
|
||||
title string
|
||||
reply []string
|
||||
value interface{}
|
||||
}{
|
||||
{"basic",
|
||||
[]string{
|
||||
"i", "-1234",
|
||||
"u", "5678",
|
||||
"s", "hello",
|
||||
"p", "world",
|
||||
"b", "t",
|
||||
"Bt", "1",
|
||||
"Bf", "0",
|
||||
"X", "123",
|
||||
"y", "456",
|
||||
"sd", "1m",
|
||||
"sdp", "1m",
|
||||
},
|
||||
&s1{
|
||||
I: -1234,
|
||||
U: 5678,
|
||||
S: "hello",
|
||||
P: []byte("world"),
|
||||
B: true,
|
||||
Bt: true,
|
||||
Bf: false,
|
||||
s0: s0{X: 123, Y: 456},
|
||||
Sd: durationScan{Duration: time.Minute},
|
||||
Sdp: &durationScan{Duration: time.Minute},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestScanStruct(t *testing.T) {
|
||||
for _, tt := range scanStructTests {
|
||||
|
||||
var reply []interface{}
|
||||
for _, v := range tt.reply {
|
||||
reply = append(reply, []byte(v))
|
||||
}
|
||||
|
||||
value := reflect.New(reflect.ValueOf(tt.value).Type().Elem())
|
||||
|
||||
if err := redis.ScanStruct(reply, value.Interface()); err != nil {
|
||||
t.Fatalf("ScanStruct(%s) returned error %v", tt.title, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(value.Interface(), tt.value) {
|
||||
t.Fatalf("ScanStruct(%s) returned %v, want %v", tt.title, value.Interface(), tt.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadScanStructArgs(t *testing.T) {
|
||||
x := []interface{}{"A", "b"}
|
||||
test := func(v interface{}) {
|
||||
if err := redis.ScanStruct(x, v); err == nil {
|
||||
t.Errorf("Expect error for ScanStruct(%T, %T)", x, v)
|
||||
}
|
||||
}
|
||||
|
||||
test(nil)
|
||||
|
||||
var v0 *struct{}
|
||||
test(v0)
|
||||
|
||||
var v1 int
|
||||
test(&v1)
|
||||
|
||||
x = x[:1]
|
||||
v2 := struct{ A string }{}
|
||||
test(&v2)
|
||||
}
|
||||
|
||||
var scanSliceTests = []struct {
|
||||
src []interface{}
|
||||
fieldNames []string
|
||||
ok bool
|
||||
dest interface{}
|
||||
}{
|
||||
{
|
||||
[]interface{}{[]byte("1"), nil, []byte("-1")},
|
||||
nil,
|
||||
true,
|
||||
[]int{1, 0, -1},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("1"), nil, []byte("2")},
|
||||
nil,
|
||||
true,
|
||||
[]uint{1, 0, 2},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("-1")},
|
||||
nil,
|
||||
false,
|
||||
[]uint{1},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("hello"), nil, []byte("world")},
|
||||
nil,
|
||||
true,
|
||||
[][]byte{[]byte("hello"), nil, []byte("world")},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("hello"), nil, []byte("world")},
|
||||
nil,
|
||||
true,
|
||||
[]string{"hello", "", "world"},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
|
||||
nil,
|
||||
true,
|
||||
[]struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("a1"), []byte("b1")},
|
||||
nil,
|
||||
false,
|
||||
[]struct{ A, B, C string }{{"a1", "b1", ""}},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
|
||||
nil,
|
||||
true,
|
||||
[]*struct{ A, B string }{{"a1", "b1"}, {"a2", "b2"}},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
|
||||
[]string{"A", "B"},
|
||||
true,
|
||||
[]struct{ A, C, B string }{{"a1", "", "b1"}, {"a2", "", "b2"}},
|
||||
},
|
||||
{
|
||||
[]interface{}{[]byte("a1"), []byte("b1"), []byte("a2"), []byte("b2")},
|
||||
nil,
|
||||
false,
|
||||
[]struct{}{},
|
||||
},
|
||||
}
|
||||
|
||||
func TestScanSlice(t *testing.T) {
|
||||
for _, tt := range scanSliceTests {
|
||||
|
||||
typ := reflect.ValueOf(tt.dest).Type()
|
||||
dest := reflect.New(typ)
|
||||
|
||||
err := redis.ScanSlice(tt.src, dest.Interface(), tt.fieldNames...)
|
||||
if tt.ok != (err == nil) {
|
||||
t.Errorf("ScanSlice(%v, []%s, %v) returned error %v", tt.src, typ, tt.fieldNames, err)
|
||||
continue
|
||||
}
|
||||
if tt.ok && !reflect.DeepEqual(dest.Elem().Interface(), tt.dest) {
|
||||
t.Errorf("ScanSlice(src, []%s) returned %#v, want %#v", typ, dest.Elem().Interface(), tt.dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleScanSlice() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
c.Send("HMSET", "album:1", "title", "Red", "rating", 5)
|
||||
c.Send("HMSET", "album:2", "title", "Earthbound", "rating", 1)
|
||||
c.Send("HMSET", "album:3", "title", "Beat", "rating", 4)
|
||||
c.Send("LPUSH", "albums", "1")
|
||||
c.Send("LPUSH", "albums", "2")
|
||||
c.Send("LPUSH", "albums", "3")
|
||||
values, err := redis.Values(c.Do("SORT", "albums",
|
||||
"BY", "album:*->rating",
|
||||
"GET", "album:*->title",
|
||||
"GET", "album:*->rating"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
var albums []struct {
|
||||
Title string
|
||||
Rating int
|
||||
}
|
||||
if err := redis.ScanSlice(values, &albums); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%v\n", albums)
|
||||
// Output:
|
||||
// [{Earthbound 1} {Beat 4} {Red 5}]
|
||||
}
|
||||
|
||||
var argsTests = []struct {
|
||||
title string
|
||||
actual redis.Args
|
||||
expected redis.Args
|
||||
}{
|
||||
{"struct ptr",
|
||||
redis.Args{}.AddFlat(&struct {
|
||||
I int `redis:"i"`
|
||||
U uint `redis:"u"`
|
||||
S string `redis:"s"`
|
||||
P []byte `redis:"p"`
|
||||
M map[string]string `redis:"m"`
|
||||
Bt bool
|
||||
Bf bool
|
||||
}{
|
||||
-1234, 5678, "hello", []byte("world"), map[string]string{"hello": "world"}, true, false,
|
||||
}),
|
||||
redis.Args{"i", int(-1234), "u", uint(5678), "s", "hello", "p", []byte("world"), "m", map[string]string{"hello": "world"}, "Bt", true, "Bf", false},
|
||||
},
|
||||
{"struct",
|
||||
redis.Args{}.AddFlat(struct{ I int }{123}),
|
||||
redis.Args{"I", 123},
|
||||
},
|
||||
{"slice",
|
||||
redis.Args{}.Add(1).AddFlat([]string{"a", "b", "c"}).Add(2),
|
||||
redis.Args{1, "a", "b", "c", 2},
|
||||
},
|
||||
{"struct omitempty",
|
||||
redis.Args{}.AddFlat(&struct {
|
||||
I int `redis:"i,omitempty"`
|
||||
U uint `redis:"u,omitempty"`
|
||||
S string `redis:"s,omitempty"`
|
||||
P []byte `redis:"p,omitempty"`
|
||||
M map[string]string `redis:"m,omitempty"`
|
||||
Bt bool `redis:"Bt,omitempty"`
|
||||
Bf bool `redis:"Bf,omitempty"`
|
||||
}{
|
||||
0, 0, "", []byte{}, map[string]string{}, true, false,
|
||||
}),
|
||||
redis.Args{"Bt", true},
|
||||
},
|
||||
}
|
||||
|
||||
func TestArgs(t *testing.T) {
|
||||
for _, tt := range argsTests {
|
||||
if !reflect.DeepEqual(tt.actual, tt.expected) {
|
||||
t.Fatalf("%s is %v, want %v", tt.title, tt.actual, tt.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleArgs() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
var p1, p2 struct {
|
||||
Title string `redis:"title"`
|
||||
Author string `redis:"author"`
|
||||
Body string `redis:"body"`
|
||||
}
|
||||
|
||||
p1.Title = "Example"
|
||||
p1.Author = "Gary"
|
||||
p1.Body = "Hello"
|
||||
|
||||
if _, err := c.Do("HMSET", redis.Args{}.Add("id1").AddFlat(&p1)...); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
m := map[string]string{
|
||||
"title": "Example2",
|
||||
"author": "Steve",
|
||||
"body": "Map",
|
||||
}
|
||||
|
||||
if _, err := c.Do("HMSET", redis.Args{}.Add("id2").AddFlat(m)...); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, id := range []string{"id1", "id2"} {
|
||||
|
||||
v, err := redis.Values(c.Do("HGETALL", id))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := redis.ScanStruct(v, &p2); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%+v\n", p2)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// {Title:Example Author:Gary Body:Hello}
|
||||
// {Title:Example2 Author:Steve Body:Map}
|
||||
}
|
||||
100
vendor/github.com/garyburd/redigo/redis/script_test.go
generated
vendored
100
vendor/github.com/garyburd/redigo/redis/script_test.go
generated
vendored
@@ -1,100 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
var (
|
||||
// These variables are declared at package level to remove distracting
|
||||
// details from the examples.
|
||||
c redis.Conn
|
||||
reply interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
func ExampleScript() {
|
||||
// Initialize a package-level variable with a script.
|
||||
var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`)
|
||||
|
||||
// In a function, use the script Do method to evaluate the script. The Do
|
||||
// method optimistically uses the EVALSHA command. If the script is not
|
||||
// loaded, then the Do method falls back to the EVAL command.
|
||||
reply, err = getScript.Do(c, "foo")
|
||||
}
|
||||
|
||||
func TestScript(t *testing.T) {
|
||||
c, err := redis.DialDefaultServer()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// To test fall back in Do, we make script unique by adding comment with current time.
|
||||
script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano())
|
||||
s := redis.NewScript(2, script)
|
||||
reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")}
|
||||
|
||||
v, err := s.Do(c, "key1", "key2", "arg1", "arg2")
|
||||
if err != nil {
|
||||
t.Errorf("s.Do(c, ...) returned %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(v, reply) {
|
||||
t.Errorf("s.Do(c, ..); = %v, want %v", v, reply)
|
||||
}
|
||||
|
||||
err = s.Load(c)
|
||||
if err != nil {
|
||||
t.Errorf("s.Load(c) returned %v", err)
|
||||
}
|
||||
|
||||
err = s.SendHash(c, "key1", "key2", "arg1", "arg2")
|
||||
if err != nil {
|
||||
t.Errorf("s.SendHash(c, ...) returned %v", err)
|
||||
}
|
||||
|
||||
err = c.Flush()
|
||||
if err != nil {
|
||||
t.Errorf("c.Flush() returned %v", err)
|
||||
}
|
||||
|
||||
v, err = c.Receive()
|
||||
if !reflect.DeepEqual(v, reply) {
|
||||
t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply)
|
||||
}
|
||||
|
||||
err = s.Send(c, "key1", "key2", "arg1", "arg2")
|
||||
if err != nil {
|
||||
t.Errorf("s.Send(c, ...) returned %v", err)
|
||||
}
|
||||
|
||||
err = c.Flush()
|
||||
if err != nil {
|
||||
t.Errorf("c.Flush() returned %v", err)
|
||||
}
|
||||
|
||||
v, err = c.Receive()
|
||||
if !reflect.DeepEqual(v, reply) {
|
||||
t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply)
|
||||
}
|
||||
|
||||
}
|
||||
183
vendor/github.com/garyburd/redigo/redis/test_test.go
generated
vendored
183
vendor/github.com/garyburd/redigo/redis/test_test.go
generated
vendored
@@ -1,183 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SetNowFunc(f func() time.Time) {
|
||||
nowFunc = f
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNegativeInt = errNegativeInt
|
||||
|
||||
serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary")
|
||||
serverAddress = flag.String("redis-address", "127.0.0.1", "The address of the server")
|
||||
serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers")
|
||||
serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`")
|
||||
serverLog = ioutil.Discard
|
||||
|
||||
defaultServerMu sync.Mutex
|
||||
defaultServer *Server
|
||||
defaultServerErr error
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
name string
|
||||
cmd *exec.Cmd
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func NewServer(name string, args ...string) (*Server, error) {
|
||||
s := &Server{
|
||||
name: name,
|
||||
cmd: exec.Command(*serverPath, args...),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
r, err := s.cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.cmd.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ready := make(chan error, 1)
|
||||
go s.watch(r, ready)
|
||||
|
||||
select {
|
||||
case err = <-ready:
|
||||
case <-time.After(time.Second * 10):
|
||||
err = errors.New("timeout waiting for server to start")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.Stop()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *Server) watch(r io.Reader, ready chan error) {
|
||||
fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name)
|
||||
var listening bool
|
||||
var text string
|
||||
scn := bufio.NewScanner(r)
|
||||
for scn.Scan() {
|
||||
text = scn.Text()
|
||||
fmt.Fprintf(serverLog, "%s\n", text)
|
||||
if !listening {
|
||||
if strings.Contains(text, " * Ready to accept connections") ||
|
||||
strings.Contains(text, " * The server is now ready to accept connections on port") {
|
||||
listening = true
|
||||
ready <- nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if !listening {
|
||||
ready <- fmt.Errorf("server exited: %s", text)
|
||||
}
|
||||
s.cmd.Wait()
|
||||
fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name)
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
func (s *Server) Stop() {
|
||||
s.cmd.Process.Signal(os.Interrupt)
|
||||
<-s.done
|
||||
}
|
||||
|
||||
// stopDefaultServer stops the server created by DialDefaultServer.
|
||||
func stopDefaultServer() {
|
||||
defaultServerMu.Lock()
|
||||
defer defaultServerMu.Unlock()
|
||||
if defaultServer != nil {
|
||||
defaultServer.Stop()
|
||||
defaultServer = nil
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultServerAddr starts the test server if not already started and returns
|
||||
// the address of that server.
|
||||
func DefaultServerAddr() (string, error) {
|
||||
defaultServerMu.Lock()
|
||||
defer defaultServerMu.Unlock()
|
||||
addr := fmt.Sprintf("%v:%d", *serverAddress, *serverBasePort)
|
||||
if defaultServer != nil || defaultServerErr != nil {
|
||||
return addr, defaultServerErr
|
||||
}
|
||||
defaultServer, defaultServerErr = NewServer(
|
||||
"default",
|
||||
"--port", strconv.Itoa(*serverBasePort),
|
||||
"--bind", *serverAddress,
|
||||
"--save", "",
|
||||
"--appendonly", "no")
|
||||
return addr, defaultServerErr
|
||||
}
|
||||
|
||||
// DialDefaultServer starts the test server if not already started and dials a
|
||||
// connection to the server.
|
||||
func DialDefaultServer() (Conn, error) {
|
||||
addr, err := DefaultServerAddr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := Dial("tcp", addr, DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Do("FLUSHDB")
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
flag.Parse()
|
||||
|
||||
var f *os.File
|
||||
if *serverLogName != "" {
|
||||
var err error
|
||||
f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
defer f.Close()
|
||||
serverLog = f
|
||||
}
|
||||
|
||||
defer stopDefaultServer()
|
||||
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
114
vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
generated
vendored
114
vendor/github.com/garyburd/redigo/redis/zpop_example_test.go
generated
vendored
@@ -1,114 +0,0 @@
|
||||
// Copyright 2013 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redis_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
// zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands.
|
||||
func zpop(c redis.Conn, key string) (result string, err error) {
|
||||
|
||||
defer func() {
|
||||
// Return connection to normal state on error.
|
||||
if err != nil {
|
||||
c.Do("DISCARD")
|
||||
}
|
||||
}()
|
||||
|
||||
// Loop until transaction is successful.
|
||||
for {
|
||||
if _, err := c.Do("WATCH", key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(members) != 1 {
|
||||
return "", redis.ErrNil
|
||||
}
|
||||
|
||||
c.Send("MULTI")
|
||||
c.Send("ZREM", key, members[0])
|
||||
queued, err := c.Do("EXEC")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if queued != nil {
|
||||
result = members[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// zpopScript pops a value from a ZSET.
|
||||
var zpopScript = redis.NewScript(1, `
|
||||
local r = redis.call('ZRANGE', KEYS[1], 0, 0)
|
||||
if r ~= nil then
|
||||
r = r[1]
|
||||
redis.call('ZREM', KEYS[1], r)
|
||||
end
|
||||
return r
|
||||
`)
|
||||
|
||||
// This example implements ZPOP as described at
|
||||
// http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting.
|
||||
func Example_zpop() {
|
||||
c, err := dial()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// Add test data using a pipeline.
|
||||
|
||||
for i, member := range []string{"red", "blue", "green"} {
|
||||
c.Send("ZADD", "zset", i, member)
|
||||
}
|
||||
if _, err := c.Do(""); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Pop using WATCH/MULTI/EXEC
|
||||
|
||||
v, err := zpop(c, "zset")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(v)
|
||||
|
||||
// Pop using a script.
|
||||
|
||||
v, err = redis.String(zpopScript.Do(c, "zset"))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(v)
|
||||
|
||||
// Output:
|
||||
// red
|
||||
// blue
|
||||
}
|
||||
152
vendor/github.com/garyburd/redigo/redisx/connmux.go
generated
vendored
152
vendor/github.com/garyburd/redigo/redisx/connmux.go
generated
vendored
@@ -1,152 +0,0 @@
|
||||
// Copyright 2014 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redisx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/garyburd/redigo/internal"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
)
|
||||
|
||||
// ConnMux multiplexes one or more connections to a single underlying
|
||||
// connection. The ConnMux connections do not support concurrency, commands
|
||||
// that associate server side state with the connection or commands that put
|
||||
// the connection in a special mode.
|
||||
type ConnMux struct {
|
||||
c redis.Conn
|
||||
|
||||
sendMu sync.Mutex
|
||||
sendID uint
|
||||
|
||||
recvMu sync.Mutex
|
||||
recvID uint
|
||||
recvWait map[uint]chan struct{}
|
||||
}
|
||||
|
||||
func NewConnMux(c redis.Conn) *ConnMux {
|
||||
return &ConnMux{c: c, recvWait: make(map[uint]chan struct{})}
|
||||
}
|
||||
|
||||
// Get gets a connection. The application must close the returned connection.
|
||||
func (p *ConnMux) Get() redis.Conn {
|
||||
c := &muxConn{p: p}
|
||||
c.ids = c.buf[:0]
|
||||
return c
|
||||
}
|
||||
|
||||
// Close closes the underlying connection.
|
||||
func (p *ConnMux) Close() error {
|
||||
return p.c.Close()
|
||||
}
|
||||
|
||||
type muxConn struct {
|
||||
p *ConnMux
|
||||
ids []uint
|
||||
buf [8]uint
|
||||
}
|
||||
|
||||
func (c *muxConn) send(flush bool, cmd string, args ...interface{}) error {
|
||||
if internal.LookupCommandInfo(cmd).Set != 0 {
|
||||
return errors.New("command not supported by mux pool")
|
||||
}
|
||||
p := c.p
|
||||
p.sendMu.Lock()
|
||||
id := p.sendID
|
||||
c.ids = append(c.ids, id)
|
||||
p.sendID++
|
||||
err := p.c.Send(cmd, args...)
|
||||
if flush {
|
||||
err = p.c.Flush()
|
||||
}
|
||||
p.sendMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *muxConn) Send(cmd string, args ...interface{}) error {
|
||||
return c.send(false, cmd, args...)
|
||||
}
|
||||
|
||||
func (c *muxConn) Flush() error {
|
||||
p := c.p
|
||||
p.sendMu.Lock()
|
||||
err := p.c.Flush()
|
||||
p.sendMu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *muxConn) Receive() (interface{}, error) {
|
||||
if len(c.ids) == 0 {
|
||||
return nil, errors.New("mux pool underflow")
|
||||
}
|
||||
|
||||
id := c.ids[0]
|
||||
c.ids = c.ids[1:]
|
||||
if len(c.ids) == 0 {
|
||||
c.ids = c.buf[:0]
|
||||
}
|
||||
|
||||
p := c.p
|
||||
p.recvMu.Lock()
|
||||
if p.recvID != id {
|
||||
ch := make(chan struct{})
|
||||
p.recvWait[id] = ch
|
||||
p.recvMu.Unlock()
|
||||
<-ch
|
||||
p.recvMu.Lock()
|
||||
if p.recvID != id {
|
||||
panic("out of sync")
|
||||
}
|
||||
}
|
||||
|
||||
v, err := p.c.Receive()
|
||||
|
||||
id++
|
||||
p.recvID = id
|
||||
ch, ok := p.recvWait[id]
|
||||
if ok {
|
||||
delete(p.recvWait, id)
|
||||
}
|
||||
p.recvMu.Unlock()
|
||||
if ok {
|
||||
ch <- struct{}{}
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *muxConn) Close() error {
|
||||
var err error
|
||||
if len(c.ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
c.Flush()
|
||||
for _ = range c.ids {
|
||||
_, err = c.Receive()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *muxConn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
||||
if err := c.send(true, cmd, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Receive()
|
||||
}
|
||||
|
||||
func (c *muxConn) Err() error {
|
||||
return c.p.c.Err()
|
||||
}
|
||||
259
vendor/github.com/garyburd/redigo/redisx/connmux_test.go
generated
vendored
259
vendor/github.com/garyburd/redigo/redisx/connmux_test.go
generated
vendored
@@ -1,259 +0,0 @@
|
||||
// Copyright 2014 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package redisx_test
|
||||
|
||||
import (
|
||||
"net/textproto"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/garyburd/redigo/internal/redistest"
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/garyburd/redigo/redisx"
|
||||
)
|
||||
|
||||
func TestConnMux(t *testing.T) {
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
m := redisx.NewConnMux(c)
|
||||
defer m.Close()
|
||||
|
||||
c1 := m.Get()
|
||||
c2 := m.Get()
|
||||
c1.Send("ECHO", "hello")
|
||||
c2.Send("ECHO", "world")
|
||||
c1.Flush()
|
||||
c2.Flush()
|
||||
s, err := redis.String(c1.Receive())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != "hello" {
|
||||
t.Fatalf("echo returned %q, want %q", s, "hello")
|
||||
}
|
||||
s, err = redis.String(c2.Receive())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != "world" {
|
||||
t.Fatalf("echo returned %q, want %q", s, "world")
|
||||
}
|
||||
c1.Close()
|
||||
c2.Close()
|
||||
}
|
||||
|
||||
func TestConnMuxClose(t *testing.T) {
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
t.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
m := redisx.NewConnMux(c)
|
||||
defer m.Close()
|
||||
|
||||
c1 := m.Get()
|
||||
c2 := m.Get()
|
||||
|
||||
if err := c1.Send("ECHO", "hello"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c1.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c2.Send("ECHO", "world"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c2.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s, err := redis.String(c2.Receive())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s != "world" {
|
||||
t.Fatalf("echo returned %q, want %q", s, "world")
|
||||
}
|
||||
c2.Close()
|
||||
}
|
||||
|
||||
func BenchmarkConn(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
b.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConnMux(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
b.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
m := redisx.NewConnMux(c)
|
||||
defer m.Close()
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
c := m.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPool(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
p := redis.Pool{Dial: redistest.Dial, MaxIdle: 1}
|
||||
defer p.Close()
|
||||
|
||||
// Fill the pool.
|
||||
c := p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
c := p.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
||||
const numConcurrent = 10
|
||||
|
||||
func BenchmarkConnMuxConcurrent(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
b.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
m := redisx.NewConnMux(c)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numConcurrent)
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < numConcurrent; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c := m.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkPoolConcurrent(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
p := redis.Pool{Dial: redistest.Dial, MaxIdle: numConcurrent}
|
||||
defer p.Close()
|
||||
|
||||
// Fill the pool.
|
||||
conns := make([]redis.Conn, numConcurrent)
|
||||
for i := range conns {
|
||||
c := p.Get()
|
||||
if err := c.Err(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
conns[i] = c
|
||||
}
|
||||
for _, c := range conns {
|
||||
c.Close()
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numConcurrent)
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < numConcurrent; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c := p.Get()
|
||||
if _, err := c.Do("PING"); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkPipelineConcurrency(b *testing.B) {
|
||||
b.StopTimer()
|
||||
c, err := redistest.Dial()
|
||||
if err != nil {
|
||||
b.Fatalf("error connection to database, %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numConcurrent)
|
||||
|
||||
var pipeline textproto.Pipeline
|
||||
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < numConcurrent; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < b.N; i++ {
|
||||
id := pipeline.Next()
|
||||
pipeline.StartRequest(id)
|
||||
c.Send("PING")
|
||||
c.Flush()
|
||||
pipeline.EndRequest(id)
|
||||
pipeline.StartResponse(id)
|
||||
_, err := c.Receive()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
pipeline.EndResponse(id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
17
vendor/github.com/garyburd/redigo/redisx/doc.go
generated
vendored
17
vendor/github.com/garyburd/redigo/redisx/doc.go
generated
vendored
@@ -1,17 +0,0 @@
|
||||
// Copyright 2012 Gary Burd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Package redisx contains experimental features for Redigo. Features in this
|
||||
// package may be modified or deleted at any time.
|
||||
package redisx // import "github.com/garyburd/redigo/redisx"
|
||||
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/gin-contrib/sse/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.6.4
|
||||
- 1.7.4
|
||||
- tip
|
||||
|
||||
git:
|
||||
depth: 3
|
||||
|
||||
script:
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
@@ -1,15 +1,19 @@
|
||||
#Server-Sent Events [](https://godoc.org/github.com/manucorporat/sse) [](https://travis-ci.org/manucorporat/sse)
|
||||
# Server-Sent Events
|
||||
|
||||
[](https://godoc.org/github.com/gin-contrib/sse)
|
||||
[](https://travis-ci.org/gin-contrib/sse)
|
||||
[](https://codecov.io/gh/gin-contrib/sse)
|
||||
[](https://goreportcard.com/report/github.com/gin-contrib/sse)
|
||||
|
||||
Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
|
||||
|
||||
- [Real world demostration using Gin](http://sse.getgin.io/)
|
||||
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
|
||||
- [Browser support](http://caniuse.com/#feat=eventsource)
|
||||
|
||||
##Sample code
|
||||
## Sample code
|
||||
|
||||
```go
|
||||
import "github.com/manucorporat/sse"
|
||||
import "github.com/gin-contrib/sse"
|
||||
|
||||
func httpHandler(w http.ResponseWriter, req *http.Request) {
|
||||
// data can be a primitive like a string, an integer or a float
|
||||
@@ -40,7 +44,7 @@ data: {"content":"hi!","date":1431540810,"user":"manu"}
|
||||
|
||||
```
|
||||
|
||||
##Content-Type
|
||||
## Content-Type
|
||||
|
||||
```go
|
||||
fmt.Println(sse.ContentType)
|
||||
@@ -49,6 +53,6 @@ fmt.Println(sse.ContentType)
|
||||
text/event-stream
|
||||
```
|
||||
|
||||
##Decoding support
|
||||
## Decoding support
|
||||
|
||||
There is a client-side implementation of SSE coming soon.
|
||||
There is a client-side implementation of SSE coming soon.
|
||||
@@ -87,13 +87,17 @@ func writeData(w stringWriter, data interface{}) error {
|
||||
}
|
||||
|
||||
func (r Event) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
return Encode(w, r)
|
||||
}
|
||||
|
||||
func (r Event) WriteContentType(w http.ResponseWriter) {
|
||||
header := w.Header()
|
||||
header["Content-Type"] = contentType
|
||||
|
||||
if _, exist := header["Cache-Control"]; !exist {
|
||||
header["Cache-Control"] = noCache
|
||||
}
|
||||
return Encode(w, r)
|
||||
}
|
||||
|
||||
func kindOfData(data interface{}) reflect.Kind {
|
||||
1
vendor/github.com/gin-gonic/autotls/.travis.yml
generated
vendored
1
vendor/github.com/gin-gonic/autotls/.travis.yml
generated
vendored
@@ -5,6 +5,7 @@ go:
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- master
|
||||
|
||||
git:
|
||||
|
||||
2
vendor/github.com/gin-gonic/autotls/autotls.go
generated
vendored
2
vendor/github.com/gin-gonic/autotls/autotls.go
generated
vendored
@@ -20,5 +20,7 @@ func RunWithManager(r http.Handler, m *autocert.Manager) error {
|
||||
Handler: r,
|
||||
}
|
||||
|
||||
go http.ListenAndServe(":http", m.HTTPHandler(nil))
|
||||
|
||||
return s.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
19
vendor/github.com/gin-gonic/autotls/example/example1.go
generated
vendored
19
vendor/github.com/gin-gonic/autotls/example/example1.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gin-gonic/autotls"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Ping handler
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
|
||||
log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
|
||||
}
|
||||
26
vendor/github.com/gin-gonic/autotls/example/example2.go
generated
vendored
26
vendor/github.com/gin-gonic/autotls/example/example2.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gin-gonic/autotls"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Ping handler
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong")
|
||||
})
|
||||
|
||||
m := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
|
||||
Cache: autocert.DirCache("/var/www/.cache"),
|
||||
}
|
||||
|
||||
log.Fatal(autotls.RunWithManager(r, &m))
|
||||
}
|
||||
19
vendor/github.com/gin-gonic/autotls/vendor/vendor.json
generated
vendored
19
vendor/github.com/gin-gonic/autotls/vendor/vendor.json
generated
vendored
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"comment": "",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
"checksumSHA1": "QNHLX/9+KNzu0oCCdqUSEX4kyiY=",
|
||||
"path": "golang.org/x/crypto/acme",
|
||||
"revision": "cbc3d0884eac986df6e78a039b8792e869bff863",
|
||||
"revisionTime": "2017-04-09T18:29:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TrKJW+flz7JulXU3sqnBJjGzgQc=",
|
||||
"path": "golang.org/x/crypto/acme/autocert",
|
||||
"revision": "5ef0053f77724838734b6945dd364d3847e5de1d",
|
||||
"revisionTime": "2017-06-29T04:06:47Z"
|
||||
}
|
||||
],
|
||||
"rootPath": "github.com/gin-gonic/autotls"
|
||||
}
|
||||
2
vendor/github.com/gin-gonic/contrib/.gitignore
generated
vendored
2
vendor/github.com/gin-gonic/contrib/.gitignore
generated
vendored
@@ -1,2 +0,0 @@
|
||||
*/Godeps/*
|
||||
!*/Godeps/Godeps.json
|
||||
18
vendor/github.com/gin-gonic/contrib/.travis.yml
generated
vendored
18
vendor/github.com/gin-gonic/contrib/.travis.yml
generated
vendored
@@ -1,18 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- tip
|
||||
|
||||
services:
|
||||
- memcache
|
||||
- redis-server
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/acc2c57482e94b44f557
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: false
|
||||
38
vendor/github.com/gin-gonic/contrib/README.md
generated
vendored
38
vendor/github.com/gin-gonic/contrib/README.md
generated
vendored
@@ -1,38 +0,0 @@
|
||||
# gin-gonic/contrib
|
||||
|
||||
[](https://travis-ci.org/gin-gonic/contrib)
|
||||
|
||||
Here you'll find middleware ready to use with [Gin Framework](https://github.com/gin-gonic/gin). Submit your pull request, either with the package in a folder, or by adding a link to this `README.md`.
|
||||
|
||||
If adding a package directly, don't forget to create a `README.md` inside with author name.
|
||||
If adding a link to your own repository, please follow this example:
|
||||
|
||||
```
|
||||
+ nameOfMiddleware (https://github.com/yourusername/yourrepo)
|
||||
```
|
||||
|
||||
Each author is responsible of maintaining his own code, although if you submit as a package, you allow the community to fix it. You can also submit a pull request to fix an existing package.
|
||||
|
||||
## List of external middleware
|
||||
|
||||
+ [RestGate](https://github.com/pjebs/restgate) - Secure authentication for REST API endpoints
|
||||
+ [staticbin](https://github.com/olebedev/staticbin) - middleware/handler for serving static files from binary data
|
||||
+ [gin-cors](https://github.com/gin-contrib/cors) - Official CORS gin's middleware
|
||||
+ [gin-csrf](https://github.com/utrack/gin-csrf) - CSRF protection
|
||||
+ [gin-health](https://github.com/utrack/gin-health) - middleware that simplifies stat reporting via [gocraft/health](https://github.com/gocraft/health)
|
||||
+ [gin-merry](https://github.com/utrack/gin-merry) - middleware for pretty-printing [merry](https://github.com/ansel1/merry) errors with context
|
||||
+ [gin-revision](https://github.com/appleboy/gin-revision-middleware) - Revision middleware for Gin framework
|
||||
+ [gin-jwt](https://github.com/appleboy/gin-jwt) - JWT Middleware for Gin Framework
|
||||
+ [gin-sessions](https://github.com/kimiazhu/ginweb-contrib/tree/master/sessions) - session middleware based on mongodb and mysql
|
||||
+ [gin-location](https://github.com/drone/gin-location) - middleware for exposing the server's hostname and scheme
|
||||
+ [gin-nice-recovery](https://github.com/ekyoung/gin-nice-recovery) - panic recovery middleware that let's you build a nicer user experience
|
||||
+ [gin-limit](https://github.com/aviddiviner/gin-limit) - limits simultaneous requests; can help with high traffic load
|
||||
+ [ez-gin-template](https://github.com/michelloworld/ez-gin-template) - easy template wrap for gin
|
||||
+ [gin-hydra](https://github.com/janekolszak/gin-hydra) - [Hydra](https://github.com/ory-am/hydra) middleware for Gin
|
||||
+ [gin-glog](https://github.com/zalando/gin-glog) - meant as drop-in replacement for Gin's default logger
|
||||
+ [gin-gomonitor](https://github.com/zalando/gin-gomonitor) - for exposing metrics with Go-Monitor
|
||||
+ [gin-oauth2](https://github.com/zalando/gin-oauth2) - for working with OAuth2
|
||||
+ [static](https://github.com/hyperboloide/static) An alternative static assets handler for the gin framework.
|
||||
+ [xss-mw](https://github.com/dvwright/xss-mw) - XssMw is a middleware designed to "auto remove XSS" from user submitted input
|
||||
+ [gin-helmet](https://github.com/danielkov/gin-helmet) - Collection of simple security middleware.
|
||||
+ [gin-jwt-session](https://github.com/ScottHuangZL/gin-jwt-session) - middleware to provide JWT/Session/Flashes, easy to use while also provide options for adjust if necessary. Provide sample too.
|
||||
22
vendor/github.com/gin-gonic/contrib/cache/Godeps/Godeps.json
generated
vendored
22
vendor/github.com/gin-gonic/contrib/cache/Godeps/Godeps.json
generated
vendored
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/contrib/cache",
|
||||
"GoVersion": "go1.3",
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/bradfitz/gomemcache/memcache",
|
||||
"Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/redis",
|
||||
"Rev": "535138d7bcd717d6531c701ef5933d98b1866257"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gin-gonic/gin",
|
||||
"Rev": "ac0ad2fed865d40a0adc1ac3ccaadc3acff5db4b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/robfig/go-cache",
|
||||
"Rev": "9fc39e0dbf62c034ec4e45e6120fc69433a3ec51"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
vendor/github.com/gin-gonic/contrib/cache/README.md
generated
vendored
5
vendor/github.com/gin-gonic/contrib/cache/README.md
generated
vendored
@@ -1,5 +0,0 @@
|
||||
# cache
|
||||
|
||||
## EOL-warning
|
||||
|
||||
**This package has been abandoned on 2016-12-07. Please use [gin-contrib/cache](https://github.com/gin-contrib/cache) instead.**
|
||||
153
vendor/github.com/gin-gonic/contrib/cache/cache.go
generated
vendored
153
vendor/github.com/gin-gonic/contrib/cache/cache.go
generated
vendored
@@ -1,153 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT = time.Duration(0)
|
||||
FOREVER = time.Duration(-1)
|
||||
CACHE_MIDDLEWARE_KEY = "gincontrib.cache"
|
||||
)
|
||||
|
||||
var (
|
||||
PageCachePrefix = "gincontrib.page.cache"
|
||||
ErrCacheMiss = errors.New("cache: key not found.")
|
||||
ErrNotStored = errors.New("cache: not stored.")
|
||||
ErrNotSupport = errors.New("cache: not support.")
|
||||
)
|
||||
|
||||
type CacheStore interface {
|
||||
Get(key string, value interface{}) error
|
||||
Set(key string, value interface{}, expire time.Duration) error
|
||||
Add(key string, value interface{}, expire time.Duration) error
|
||||
Replace(key string, data interface{}, expire time.Duration) error
|
||||
Delete(key string) error
|
||||
Increment(key string, data uint64) (uint64, error)
|
||||
Decrement(key string, data uint64) (uint64, error)
|
||||
Flush() error
|
||||
}
|
||||
|
||||
type responseCache struct {
|
||||
status int
|
||||
header http.Header
|
||||
data []byte
|
||||
}
|
||||
|
||||
type cachedWriter struct {
|
||||
gin.ResponseWriter
|
||||
status int
|
||||
written bool
|
||||
store CacheStore
|
||||
expire time.Duration
|
||||
key string
|
||||
}
|
||||
|
||||
func urlEscape(prefix string, u string) string {
|
||||
key := url.QueryEscape(u)
|
||||
if len(key) > 200 {
|
||||
h := sha1.New()
|
||||
io.WriteString(h, u)
|
||||
key = string(h.Sum(nil))
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(prefix)
|
||||
buffer.WriteString(":")
|
||||
buffer.WriteString(key)
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func newCachedWriter(store CacheStore, expire time.Duration, writer gin.ResponseWriter, key string) *cachedWriter {
|
||||
return &cachedWriter{writer, 0, false, store, expire, key}
|
||||
}
|
||||
|
||||
func (w *cachedWriter) WriteHeader(code int) {
|
||||
w.status = code
|
||||
w.written = true
|
||||
w.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (w *cachedWriter) Status() int {
|
||||
return w.status
|
||||
}
|
||||
|
||||
func (w *cachedWriter) Written() bool {
|
||||
return w.written
|
||||
}
|
||||
|
||||
func (w *cachedWriter) Write(data []byte) (int, error) {
|
||||
ret, err := w.ResponseWriter.Write(data)
|
||||
if err == nil {
|
||||
//cache response
|
||||
store := w.store
|
||||
val := responseCache{
|
||||
w.status,
|
||||
w.Header(),
|
||||
data,
|
||||
}
|
||||
err = store.Set(w.key, val, w.expire)
|
||||
if err != nil {
|
||||
// need logger
|
||||
}
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Cache Middleware
|
||||
func Cache(store *CacheStore) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set(CACHE_MIDDLEWARE_KEY, store)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func SiteCache(store CacheStore, expire time.Duration) gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
var cache responseCache
|
||||
url := c.Request.URL
|
||||
key := urlEscape(PageCachePrefix, url.RequestURI())
|
||||
if err := store.Get(key, &cache); err != nil {
|
||||
c.Next()
|
||||
} else {
|
||||
c.Writer.WriteHeader(cache.status)
|
||||
for k, vals := range cache.header {
|
||||
for _, v := range vals {
|
||||
c.Writer.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
c.Writer.Write(cache.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache Decorator
|
||||
func CachePage(store CacheStore, expire time.Duration, handle gin.HandlerFunc) gin.HandlerFunc {
|
||||
|
||||
return func(c *gin.Context) {
|
||||
var cache responseCache
|
||||
url := c.Request.URL
|
||||
key := urlEscape(PageCachePrefix, url.RequestURI())
|
||||
if err := store.Get(key, &cache); err != nil {
|
||||
// replace writer
|
||||
writer := newCachedWriter(store, expire, c.Writer, key)
|
||||
c.Writer = writer
|
||||
handle(c)
|
||||
} else {
|
||||
c.Writer.WriteHeader(cache.status)
|
||||
for k, vals := range cache.header {
|
||||
for _, v := range vals {
|
||||
c.Writer.Header().Add(k, v)
|
||||
}
|
||||
}
|
||||
c.Writer.Write(cache.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
202
vendor/github.com/gin-gonic/contrib/cache/cache_test.go
generated
vendored
202
vendor/github.com/gin-gonic/contrib/cache/cache_test.go
generated
vendored
@@ -1,202 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type cacheFactory func(*testing.T, time.Duration) CacheStore
|
||||
|
||||
// Test typical cache interactions
|
||||
func typicalGetSet(t *testing.T, newCache cacheFactory) {
|
||||
var err error
|
||||
cache := newCache(t, time.Hour)
|
||||
|
||||
value := "foo"
|
||||
if err = cache.Set("value", value, DEFAULT); err != nil {
|
||||
t.Errorf("Error setting a value: %s", err)
|
||||
}
|
||||
|
||||
value = ""
|
||||
err = cache.Get("value", &value)
|
||||
if err != nil {
|
||||
t.Errorf("Error getting a value: %s", err)
|
||||
}
|
||||
if value != "foo" {
|
||||
t.Errorf("Expected to get foo back, got %s", value)
|
||||
}
|
||||
}
|
||||
|
||||
// Test the increment-decrement cases
|
||||
func incrDecr(t *testing.T, newCache cacheFactory) {
|
||||
var err error
|
||||
cache := newCache(t, time.Hour)
|
||||
|
||||
// Normal increment / decrement operation.
|
||||
if err = cache.Set("int", 10, DEFAULT); err != nil {
|
||||
t.Errorf("Error setting int: %s", err)
|
||||
}
|
||||
newValue, err := cache.Increment("int", 50)
|
||||
if err != nil {
|
||||
t.Errorf("Error incrementing int: %s", err)
|
||||
}
|
||||
if newValue != 60 {
|
||||
t.Errorf("Expected 60, was %d", newValue)
|
||||
}
|
||||
|
||||
if newValue, err = cache.Decrement("int", 50); err != nil {
|
||||
t.Errorf("Error decrementing: %s", err)
|
||||
}
|
||||
if newValue != 10 {
|
||||
t.Errorf("Expected 10, was %d", newValue)
|
||||
}
|
||||
|
||||
// Increment wraparound
|
||||
newValue, err = cache.Increment("int", math.MaxUint64-5)
|
||||
if err != nil {
|
||||
t.Errorf("Error wrapping around: %s", err)
|
||||
}
|
||||
if newValue != 4 {
|
||||
t.Errorf("Expected wraparound 4, got %d", newValue)
|
||||
}
|
||||
|
||||
// Decrement capped at 0
|
||||
newValue, err = cache.Decrement("int", 25)
|
||||
if err != nil {
|
||||
t.Errorf("Error decrementing below 0: %s", err)
|
||||
}
|
||||
if newValue != 0 {
|
||||
t.Errorf("Expected capped at 0, got %d", newValue)
|
||||
}
|
||||
}
|
||||
|
||||
func expiration(t *testing.T, newCache cacheFactory) {
|
||||
// memcached does not support expiration times less than 1 second.
|
||||
var err error
|
||||
cache := newCache(t, time.Second)
|
||||
// Test Set w/ DEFAULT
|
||||
value := 10
|
||||
cache.Set("int", value, DEFAULT)
|
||||
time.Sleep(2 * time.Second)
|
||||
err = cache.Get("int", &value)
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected CacheMiss, but got: %s", err)
|
||||
}
|
||||
|
||||
// Test Set w/ short time
|
||||
cache.Set("int", value, time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
err = cache.Get("int", &value)
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected CacheMiss, but got: %s", err)
|
||||
}
|
||||
|
||||
// Test Set w/ longer time.
|
||||
cache.Set("int", value, time.Hour)
|
||||
time.Sleep(2 * time.Second)
|
||||
err = cache.Get("int", &value)
|
||||
if err != nil {
|
||||
t.Errorf("Expected to get the value, but got: %s", err)
|
||||
}
|
||||
|
||||
// Test Set w/ forever.
|
||||
cache.Set("int", value, FOREVER)
|
||||
time.Sleep(2 * time.Second)
|
||||
err = cache.Get("int", &value)
|
||||
if err != nil {
|
||||
t.Errorf("Expected to get the value, but got: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func emptyCache(t *testing.T, newCache cacheFactory) {
|
||||
var err error
|
||||
cache := newCache(t, time.Hour)
|
||||
|
||||
err = cache.Get("notexist", 0)
|
||||
if err == nil {
|
||||
t.Errorf("Error expected for non-existent key")
|
||||
}
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
|
||||
}
|
||||
|
||||
err = cache.Delete("notexist")
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected ErrCacheMiss for non-existent key: %s", err)
|
||||
}
|
||||
|
||||
_, err = cache.Increment("notexist", 1)
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected cache miss incrementing non-existent key: %s", err)
|
||||
}
|
||||
|
||||
_, err = cache.Decrement("notexist", 1)
|
||||
if err != ErrCacheMiss {
|
||||
t.Errorf("Expected cache miss decrementing non-existent key: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testReplace(t *testing.T, newCache cacheFactory) {
|
||||
var err error
|
||||
cache := newCache(t, time.Hour)
|
||||
|
||||
// Replace in an empty cache.
|
||||
if err = cache.Replace("notexist", 1, FOREVER); err != ErrNotStored {
|
||||
t.Errorf("Replace in empty cache: expected ErrNotStored, got: %s", err)
|
||||
}
|
||||
|
||||
// Set a value of 1, and replace it with 2
|
||||
if err = cache.Set("int", 1, time.Second); err != nil {
|
||||
t.Errorf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if err = cache.Replace("int", 2, time.Second); err != nil {
|
||||
t.Errorf("Unexpected error: %s", err)
|
||||
}
|
||||
var i int
|
||||
if err = cache.Get("int", &i); err != nil {
|
||||
t.Errorf("Unexpected error getting a replaced item: %s", err)
|
||||
}
|
||||
if i != 2 {
|
||||
t.Errorf("Expected 2, got %d", i)
|
||||
}
|
||||
|
||||
// Wait for it to expire and replace with 3 (unsuccessfully).
|
||||
time.Sleep(2 * time.Second)
|
||||
if err = cache.Replace("int", 3, time.Second); err != ErrNotStored {
|
||||
t.Errorf("Expected ErrNotStored, got: %s", err)
|
||||
}
|
||||
if err = cache.Get("int", &i); err != ErrCacheMiss {
|
||||
t.Errorf("Expected cache miss, got: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testAdd(t *testing.T, newCache cacheFactory) {
|
||||
var err error
|
||||
cache := newCache(t, time.Hour)
|
||||
// Add to an empty cache.
|
||||
if err = cache.Add("int", 1, time.Second); err != nil {
|
||||
t.Errorf("Unexpected error adding to empty cache: %s", err)
|
||||
}
|
||||
|
||||
// Try to add again. (fail)
|
||||
if err = cache.Add("int", 2, time.Second); err != ErrNotStored {
|
||||
t.Errorf("Expected ErrNotStored adding dupe to cache: %s", err)
|
||||
}
|
||||
|
||||
// Wait for it to expire, and add again.
|
||||
time.Sleep(2 * time.Second)
|
||||
if err = cache.Add("int", 3, time.Second); err != nil {
|
||||
t.Errorf("Unexpected error adding to cache: %s", err)
|
||||
}
|
||||
|
||||
// Get and verify the value.
|
||||
var i int
|
||||
if err = cache.Get("int", &i); err != nil {
|
||||
t.Errorf("Unexpected error: %s", err)
|
||||
}
|
||||
if i != 3 {
|
||||
t.Errorf("Expected 3, got: %d", i)
|
||||
}
|
||||
}
|
||||
25
vendor/github.com/gin-gonic/contrib/cache/example/example.go
generated
vendored
25
vendor/github.com/gin-gonic/contrib/cache/example/example.go
generated
vendored
@@ -1,25 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/contrib/cache"
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
store := cache.NewInMemoryStore(time.Second)
|
||||
// Cached Page
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
|
||||
})
|
||||
|
||||
r.GET("/cache_ping", cache.CachePage(store, time.Minute, func(c *gin.Context) {
|
||||
c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
|
||||
}))
|
||||
|
||||
// Listen and Server in 0.0.0.0:8080
|
||||
r.Run(":8080")
|
||||
}
|
||||
78
vendor/github.com/gin-gonic/contrib/cache/inmemory.go
generated
vendored
78
vendor/github.com/gin-gonic/contrib/cache/inmemory.go
generated
vendored
@@ -1,78 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/robfig/go-cache"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InMemoryStore struct {
|
||||
cache.Cache
|
||||
}
|
||||
|
||||
func NewInMemoryStore(defaultExpiration time.Duration) *InMemoryStore {
|
||||
return &InMemoryStore{*cache.New(defaultExpiration, time.Minute)}
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Get(key string, value interface{}) error {
|
||||
val, found := c.Cache.Get(key)
|
||||
if !found {
|
||||
return ErrCacheMiss
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(value)
|
||||
if v.Type().Kind() == reflect.Ptr && v.Elem().CanSet() {
|
||||
v.Elem().Set(reflect.ValueOf(val))
|
||||
return nil
|
||||
}
|
||||
return ErrNotStored
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Set(key string, value interface{}, expires time.Duration) error {
|
||||
// NOTE: go-cache understands the values of DEFAULT and FOREVER
|
||||
c.Cache.Set(key, value, expires)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Add(key string, value interface{}, expires time.Duration) error {
|
||||
err := c.Cache.Add(key, value, expires)
|
||||
if err == cache.ErrKeyExists {
|
||||
return ErrNotStored
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Replace(key string, value interface{}, expires time.Duration) error {
|
||||
if err := c.Cache.Replace(key, value, expires); err != nil {
|
||||
return ErrNotStored
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Delete(key string) error {
|
||||
if found := c.Cache.Delete(key); !found {
|
||||
return ErrCacheMiss
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Increment(key string, n uint64) (uint64, error) {
|
||||
newValue, err := c.Cache.Increment(key, n)
|
||||
if err == cache.ErrCacheMiss {
|
||||
return 0, ErrCacheMiss
|
||||
}
|
||||
return newValue, err
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Decrement(key string, n uint64) (uint64, error) {
|
||||
newValue, err := c.Cache.Decrement(key, n)
|
||||
if err == cache.ErrCacheMiss {
|
||||
return 0, ErrCacheMiss
|
||||
}
|
||||
return newValue, err
|
||||
}
|
||||
|
||||
func (c *InMemoryStore) Flush() error {
|
||||
c.Cache.Flush()
|
||||
return nil
|
||||
}
|
||||
35
vendor/github.com/gin-gonic/contrib/cache/inmemory_test.go
generated
vendored
35
vendor/github.com/gin-gonic/contrib/cache/inmemory_test.go
generated
vendored
@@ -1,35 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var newInMemoryStore = func(_ *testing.T, defaultExpiration time.Duration) CacheStore {
|
||||
return NewInMemoryStore(defaultExpiration)
|
||||
}
|
||||
|
||||
// Test typical cache interactions
|
||||
func TestInMemoryCache_TypicalGetSet(t *testing.T) {
|
||||
typicalGetSet(t, newInMemoryStore)
|
||||
}
|
||||
|
||||
func TestInMemoryCache_IncrDecr(t *testing.T) {
|
||||
incrDecr(t, newInMemoryStore)
|
||||
}
|
||||
|
||||
func TestInMemoryCache_Expiration(t *testing.T) {
|
||||
expiration(t, newInMemoryStore)
|
||||
}
|
||||
|
||||
func TestInMemoryCache_EmptyCache(t *testing.T) {
|
||||
emptyCache(t, newInMemoryStore)
|
||||
}
|
||||
|
||||
func TestInMemoryCache_Replace(t *testing.T) {
|
||||
testReplace(t, newInMemoryStore)
|
||||
}
|
||||
|
||||
func TestInMemoryCache_Add(t *testing.T) {
|
||||
testAdd(t, newInMemoryStore)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user