updating github.com/emicklei/go-restful to v2.9.5+incompatible

This commit is contained in:
Davanum Srinivas 2019-06-14 10:50:10 -04:00
parent 8d6848ada1
commit 453eac7c08
No known key found for this signature in database
GPG Key ID: 80D83A796103BF59
36 changed files with 514 additions and 211 deletions

4
go.mod
View File

@ -51,7 +51,7 @@ require (
github.com/docker/go-units v0.3.3 github.com/docker/go-units v0.3.3
github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789 github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 github.com/emicklei/go-restful v2.9.5+incompatible
github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect github.com/euank/go-kmsg-parser v2.0.0+incompatible // indirect
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d
@ -251,7 +251,7 @@ replace (
github.com/docker/libnetwork => github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789 github.com/docker/libnetwork => github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789
github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96
github.com/elazarl/goproxy => github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e github.com/elazarl/goproxy => github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e
github.com/emicklei/go-restful => github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 github.com/emicklei/go-restful => github.com/emicklei/go-restful v2.9.5+incompatible
github.com/euank/go-kmsg-parser => github.com/euank/go-kmsg-parser v2.0.0+incompatible github.com/euank/go-kmsg-parser => github.com/euank/go-kmsg-parser v2.0.0+incompatible
github.com/evanphx/json-patch => github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 github.com/evanphx/json-patch => github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550
github.com/exponent-io/jsonpath => github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d github.com/exponent-io/jsonpath => github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d

4
go.sum
View File

@ -111,8 +111,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/euank/go-kmsg-parser v2.0.0+incompatible h1:cHD53+PLQuuQyLZeriD1V/esuG4MuU0Pjs5y6iknohY= github.com/euank/go-kmsg-parser v2.0.0+incompatible h1:cHD53+PLQuuQyLZeriD1V/esuG4MuU0Pjs5y6iknohY=
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw= github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=

View File

@ -6,7 +6,7 @@ go 1.12
require ( require (
github.com/coreos/etcd v3.3.13+incompatible github.com/coreos/etcd v3.3.13+incompatible
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 github.com/emicklei/go-restful v2.9.5+incompatible
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 // indirect github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 // indirect
github.com/go-openapi/analysis v0.17.2 // indirect github.com/go-openapi/analysis v0.17.2 // indirect
github.com/go-openapi/errors v0.17.2 // indirect github.com/go-openapi/errors v0.17.2 // indirect

View File

@ -34,8 +34,8 @@ github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -15,7 +15,7 @@ require (
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0 github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 github.com/emicklei/go-restful v2.9.5+incompatible
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 // indirect github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 // indirect
github.com/go-openapi/jsonpointer v0.19.0 // indirect github.com/go-openapi/jsonpointer v0.19.0 // indirect

View File

@ -33,8 +33,8 @@ github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0 h1:w3NnFcKR5241cfm
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -6,7 +6,7 @@ go 1.12
require ( require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 // indirect github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4 // indirect
github.com/go-openapi/jsonpointer v0.19.0 // indirect github.com/go-openapi/jsonpointer v0.19.0 // indirect

View File

@ -9,8 +9,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -6,7 +6,7 @@ go 1.12
require ( require (
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 github.com/emicklei/go-restful v2.9.5+incompatible
github.com/go-openapi/spec v0.17.2 github.com/go-openapi/spec v0.17.2
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415 github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect

View File

@ -33,8 +33,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QL
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -31,8 +31,8 @@ github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -9,8 +9,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550 h1:mV9jbLoSW/8m4VK16ZkHTozJa8sesK5u5kTMFysTYac=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=

View File

@ -15,12 +15,14 @@ go_library(
"doc.go", "doc.go",
"entity_accessors.go", "entity_accessors.go",
"filter.go", "filter.go",
"json.go",
"jsr311.go", "jsr311.go",
"logger.go", "logger.go",
"mime.go", "mime.go",
"options_filter.go", "options_filter.go",
"parameter.go", "parameter.go",
"path_expression.go", "path_expression.go",
"path_processor.go",
"request.go", "request.go",
"response.go", "response.go",
"route.go", "route.go",

View File

@ -1,6 +1,56 @@
Change history of go-restful ## Change history of go-restful
=
v2.9.5
- fix panic in Response.WriteError if err == nil
v2.9.4
- fix issue #400 , parsing mime type quality
- Route Builder added option for contentEncodingEnabled (#398)
v2.9.3
- Avoid return of 415 Unsupported Media Type when request body is empty (#396)
v2.9.2
- Reduce allocations in per-request methods to improve performance (#395)
v2.9.1
- Fix issue with default responses and invalid status code 0. (#393)
v2.9.0
- add per Route content encoding setting (overrides container setting)
v2.8.0
- add Request.QueryParameters()
- add json-iterator (via build tag)
- disable vgo module (until log is moved)
v2.7.1
- add vgo module
v2.6.1
- add JSONNewDecoderFunc to allow custom JSON Decoder usage (go 1.10+)
v2.6.0
- Make JSR 311 routing and path param processing consistent
- Adding description to RouteBuilder.Reads()
- Update example for Swagger12 and OpenAPI
2017-09-13
- added route condition functions using `.If(func)` in route building.
2017-02-16 2017-02-16
- solved issue #304, make operation names unique - solved issue #304, make operation names unique
2017-01-30 2017-01-30

View File

@ -56,19 +56,33 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
- Content encoding (gzip,deflate) of request and response payloads - Content encoding (gzip,deflate) of request and response payloads
- Automatic responses on OPTIONS (using a filter) - Automatic responses on OPTIONS (using a filter)
- Automatic CORS request handling (using a filter) - Automatic CORS request handling (using a filter)
- API declaration for Swagger UI (see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12),[go-restful-openapi](https://github.com/emicklei/go-restful-openapi)) - API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi), see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12))
- Panic recovery to produce HTTP 500, customizable using RecoverHandler(...) - Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
- Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...) - Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
- Configurable (trace) logging - Configurable (trace) logging
- Customizable gzip/deflate readers and writers using CompressorProvider registration - Customizable gzip/deflate readers and writers using CompressorProvider registration
### Resources ## How to customize
There are several hooks to customize the behavior of the go-restful package.
- Router algorithm
- Panic recovery
- JSON decoder
- Trace logging
- Compression
- Encoders for other serializers
- Use [jsoniter](https://github.com/json-iterator/go) by build this package using a tag, e.g. `go build -tags=jsoniter .`
TODO: write examples of these.
## Resources
- [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/) - [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/)
- [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/) - [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/)
- [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful) - [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful)
- [showcase: Zazkia - tcp proxy for testing resiliency](https://github.com/emicklei/zazkia)
- [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora) - [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora)
Type ```git shortlog -s``` for a full list of contributors. Type ```git shortlog -s``` for a full list of contributors.
© 2012 - 2017, http://ernestmicklei.com. MIT License. Contributions are welcome. © 2012 - 2018, http://ernestmicklei.com. MIT License. Contributions are welcome.

View File

@ -15,20 +15,20 @@ type CompressorProvider interface {
// Before using it, call Reset(). // Before using it, call Reset().
AcquireGzipWriter() *gzip.Writer AcquireGzipWriter() *gzip.Writer
// Releases an aqcuired *gzip.Writer. // Releases an acquired *gzip.Writer.
ReleaseGzipWriter(w *gzip.Writer) ReleaseGzipWriter(w *gzip.Writer)
// Returns a *gzip.Reader which needs to be released later. // Returns a *gzip.Reader which needs to be released later.
AcquireGzipReader() *gzip.Reader AcquireGzipReader() *gzip.Reader
// Releases an aqcuired *gzip.Reader. // Releases an acquired *gzip.Reader.
ReleaseGzipReader(w *gzip.Reader) ReleaseGzipReader(w *gzip.Reader)
// Returns a *zlib.Writer which needs to be released later. // Returns a *zlib.Writer which needs to be released later.
// Before using it, call Reset(). // Before using it, call Reset().
AcquireZlibWriter() *zlib.Writer AcquireZlibWriter() *zlib.Writer
// Releases an aqcuired *zlib.Writer. // Releases an acquired *zlib.Writer.
ReleaseZlibWriter(w *zlib.Writer) ReleaseZlibWriter(w *zlib.Writer)
} }
@ -45,7 +45,7 @@ func CurrentCompressorProvider() CompressorProvider {
return currentCompressorProvider return currentCompressorProvider
} }
// CompressorProvider sets the actual provider of compressors (zlib or gzip). // SetCompressorProvider sets the actual provider of compressors (zlib or gzip).
func SetCompressorProvider(p CompressorProvider) { func SetCompressorProvider(p CompressorProvider) {
if p == nil { if p == nil {
panic("cannot set compressor provider to nil") panic("cannot set compressor provider to nil")

View File

@ -97,7 +97,7 @@ func (c *Container) Add(service *WebService) *Container {
// cannot have duplicate root paths // cannot have duplicate root paths
for _, each := range c.webServices { for _, each := range c.webServices {
if each.RootPath() == service.RootPath() { if each.RootPath() == service.RootPath() {
log.Printf("[restful] WebService with duplicate root path detected:['%v']", each) log.Printf("WebService with duplicate root path detected:['%v']", each)
os.Exit(1) os.Exit(1)
} }
} }
@ -139,8 +139,8 @@ func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) boo
func (c *Container) Remove(ws *WebService) error { func (c *Container) Remove(ws *WebService) error {
if c.ServeMux == http.DefaultServeMux { if c.ServeMux == http.DefaultServeMux {
errMsg := fmt.Sprintf("[restful] cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws) errMsg := fmt.Sprintf("cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws)
log.Printf(errMsg) log.Print(errMsg)
return errors.New(errMsg) return errors.New(errMsg)
} }
c.webServicesLock.Lock() c.webServicesLock.Lock()
@ -168,7 +168,7 @@ func (c *Container) Remove(ws *WebService) error {
// This may be a security issue as it exposes sourcecode information. // This may be a security issue as it exposes sourcecode information.
func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) { func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) {
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString(fmt.Sprintf("[restful] recover from panic situation: - %v\r\n", panicReason)) buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
for i := 2; ; i += 1 { for i := 2; ; i += 1 {
_, file, line, ok := runtime.Caller(i) _, file, line, ok := runtime.Caller(i)
if !ok { if !ok {
@ -220,20 +220,6 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
}() }()
} }
// Detect if compression is needed
// assume without compression, test for override
if c.contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("[restful] unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
// Find best match Route ; err is non nil if no match was found // Find best match Route ; err is non nil if no match was found
var webService *WebService var webService *WebService
var route *Route var route *Route
@ -245,6 +231,26 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
c.webServices, c.webServices,
httpRequest) httpRequest)
}() }()
// Detect if compression is needed
// assume without compression, test for override
contentEncodingEnabled := c.contentEncodingEnabled
if route != nil && route.contentEncodingEnabled != nil {
contentEncodingEnabled = *route.contentEncodingEnabled
}
if contentEncodingEnabled {
doCompress, encoding := wantsCompressedResponse(httpRequest)
if doCompress {
var err error
writer, err = NewCompressingResponseWriter(httpWriter, encoding)
if err != nil {
log.Print("unable to install compressor: ", err)
httpWriter.WriteHeader(http.StatusInternalServerError)
return
}
}
}
if err != nil { if err != nil {
// a non-200 response has already been written // a non-200 response has already been written
// run container filters anyway ; they should not touch the response... // run container filters anyway ; they should not touch the response...
@ -259,7 +265,12 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer)) chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer))
return return
} }
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest) pathProcessor, routerProcessesPath := c.router.(PathProcessor)
if !routerProcessesPath {
pathProcessor = defaultPathProcessor{}
}
pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path)
wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams)
// pass through filters (if any) // pass through filters (if any)
if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 { if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 {
// compose filter chain // compose filter chain

View File

@ -45,14 +45,14 @@ func (c CurlyRouter) SelectRoute(
// selectRoutes return a collection of Route from a WebService that matches the path tokens from the request. // selectRoutes return a collection of Route from a WebService that matches the path tokens from the request.
func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes { func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes {
candidates := sortableCurlyRoutes{} candidates := make(sortableCurlyRoutes, 0, 8)
for _, each := range ws.routes { for _, each := range ws.routes {
matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens) matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens)
if matches { if matches {
candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers? candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers?
} }
} }
sort.Sort(sort.Reverse(candidates)) sort.Sort(candidates)
return candidates return candidates
} }

View File

@ -11,6 +11,7 @@ type curlyRoute struct {
staticCount int staticCount int
} }
// sortableCurlyRoutes orders by most parameters and path elements first.
type sortableCurlyRoutes []curlyRoute type sortableCurlyRoutes []curlyRoute
func (s *sortableCurlyRoutes) add(route curlyRoute) { func (s *sortableCurlyRoutes) add(route curlyRoute) {
@ -18,6 +19,7 @@ func (s *sortableCurlyRoutes) add(route curlyRoute) {
} }
func (s sortableCurlyRoutes) routes() (routes []Route) { func (s sortableCurlyRoutes) routes() (routes []Route) {
routes = make([]Route, 0, len(s))
for _, each := range s { for _, each := range s {
routes = append(routes, each.route) // TODO change return type routes = append(routes, each.route) // TODO change return type
} }
@ -31,22 +33,22 @@ func (s sortableCurlyRoutes) Swap(i, j int) {
s[i], s[j] = s[j], s[i] s[i], s[j] = s[j], s[i]
} }
func (s sortableCurlyRoutes) Less(i, j int) bool { func (s sortableCurlyRoutes) Less(i, j int) bool {
ci := s[i] a := s[j]
cj := s[j] b := s[i]
// primary key // primary key
if ci.staticCount < cj.staticCount { if a.staticCount < b.staticCount {
return true return true
} }
if ci.staticCount > cj.staticCount { if a.staticCount > b.staticCount {
return false return false
} }
// secundary key // secundary key
if ci.paramCount < cj.paramCount { if a.paramCount < b.paramCount {
return true return true
} }
if ci.paramCount > cj.paramCount { if a.paramCount > b.paramCount {
return false return false
} }
return ci.route.Path < cj.route.Path return a.route.Path < b.route.Path
} }

View File

@ -5,7 +5,6 @@ package restful
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
import ( import (
"encoding/json"
"encoding/xml" "encoding/xml"
"strings" "strings"
"sync" "sync"
@ -128,7 +127,7 @@ type entityJSONAccess struct {
// Read unmarshalls the value from JSON // Read unmarshalls the value from JSON
func (e entityJSONAccess) Read(req *Request, v interface{}) error { func (e entityJSONAccess) Read(req *Request, v interface{}) error {
decoder := json.NewDecoder(req.Request.Body) decoder := NewDecoder(req.Request.Body)
decoder.UseNumber() decoder.UseNumber()
return decoder.Decode(v) return decoder.Decode(v)
} }
@ -147,7 +146,7 @@ func writeJSON(resp *Response, status int, contentType string, v interface{}) er
} }
if resp.prettyPrint { if resp.prettyPrint {
// pretty output must be created and written explicitly // pretty output must be created and written explicitly
output, err := json.MarshalIndent(v, " ", " ") output, err := MarshalIndent(v, "", " ")
if err != nil { if err != nil {
return err return err
} }
@ -159,5 +158,5 @@ func writeJSON(resp *Response, status int, contentType string, v interface{}) er
// not-so-pretty // not-so-pretty
resp.Header().Set(HEADER_ContentType, contentType) resp.Header().Set(HEADER_ContentType, contentType)
resp.WriteHeader(status) resp.WriteHeader(status)
return json.NewEncoder(resp).Encode(v) return NewEncoder(resp).Encode(v)
} }

11
vendor/github.com/emicklei/go-restful/json.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
// +build !jsoniter
package restful
import "encoding/json"
var (
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
)

12
vendor/github.com/emicklei/go-restful/jsoniter.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build jsoniter
package restful
import "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
)

View File

@ -39,57 +39,106 @@ func (r RouterJSR311) SelectRoute(
return dispatcher, route, ok return dispatcher, route, ok
} }
// ExtractParameters is used to obtain the path parameters from the route using the same matching
// engine as the JSR 311 router.
func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string {
webServiceExpr := webService.pathExpr
webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath)
pathParameters := r.extractParams(webServiceExpr, webServiceMatches)
routeExpr := route.pathExpr
routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1])
routeParams := r.extractParams(routeExpr, routeMatches)
for key, value := range routeParams {
pathParameters[key] = value
}
return pathParameters
}
func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string {
params := map[string]string{}
for i := 1; i < len(matches); i++ {
if len(pathExpr.VarNames) >= i {
params[pathExpr.VarNames[i-1]] = matches[i]
}
}
return params
}
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2
func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) { func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) {
// http method candidates := make([]*Route, 0, 8)
methodOk := []Route{} for i, each := range routes {
for _, each := range routes { ok := true
if httpRequest.Method == each.Method { for _, fn := range each.If {
methodOk = append(methodOk, each) if !fn(httpRequest) {
ok = false
break
} }
} }
if len(methodOk) == 0 { if ok {
candidates = append(candidates, &routes[i])
}
}
if len(candidates) == 0 {
if trace { if trace {
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(routes), httpRequest.Method) traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes))
}
return nil, NewError(http.StatusNotFound, "404: Not Found")
}
// http method
previous := candidates
candidates = candidates[:0]
for _, each := range previous {
if httpRequest.Method == each.Method {
candidates = append(candidates, each)
}
}
if len(candidates) == 0 {
if trace {
traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method)
} }
return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed") return nil, NewError(http.StatusMethodNotAllowed, "405: Method Not Allowed")
} }
inputMediaOk := methodOk
// content-type // content-type
contentType := httpRequest.Header.Get(HEADER_ContentType) contentType := httpRequest.Header.Get(HEADER_ContentType)
inputMediaOk = []Route{} previous = candidates
for _, each := range methodOk { candidates = candidates[:0]
for _, each := range previous {
if each.matchesContentType(contentType) { if each.matchesContentType(contentType) {
inputMediaOk = append(inputMediaOk, each) candidates = append(candidates, each)
} }
} }
if len(inputMediaOk) == 0 { if len(candidates) == 0 {
if trace { if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(methodOk), contentType) traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType)
} }
if httpRequest.ContentLength > 0 {
return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type") return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type")
} }
}
// accept // accept
outputMediaOk := []Route{} previous = candidates
candidates = candidates[:0]
accept := httpRequest.Header.Get(HEADER_Accept) accept := httpRequest.Header.Get(HEADER_Accept)
if len(accept) == 0 { if len(accept) == 0 {
accept = "*/*" accept = "*/*"
} }
for _, each := range inputMediaOk { for _, each := range previous {
if each.matchesAccept(accept) { if each.matchesAccept(accept) {
outputMediaOk = append(outputMediaOk, each) candidates = append(candidates, each)
} }
} }
if len(outputMediaOk) == 0 { if len(candidates) == 0 {
if trace { if trace {
traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(inputMediaOk), accept) traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept)
} }
return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable") return nil, NewError(http.StatusNotAcceptable, "406: Not Acceptable")
} }
// return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil // return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil
return &outputMediaOk[0], nil return candidates[0], nil
} }
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2

View File

@ -21,7 +21,7 @@ func TraceLogger(logger log.StdLogger) {
EnableTracing(logger != nil) EnableTracing(logger != nil)
} }
// expose the setter for the global logger on the top-level package // SetLogger exposes the setter for the global logger on the top-level package
func SetLogger(customLogger log.StdLogger) { func SetLogger(customLogger log.StdLogger) {
log.SetLogger(customLogger) log.SetLogger(customLogger)
} }

View File

@ -22,7 +22,10 @@ func insertMime(l []mime, e mime) []mime {
return append(l, e) return append(l, e)
} }
const qFactorWeightingKey = "q"
// sortedMimes returns a list of mime sorted (desc) by its specified quality. // sortedMimes returns a list of mime sorted (desc) by its specified quality.
// e.g. text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
func sortedMimes(accept string) (sorted []mime) { func sortedMimes(accept string) (sorted []mime) {
for _, each := range strings.Split(accept, ",") { for _, each := range strings.Split(accept, ",") {
typeAndQuality := strings.Split(strings.Trim(each, " "), ";") typeAndQuality := strings.Split(strings.Trim(each, " "), ";")
@ -30,14 +33,16 @@ func sortedMimes(accept string) (sorted []mime) {
sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0}) sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
} else { } else {
// take factor // take factor
parts := strings.Split(typeAndQuality[1], "=") qAndWeight := strings.Split(typeAndQuality[1], "=")
if len(parts) == 2 { if len(qAndWeight) == 2 && strings.Trim(qAndWeight[0], " ") == qFactorWeightingKey {
f, err := strconv.ParseFloat(parts[1], 64) f, err := strconv.ParseFloat(qAndWeight[1], 64)
if err != nil { if err != nil {
traceLogger.Printf("unable to parse quality in %s, %v", each, err) traceLogger.Printf("unable to parse quality in %s, %v", each, err)
} else { } else {
sorted = insertMime(sorted, mime{typeAndQuality[0], f}) sorted = insertMime(sorted, mime{typeAndQuality[0], f})
} }
} else {
sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
} }
} }
} }

View File

@ -15,7 +15,15 @@ func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterCha
chain.ProcessFilter(req, resp) chain.ProcessFilter(req, resp)
return return
} }
resp.AddHeader(HEADER_Allow, strings.Join(c.computeAllowedMethods(req), ","))
archs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders)
methods := strings.Join(c.computeAllowedMethods(req), ",")
origin := req.Request.Header.Get(HEADER_Origin)
resp.AddHeader(HEADER_Allow, methods)
resp.AddHeader(HEADER_AccessControlAllowOrigin, origin)
resp.AddHeader(HEADER_AccessControlAllowHeaders, archs)
resp.AddHeader(HEADER_AccessControlAllowMethods, methods)
} }
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method // OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method

View File

@ -19,8 +19,30 @@ const (
// FormParameterKind = indicator of Request parameter type "form" // FormParameterKind = indicator of Request parameter type "form"
FormParameterKind FormParameterKind
// CollectionFormatCSV comma separated values `foo,bar`
CollectionFormatCSV = CollectionFormat("csv")
// CollectionFormatSSV space separated values `foo bar`
CollectionFormatSSV = CollectionFormat("ssv")
// CollectionFormatTSV tab separated values `foo\tbar`
CollectionFormatTSV = CollectionFormat("tsv")
// CollectionFormatPipes pipe separated values `foo|bar`
CollectionFormatPipes = CollectionFormat("pipes")
// CollectionFormatMulti corresponds to multiple parameter instances instead of multiple values for a single
// instance `foo=bar&foo=baz`. This is valid only for QueryParameters and FormParameters
CollectionFormatMulti = CollectionFormat("multi")
) )
type CollectionFormat string
func (cf CollectionFormat) String() string {
return string(cf)
}
// Parameter is for documententing the parameter used in a Http Request // Parameter is for documententing the parameter used in a Http Request
// ParameterData kinds are Path,Query and Body // ParameterData kinds are Path,Query and Body
type Parameter struct { type Parameter struct {
@ -36,6 +58,7 @@ type ParameterData struct {
AllowableValues map[string]string AllowableValues map[string]string
AllowMultiple bool AllowMultiple bool
DefaultValue string DefaultValue string
CollectionFormat string
} }
// Data returns the state of the Parameter // Data returns the state of the Parameter
@ -112,3 +135,9 @@ func (p *Parameter) Description(doc string) *Parameter {
p.data.Description = doc p.data.Description = doc
return p return p
} }
// CollectionFormat sets the collection format for an array type
func (p *Parameter) CollectionFormat(format CollectionFormat) *Parameter {
p.data.CollectionFormat = format.String()
return p
}

View File

@ -15,6 +15,7 @@ import (
// Http request paths and to extract path parameter values. // Http request paths and to extract path parameter values.
type pathExpression struct { type pathExpression struct {
LiteralCount int // the number of literal characters (means those not resulting from template variable substitution) LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
VarNames []string // the names of parameters (enclosed by {}) in the path
VarCount int // the number of named parameters (enclosed by {}) in the path VarCount int // the number of named parameters (enclosed by {}) in the path
Matcher *regexp.Regexp Matcher *regexp.Regexp
Source string // Path as defined by the RouteBuilder Source string // Path as defined by the RouteBuilder
@ -24,16 +25,16 @@ type pathExpression struct {
// NewPathExpression creates a PathExpression from the input URL path. // NewPathExpression creates a PathExpression from the input URL path.
// Returns an error if the path is invalid. // Returns an error if the path is invalid.
func newPathExpression(path string) (*pathExpression, error) { func newPathExpression(path string) (*pathExpression, error) {
expression, literalCount, varCount, tokens := templateToRegularExpression(path) expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
compiled, err := regexp.Compile(expression) compiled, err := regexp.Compile(expression)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &pathExpression{literalCount, varCount, compiled, expression, tokens}, nil return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
} }
// http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3 // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
func templateToRegularExpression(template string) (expression string, literalCount int, varCount int, tokens []string) { func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString("^") buffer.WriteString("^")
//tokens = strings.Split(template, "/") //tokens = strings.Split(template, "/")
@ -46,8 +47,10 @@ func templateToRegularExpression(template string) (expression string, literalCou
if strings.HasPrefix(each, "{") { if strings.HasPrefix(each, "{") {
// check for regular expression in variable // check for regular expression in variable
colon := strings.Index(each, ":") colon := strings.Index(each, ":")
var varName string
if colon != -1 { if colon != -1 {
// extract expression // extract expression
varName = strings.TrimSpace(each[1:colon])
paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1]) paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
if paramExpr == "*" { // special case if paramExpr == "*" { // special case
buffer.WriteString("(.*)") buffer.WriteString("(.*)")
@ -56,8 +59,10 @@ func templateToRegularExpression(template string) (expression string, literalCou
} }
} else { } else {
// plain var // plain var
varName = strings.TrimSpace(each[1 : len(each)-1])
buffer.WriteString("([^/]+?)") buffer.WriteString("([^/]+?)")
} }
varNames = append(varNames, varName)
varCount += 1 varCount += 1
} else { } else {
literalCount += len(each) literalCount += len(each)
@ -65,5 +70,5 @@ func templateToRegularExpression(template string) (expression string, literalCou
buffer.WriteString(regexp.QuoteMeta(encoded)) buffer.WriteString(regexp.QuoteMeta(encoded))
} }
} }
return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varCount, tokens return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
} }

View File

@ -0,0 +1,63 @@
package restful
import (
"bytes"
"strings"
)
// Copyright 2018 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
// PathProcessor is extra behaviour that a Router can provide to extract path parameters from the path.
// If a Router does not implement this interface then the default behaviour will be used.
type PathProcessor interface {
// ExtractParameters gets the path parameters defined in the route and webService from the urlPath
ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string
}
type defaultPathProcessor struct{}
// Extract the parameters from the request url path
func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath string) map[string]string {
urlParts := tokenizePath(urlPath)
pathParameters := map[string]string{}
for i, key := range r.pathParts {
var value string
if i >= len(urlParts) {
value = ""
} else {
value = urlParts[i]
}
if strings.HasPrefix(key, "{") { // path-parameter
if colon := strings.Index(key, ":"); colon != -1 {
// extract by regex
regPart := key[colon+1 : len(key)-1]
keyPart := key[1:colon]
if regPart == "*" {
pathParameters[keyPart] = untokenizePath(i, urlParts)
break
} else {
pathParameters[keyPart] = value
}
} else {
// without enclosing {}
pathParameters[key[1:len(key)-1]] = value
}
}
}
return pathParameters
}
// Untokenize back into an URL path using the slash separator
func untokenizePath(offset int, parts []string) string {
var buffer bytes.Buffer
for p := offset; p < len(parts); p++ {
buffer.WriteString(parts[p])
// do not end
if p < len(parts)-1 {
buffer.WriteString("/")
}
}
return buffer.String()
}

View File

@ -51,6 +51,11 @@ func (r *Request) QueryParameter(name string) string {
return r.Request.FormValue(name) return r.Request.FormValue(name)
} }
// QueryParameters returns the all the query parameters values by name
func (r *Request) QueryParameters(name string) []string {
return r.Request.URL.Query()[name]
}
// BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error. // BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
func (r *Request) BodyParameter(name string) (string, error) { func (r *Request) BodyParameter(name string) (string, error) {
err := r.Request.ParseForm() err := r.Request.ParseForm()

View File

@ -5,7 +5,9 @@ package restful
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
import ( import (
"bufio"
"errors" "errors"
"net"
"net/http" "net/http"
) )
@ -21,15 +23,17 @@ type Response struct {
http.ResponseWriter http.ResponseWriter
requestAccept string // mime-type what the Http Request says it wants to receive requestAccept string // mime-type what the Http Request says it wants to receive
routeProduces []string // mime-types what the Route says it can produce routeProduces []string // mime-types what the Route says it can produce
statusCode int // HTTP status code that has been written explicity (if zero then net/http has written 200) statusCode int // HTTP status code that has been written explicitly (if zero then net/http has written 200)
contentLength int // number of bytes written for the response body contentLength int // number of bytes written for the response body
prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses. prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
err error // err property is kept when WriteError is called err error // err property is kept when WriteError is called
hijacker http.Hijacker // if underlying ResponseWriter supports it
} }
// NewResponse creates a new response based on a http ResponseWriter. // NewResponse creates a new response based on a http ResponseWriter.
func NewResponse(httpWriter http.ResponseWriter) *Response { func NewResponse(httpWriter http.ResponseWriter) *Response {
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses, nil} // empty content-types hijacker, _ := httpWriter.(http.Hijacker)
return &Response{ResponseWriter: httpWriter, routeProduces: []string{}, statusCode: http.StatusOK, prettyPrint: PrettyPrintResponses, hijacker: hijacker}
} }
// DefaultResponseContentType set a default. // DefaultResponseContentType set a default.
@ -48,6 +52,16 @@ func (r Response) InternalServerError() Response {
return r return r
} }
// Hijack implements the http.Hijacker interface. This expands
// the Response to fulfill http.Hijacker if the underlying
// http.ResponseWriter supports it.
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if r.hijacker == nil {
return nil, nil, errors.New("http.Hijacker not implemented by underlying http.ResponseWriter")
}
return r.hijacker.Hijack()
}
// PrettyPrint changes whether this response must produce pretty (line-by-line, indented) JSON or XML output. // PrettyPrint changes whether this response must produce pretty (line-by-line, indented) JSON or XML output.
func (r *Response) PrettyPrint(bePretty bool) { func (r *Response) PrettyPrint(bePretty bool) {
r.prettyPrint = bePretty r.prettyPrint = bePretty
@ -160,10 +174,15 @@ func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType
return writeJSON(r, status, contentType, value) return writeJSON(r, status, contentType, value)
} }
// WriteError write the http status and the error string on the response. // WriteError write the http status and the error string on the response. err can be nil.
func (r *Response) WriteError(httpStatus int, err error) error { func (r *Response) WriteError(httpStatus int, err error) error {
r.err = err r.err = err
return r.WriteErrorString(httpStatus, err.Error()) if err == nil {
r.WriteErrorString(httpStatus, "")
} else {
r.WriteErrorString(httpStatus, err.Error())
}
return err
} }
// WriteServiceError is a convenience method for a responding with a status and a ServiceError // WriteServiceError is a convenience method for a responding with a status and a ServiceError

View File

@ -5,7 +5,6 @@ package restful
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
import ( import (
"bytes"
"net/http" "net/http"
"strings" "strings"
) )
@ -13,6 +12,11 @@ import (
// RouteFunction declares the signature of a function that can be bound to a Route. // RouteFunction declares the signature of a function that can be bound to a Route.
type RouteFunction func(*Request, *Response) type RouteFunction func(*Request, *Response)
// RouteSelectionConditionFunction declares the signature of a function that
// can be used to add extra conditional logic when selecting whether the route
// matches the HTTP request.
type RouteSelectionConditionFunction func(httpRequest *http.Request) bool
// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction. // Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
type Route struct { type Route struct {
Method string Method string
@ -21,6 +25,7 @@ type Route struct {
Path string // webservice root path + described path Path string // webservice root path + described path
Function RouteFunction Function RouteFunction
Filters []FilterFunction Filters []FilterFunction
If []RouteSelectionConditionFunction
// cached values for dispatching // cached values for dispatching
relativePath string relativePath string
@ -33,10 +38,17 @@ type Route struct {
Operation string Operation string
ParameterDocs []*Parameter ParameterDocs []*Parameter
ResponseErrors map[int]ResponseError ResponseErrors map[int]ResponseError
DefaultResponse *ResponseError
ReadSample, WriteSample interface{} // structs that model an example request or response payload ReadSample, WriteSample interface{} // structs that model an example request or response payload
// Extra information used to store custom information about the route. // Extra information used to store custom information about the route.
Metadata map[string]interface{} Metadata map[string]interface{}
// marks a route as deprecated
Deprecated bool
//Overrides the container.contentEncodingEnabled
contentEncodingEnabled *bool
} }
// Initialize for Route // Initialize for Route
@ -45,10 +57,9 @@ func (r *Route) postBuild() {
} }
// Create Request and Response from their http versions // Create Request and Response from their http versions
func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) { func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) {
params := r.extractParameters(httpRequest.URL.Path)
wrappedRequest := NewRequest(httpRequest) wrappedRequest := NewRequest(httpRequest)
wrappedRequest.pathParameters = params wrappedRequest.pathParameters = pathParams
wrappedRequest.selectedRoutePath = r.Path wrappedRequest.selectedRoutePath = r.Path
wrappedResponse := NewResponse(httpWriter) wrappedResponse := NewResponse(httpWriter)
wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept) wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
@ -67,29 +78,37 @@ func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Re
} }
} }
func stringTrimSpaceCutset(r rune) bool {
return r == ' '
}
// Return whether the mimeType matches to what this Route can produce. // Return whether the mimeType matches to what this Route can produce.
func (r Route) matchesAccept(mimeTypesWithQuality string) bool { func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
parts := strings.Split(mimeTypesWithQuality, ",") remaining := mimeTypesWithQuality
for _, each := range parts { for {
var withoutQuality string var mimeType string
if strings.Contains(each, ";") { if end := strings.Index(remaining, ","); end == -1 {
withoutQuality = strings.Split(each, ";")[0] mimeType, remaining = remaining, ""
} else { } else {
withoutQuality = each mimeType, remaining = remaining[:end], remaining[end+1:]
} }
// trim before compare if quality := strings.Index(mimeType, ";"); quality != -1 {
withoutQuality = strings.Trim(withoutQuality, " ") mimeType = mimeType[:quality]
if withoutQuality == "*/*" { }
mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
if mimeType == "*/*" {
return true return true
} }
for _, producibleType := range r.Produces { for _, producibleType := range r.Produces {
if producibleType == "*/*" || producibleType == withoutQuality { if producibleType == "*/*" || producibleType == mimeType {
return true return true
} }
} }
} if len(remaining) == 0 {
return false return false
} }
}
}
// Return whether this Route can consume content with a type specified by mimeTypes (can be empty). // Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
func (r Route) matchesContentType(mimeTypes string) bool { func (r Route) matchesContentType(mimeTypes string) bool {
@ -100,7 +119,7 @@ func (r Route) matchesContentType(mimeTypes string) bool {
} }
if len(mimeTypes) == 0 { if len(mimeTypes) == 0 {
// idempotent methods with (most-likely or garanteed) empty content match missing Content-Type // idempotent methods with (most-likely or guaranteed) empty content match missing Content-Type
m := r.Method m := r.Method
if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" { if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
return true return true
@ -109,73 +128,33 @@ func (r Route) matchesContentType(mimeTypes string) bool {
mimeTypes = MIME_OCTET mimeTypes = MIME_OCTET
} }
parts := strings.Split(mimeTypes, ",") remaining := mimeTypes
for _, each := range parts { for {
var contentType string var mimeType string
if strings.Contains(each, ";") { if end := strings.Index(remaining, ","); end == -1 {
contentType = strings.Split(each, ";")[0] mimeType, remaining = remaining, ""
} else { } else {
contentType = each mimeType, remaining = remaining[:end], remaining[end+1:]
} }
// trim before compare if quality := strings.Index(mimeType, ";"); quality != -1 {
contentType = strings.Trim(contentType, " ") mimeType = mimeType[:quality]
}
mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset)
for _, consumeableType := range r.Consumes { for _, consumeableType := range r.Consumes {
if consumeableType == "*/*" || consumeableType == contentType { if consumeableType == "*/*" || consumeableType == mimeType {
return true return true
} }
} }
} if len(remaining) == 0 {
return false return false
} }
// Extract the parameters from the request url path
func (r Route) extractParameters(urlPath string) map[string]string {
urlParts := tokenizePath(urlPath)
pathParameters := map[string]string{}
for i, key := range r.pathParts {
var value string
if i >= len(urlParts) {
value = ""
} else {
value = urlParts[i]
} }
if strings.HasPrefix(key, "{") { // path-parameter
if colon := strings.Index(key, ":"); colon != -1 {
// extract by regex
regPart := key[colon+1 : len(key)-1]
keyPart := key[1:colon]
if regPart == "*" {
pathParameters[keyPart] = untokenizePath(i, urlParts)
break
} else {
pathParameters[keyPart] = value
}
} else {
// without enclosing {}
pathParameters[key[1:len(key)-1]] = value
}
}
}
return pathParameters
}
// Untokenize back into an URL path using the slash separator
func untokenizePath(offset int, parts []string) string {
var buffer bytes.Buffer
for p := offset; p < len(parts); p++ {
buffer.WriteString(parts[p])
// do not end
if p < len(parts)-1 {
buffer.WriteString("/")
}
}
return buffer.String()
} }
// Tokenize an URL path using the slash separator ; the result does not have empty tokens // Tokenize an URL path using the slash separator ; the result does not have empty tokens
func tokenizePath(path string) []string { func tokenizePath(path string) []string {
if "/" == path { if "/" == path {
return []string{} return nil
} }
return strings.Split(strings.Trim(path, "/"), "/") return strings.Split(strings.Trim(path, "/"), "/")
} }
@ -184,3 +163,8 @@ func tokenizePath(path string) []string {
func (r Route) String() string { func (r Route) String() string {
return r.Method + " " + r.Path return r.Method + " " + r.Path
} }
// EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. Overrides the container.contentEncodingEnabled value.
func (r Route) EnableContentEncoding(enabled bool) {
r.contentEncodingEnabled = &enabled
}

View File

@ -24,6 +24,7 @@ type RouteBuilder struct {
httpMethod string // required httpMethod string // required
function RouteFunction // required function RouteFunction // required
filters []FilterFunction filters []FilterFunction
conditions []RouteSelectionConditionFunction
typeNameHandleFunc TypeNameHandleFunction // required typeNameHandleFunc TypeNameHandleFunction // required
@ -34,7 +35,10 @@ type RouteBuilder struct {
readSample, writeSample interface{} readSample, writeSample interface{}
parameters []*Parameter parameters []*Parameter
errorMap map[int]ResponseError errorMap map[int]ResponseError
defaultResponse *ResponseError
metadata map[string]interface{} metadata map[string]interface{}
deprecated bool
contentEncodingEnabled *bool
} }
// Do evaluates each argument with the RouteBuilder itself. // Do evaluates each argument with the RouteBuilder itself.
@ -89,7 +93,7 @@ func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
return b return b
} }
// A verbose explanation of the operation behavior. Optional. // Notes is a verbose explanation of the operation behavior. Optional.
func (b *RouteBuilder) Notes(notes string) *RouteBuilder { func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
b.notes = notes b.notes = notes
return b return b
@ -97,15 +101,18 @@ func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
// Reads tells what resource type will be read from the request payload. Optional. // Reads tells what resource type will be read from the request payload. Optional.
// A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type. // A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type.
func (b *RouteBuilder) Reads(sample interface{}) *RouteBuilder { func (b *RouteBuilder) Reads(sample interface{}, optionalDescription ...string) *RouteBuilder {
fn := b.typeNameHandleFunc fn := b.typeNameHandleFunc
if fn == nil { if fn == nil {
fn = reflectTypeName fn = reflectTypeName
} }
typeAsName := fn(sample) typeAsName := fn(sample)
description := ""
if len(optionalDescription) > 0 {
description = optionalDescription[0]
}
b.readSample = sample b.readSample = sample
bodyParameter := &Parameter{&ParameterData{Name: "body"}} bodyParameter := &Parameter{&ParameterData{Name: "body", Description: description}}
bodyParameter.beBody() bodyParameter.beBody()
bodyParameter.Required(true) bodyParameter.Required(true)
bodyParameter.DataType(typeAsName) bodyParameter.DataType(typeAsName)
@ -159,7 +166,7 @@ func (b *RouteBuilder) Returns(code int, message string, model interface{}) *Rou
Code: code, Code: code,
Message: message, Message: message,
Model: model, Model: model,
IsDefault: false, IsDefault: false, // this field is deprecated, use default response instead.
} }
// lazy init because there is no NewRouteBuilder (yet) // lazy init because there is no NewRouteBuilder (yet)
if b.errorMap == nil { if b.errorMap == nil {
@ -169,17 +176,11 @@ func (b *RouteBuilder) Returns(code int, message string, model interface{}) *Rou
return b return b
} }
// DefaultReturns is a special Returns call that sets the default of the response ; the code is zero. // DefaultReturns is a special Returns call that sets the default of the response.
func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder { func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder {
b.Returns(0, message, model) b.defaultResponse = &ResponseError{
// Modify the ResponseError just added/updated Message: message,
re := b.errorMap[0] Model: model,
// errorMap is initialized
b.errorMap[0] = ResponseError{
Code: re.Code,
Message: re.Message,
Model: re.Model,
IsDefault: true,
} }
return b return b
} }
@ -193,6 +194,12 @@ func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder {
return b return b
} }
// Deprecate sets the value of deprecated to true. Deprecated routes have a special UI treatment to warn against use
func (b *RouteBuilder) Deprecate() *RouteBuilder {
b.deprecated = true
return b
}
// ResponseError represents a response; not necessarily an error. // ResponseError represents a response; not necessarily an error.
type ResponseError struct { type ResponseError struct {
Code int Code int
@ -212,6 +219,27 @@ func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder {
return b return b
} }
// If sets a condition function that controls matching the Route based on custom logic.
// The condition function is provided the HTTP request and should return true if the route
// should be considered.
//
// Efficiency note: the condition function is called before checking the method, produces, and
// consumes criteria, so that the correct HTTP status code can be returned.
//
// Lifecycle note: no filter functions have been called prior to calling the condition function,
// so the condition function should not depend on any context that might be set up by container
// or route filters.
func (b *RouteBuilder) If(condition RouteSelectionConditionFunction) *RouteBuilder {
b.conditions = append(b.conditions, condition)
return b
}
// ContentEncodingEnabled allows you to override the Containers value for auto-compressing this route response.
func (b *RouteBuilder) ContentEncodingEnabled(enabled bool) *RouteBuilder {
b.contentEncodingEnabled = &enabled
return b
}
// If no specific Route path then set to rootPath // If no specific Route path then set to rootPath
// If no specific Produces then set to rootProduces // If no specific Produces then set to rootProduces
// If no specific Consumes then set to rootConsumes // If no specific Consumes then set to rootConsumes
@ -235,11 +263,11 @@ func (b *RouteBuilder) typeNameHandler(handler TypeNameHandleFunction) *RouteBui
func (b *RouteBuilder) Build() Route { func (b *RouteBuilder) Build() Route {
pathExpr, err := newPathExpression(b.currentPath) pathExpr, err := newPathExpression(b.currentPath)
if err != nil { if err != nil {
log.Printf("[restful] Invalid path:%s because:%v", b.currentPath, err) log.Printf("Invalid path:%s because:%v", b.currentPath, err)
os.Exit(1) os.Exit(1)
} }
if b.function == nil { if b.function == nil {
log.Printf("[restful] No function specified for route:" + b.currentPath) log.Printf("No function specified for route:" + b.currentPath)
os.Exit(1) os.Exit(1)
} }
operationName := b.operation operationName := b.operation
@ -254,6 +282,7 @@ func (b *RouteBuilder) Build() Route {
Consumes: b.consumes, Consumes: b.consumes,
Function: b.function, Function: b.function,
Filters: b.filters, Filters: b.filters,
If: b.conditions,
relativePath: b.currentPath, relativePath: b.currentPath,
pathExpr: pathExpr, pathExpr: pathExpr,
Doc: b.doc, Doc: b.doc,
@ -261,9 +290,13 @@ func (b *RouteBuilder) Build() Route {
Operation: operationName, Operation: operationName,
ParameterDocs: b.parameters, ParameterDocs: b.parameters,
ResponseErrors: b.errorMap, ResponseErrors: b.errorMap,
DefaultResponse: b.defaultResponse,
ReadSample: b.readSample, ReadSample: b.readSample,
WriteSample: b.writeSample, WriteSample: b.writeSample,
Metadata: b.metadata} Metadata: b.metadata,
Deprecated: b.deprecated,
contentEncodingEnabled: b.contentEncodingEnabled,
}
route.postBuild() route.postBuild()
return route return route
} }

View File

@ -7,6 +7,8 @@ package restful
import "net/http" import "net/http"
// A RouteSelector finds the best matching Route given the input HTTP Request // A RouteSelector finds the best matching Route given the input HTTP Request
// RouteSelectors can optionally also implement the PathProcessor interface to also calculate the
// path parameters after the route has been selected.
type RouteSelector interface { type RouteSelector interface {
// SelectRoute finds a Route given the input HTTP Request and a list of WebServices. // SelectRoute finds a Route given the input HTTP Request and a list of WebServices.

View File

@ -60,7 +60,7 @@ func reflectTypeName(sample interface{}) string {
func (w *WebService) compilePathExpression() { func (w *WebService) compilePathExpression() {
compiled, err := newPathExpression(w.rootPath) compiled, err := newPathExpression(w.rootPath)
if err != nil { if err != nil {
log.Printf("[restful] invalid path:%s because:%v", w.rootPath, err) log.Printf("invalid path:%s because:%v", w.rootPath, err)
os.Exit(1) os.Exit(1)
} }
w.pathExpr = compiled w.pathExpr = compiled
@ -118,7 +118,7 @@ func (w *WebService) QueryParameter(name, description string) *Parameter {
// QueryParameter creates a new Parameter of kind Query for documentation purposes. // QueryParameter creates a new Parameter of kind Query for documentation purposes.
// It is initialized as not required with string as its DataType. // It is initialized as not required with string as its DataType.
func QueryParameter(name, description string) *Parameter { func QueryParameter(name, description string) *Parameter {
p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}} p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string", CollectionFormat: CollectionFormatCSV.String()}}
p.beQuery() p.beQuery()
return p return p
} }
@ -233,7 +233,7 @@ func (w *WebService) RootPath() string {
return w.rootPath return w.rootPath
} }
// PathParameters return the path parameter names for (shared amoung its Routes) // PathParameters return the path parameter names for (shared among its Routes)
func (w *WebService) PathParameters() []*Parameter { func (w *WebService) PathParameters() []*Parameter {
return w.pathParameters return w.pathParameters
} }

2
vendor/modules.txt vendored
View File

@ -324,7 +324,7 @@ github.com/docker/spdystream
github.com/docker/spdystream/spdy github.com/docker/spdystream/spdy
# github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e => github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e # github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e => github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e
github.com/elazarl/goproxy github.com/elazarl/goproxy
# github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 => github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 # github.com/emicklei/go-restful v2.9.5+incompatible => github.com/emicklei/go-restful v2.9.5+incompatible
github.com/emicklei/go-restful github.com/emicklei/go-restful
github.com/emicklei/go-restful/log github.com/emicklei/go-restful/log
# github.com/euank/go-kmsg-parser v2.0.0+incompatible => github.com/euank/go-kmsg-parser v2.0.0+incompatible # github.com/euank/go-kmsg-parser v2.0.0+incompatible => github.com/euank/go-kmsg-parser v2.0.0+incompatible