Skip to content

Commit

Permalink
added ConditionalHTTPService
Browse files Browse the repository at this point in the history
  • Loading branch information
panorix committed Dec 30, 2021
1 parent b9afa7e commit 6dd450b
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 7 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Config = webconfig.NewWebConfig(<websites' full root path>)
- Common web settings + security, and URL management options.
- Keeps a separate file for blocked IP addresses.
- Built-in timeout event to reset the Message Banner display value to off.
- Conditional HTTP Service based on ip address, header, and query string.
The following example allows only bing and google bots to see /robot.txt:
conditional-http-service [{"rule-type":"ip-address","url-path":"/robot.txt","serve-only-to-criteria":["+http://www.bing.com/bingbot.htm","+http://www.google.com/bot.html"],"http-status-code":404}]]
See *ConditionalHTTPService* and *conditional-http-service* in defs.go
- Built-in Request Validation; usage example:
``` go
isRequestValid, httpErrCode := Config.ValidateHTTPRequest(w, r)
Expand Down
51 changes: 46 additions & 5 deletions defs.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
package webconfig

const (
CondHTTPSvc_Header = "header"
CondHTTPSvc_IPAddress = "ip-address"
CondHTTPSvc_QueryString = "query-string"
)

// ConditionalHTTPService serves an HTTP request according to a
// condition based on a value in the header, query string or
// ip address.
type ConditionalHTTPService struct {

// RuleType can be: header, query-string, or ip-address
RuleType string `json:"rule-type"`

// URLPath is the relative URL of the request; i.e. /robot.txt
URLPath string `json:"url-path"`

// ServeOnlyToCriteria are the related strings to match. i.e.,
// bingbot/2.0; +http://www.bing.com/bingbot.htm and
// Googlebot/2.1; +http://www.google.com/bot.html can be
// two string elements in the Vaues array that will be used to
// match in the Header (User-Agent). For example, if the target url
// is /robot.txt; it will only be served to google and bing bots.
ServeOnlyToCriteria []string `json:"serve-only-to-criteria"`

// HTTPStatusCode is http status code that will be retured, if
// a match is found. The default is 404 (not found).
HTTPStatusCode int `json:"http-status-code"`
}
type messageBanner struct {
On bool `json:"on"`
SecondsToDisplay int `json:"seconds-to-display"`
Expand All @@ -22,10 +51,12 @@ type tlsFiles struct {
CertFilePath string `json:"cert-file-path"`
KeyFilePath string `json:"key-file-path"`
}

type urlPaths struct {
Restrict []string `json:"restrict"`
Forward []string `json:"forward"`
Exclude []string `json:"exclude"`
Restrict []string `json:"restrict"`
Forward []string `json:"forward"`
Exclude []string `json:"exclude"`
ServeOnlyTo []ConditionalHTTPService `json:"conditional-http-service"`
}

// admin defines the IP addresses
Expand Down Expand Up @@ -169,22 +200,32 @@ MessageBanner
# These option to make a portion of your site unavailable for maintenance
# or other reasons. Each path must begin with a slash (relative path).
# The following should be the order or evaluation:
# restrict-paths, exclude-paths, forward-paths.
# restrict-paths, exclude-paths, forward-paths, conditional-http-service.
URLPaths
# restrict-paths <url paths separated by comma>
# e.g.
# restrict-paths /gallery,/accounting, /customer-review, /myblog
restrict-paths
# with the exclude option, files will be intact in the same location, but not
# served; the end-user will receive 404 error; or the behaviour can be
# customized.
# exclude-paths /galleryx, /someotherpath
exclude-paths
# forward-paths <url-from|url-to-forward paths separated by comma>.
# Note that forwarding to a fully qualified url must not be allowed!
# Note that forwarding to a fully qualified url must not be allowed.
# e.g.
# forward-paths /along-name-of-a-blog-page|/latest-blog, /another-along-name-of-a-blog-page|/best-of-blogs, \
# /and-more-and-more-pages|/yet-the-best-blog
forward-paths
# Conditional HTTP Service serves an HTTP request according to a
# condition based on a value in the header, query string or
# ip address. If a matching string is found the request will be
# e.g. the following only allows the bing and google bots see the robot.txt file.
# conditional-http-service [{"rule-type":"header","url-path":"/robot.txt","serve-only-to-criteria":["+http://www.bing.com/bingbot.htm","+http://www.google.com/bot.html"],"http-status-code":404}]
conditional-http-service
# HEAD and GET are generally allowed by default.
HTTP
Expand Down
4 changes: 4 additions & 0 deletions private.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package webconfig

import (
"encoding/json"
"fmt"
"log"
"os"
Expand Down Expand Up @@ -266,6 +267,9 @@ func (c *Config) getConfigLeaves(lines []string, i int, section string, keys []s
c.URLPaths.Exclude[j] = strings.TrimLeft(c.URLPaths.Exclude[j], " ")
c.URLPaths.Exclude[j] = strings.TrimRight(c.URLPaths.Exclude[j], " ")
}
} else if strings.HasPrefix(l, "conditional-http-service") {
s := c.parseCofigLine(l, "conditional-http-service")
json.Unmarshal([]byte(s), &c.URLPaths.ServeOnlyTo)
}
}

Expand Down
2 changes: 1 addition & 1 deletion public.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (c *Config) GetConfig() {

} else if strings.HasPrefix(lLower, "urlpaths") {

keys := []string{"restrict-paths", "exclude-paths", "forward-paths"}
keys := []string{"restrict-paths", "exclude-paths", "forward-paths", "conditional-http-service"}
i++
i = c.getConfigLeaves(line, i, "URLPaths", keys)
}
Expand Down
47 changes: 46 additions & 1 deletion validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (c *Config) ValidateHTTPRequest(w http.ResponseWriter, r *http.Request) (bo
return false, http.StatusMethodNotAllowed
}

// This is the order: restrict-paths, exclude-path, forward-paths
// This is the order: restrict-paths, exclude-path, forward-paths, conditional-http-service

// restrict-paths
for i := 0; i < len(c.URLPaths.Restrict); i++ {
Expand Down Expand Up @@ -78,5 +78,50 @@ func (c *Config) ValidateHTTPRequest(w http.ResponseWriter, r *http.Request) (bo
}
}

// conditional-http-service
ip := strings.Split(strings.Replace(strings.Replace(r.RemoteAddr, "[", "", -1), "]", "", -1), ":")[0]
qs := r.URL.RawQuery

for i := 0; i < len(c.URLPaths.ServeOnlyTo); i++ {
if rPath == c.URLPaths.ServeOnlyTo[i].URLPath {
// check headers
if c.URLPaths.ServeOnlyTo[i].RuleType == CondHTTPSvc_Header {
for _, v := range r.Header {
for j := 0; j < len(c.URLPaths.ServeOnlyTo[i].ServeOnlyToCriteria); j++ {
m := c.URLPaths.ServeOnlyTo[i].ServeOnlyToCriteria[j]
for k := 0; k < len(v); k++ {
if strings.Contains(v[k], m) {
// The caller can view the page - as its request header
// has a value that matches the ServerOnlyTo critiera
return true, 0
}
}
}
}
}

// check IP address and query string
for j := 0; j < len(c.URLPaths.ServeOnlyTo[i].ServeOnlyToCriteria); j++ {
if c.URLPaths.ServeOnlyTo[i].ServeOnlyToCriteria[j] == ip || strings.Contains(qs, c.URLPaths.ServeOnlyTo[i].ServeOnlyToCriteria[j]) {
// The caller can view the page - as its request header
// has a value that matches the ServerOnlyTo critiera
return true, 0
}
}

// If we get here, it means that:
// 1. there is a rule for the current r.Path
// 2. there is no match found to allow the client to recieve the content
// so, return error
errCode := c.URLPaths.ServeOnlyTo[i].HTTPStatusCode

if errCode < 1 {
errCode = 404 // default error code
}

return false, errCode
}
}

return true, 0
}

0 comments on commit 6dd450b

Please sign in to comment.