You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

124 lines
3.1 KiB

  1. // Copyright 2015 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package pingback provides verification that specific URLs have been seen by
  15. // the proxy.
  16. package pingback
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "net/http"
  21. "net/url"
  22. "github.com/google/martian"
  23. "github.com/google/martian/parse"
  24. "github.com/google/martian/verify"
  25. )
  26. const (
  27. errFormat = "request(%s): pingback never occurred"
  28. )
  29. func init() {
  30. parse.Register("pingback.Verifier", verifierFromJSON)
  31. }
  32. // Verifier verifies that the specific URL has been seen.
  33. type Verifier struct {
  34. url *url.URL
  35. err error
  36. }
  37. type verifierJSON struct {
  38. Scheme string `json:"scheme"`
  39. Host string `json:"host"`
  40. Path string `json:"path"`
  41. Query string `json:"query"`
  42. Scope []parse.ModifierType `json:"scope"`
  43. }
  44. // NewVerifier returns a new pingback verifier.
  45. func NewVerifier(url *url.URL) verify.RequestVerifier {
  46. return &Verifier{
  47. url: url,
  48. err: fmt.Errorf(errFormat, url.String()),
  49. }
  50. }
  51. // ModifyRequest verifies that the request URL matches all parts of url.
  52. //
  53. // If the value in url is non-empty, it must be an exact match. If the URL
  54. // matches the pingback, it is recorded by setting the error to nil. The error
  55. // will continue to be nil until the verifier has been reset, regardless of
  56. // subsequent requests matching.
  57. func (v *Verifier) ModifyRequest(req *http.Request) error {
  58. // skip requests to API
  59. ctx := martian.NewContext(req)
  60. if ctx.IsAPIRequest() {
  61. return nil
  62. }
  63. u := req.URL
  64. switch {
  65. case v.url.Scheme != "" && v.url.Scheme != u.Scheme:
  66. case v.url.Host != "" && v.url.Host != u.Host:
  67. case v.url.Path != "" && v.url.Path != u.Path:
  68. case v.url.RawQuery != "" && v.url.RawQuery != u.RawQuery:
  69. default:
  70. v.err = nil
  71. }
  72. return nil
  73. }
  74. // VerifyRequests returns an error if pingback never occurred.
  75. func (v *Verifier) VerifyRequests() error {
  76. return v.err
  77. }
  78. // ResetRequestVerifications clears the failed request verification.
  79. func (v *Verifier) ResetRequestVerifications() {
  80. v.err = fmt.Errorf(errFormat, v.url.String())
  81. }
  82. // verifierFromJSON builds a pingback.Verifier from JSON.
  83. //
  84. // Example JSON:
  85. // {
  86. // "pingback.Verifier": {
  87. // "scope": ["request"],
  88. // "scheme": "https",
  89. // "host": "www.google.com",
  90. // "path": "/proxy",
  91. // "query": "testing=true"
  92. // }
  93. // }
  94. func verifierFromJSON(b []byte) (*parse.Result, error) {
  95. msg := &verifierJSON{}
  96. if err := json.Unmarshal(b, msg); err != nil {
  97. return nil, err
  98. }
  99. v := NewVerifier(&url.URL{
  100. Scheme: msg.Scheme,
  101. Host: msg.Host,
  102. Path: msg.Path,
  103. RawQuery: msg.Query,
  104. })
  105. return parse.NewResult(v, msg.Scope)
  106. }