From 89b91cea9901d8a92adfdbb5d0e8491344253132 Mon Sep 17 00:00:00 2001 From: Owen Gong Date: Mon, 20 Mar 2023 03:17:08 +0800 Subject: [PATCH] Added "http_url" tag to validate if the variable is a HTTP(s) URL (#1047) --- README.md | 1 + baked_in.go | 18 ++++++++++++ validator_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/README.md b/README.md index f2b6772..84b4cb6 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Baked-in Validations | unix_addr | Unix domain socket end point Address | | uri | URI String | | url | URL String | +| http_url | HTTP URL String | | url_encoded | URL Encoded | | urn_rfc2141 | Urn RFC 2141 String | diff --git a/baked_in.go b/baked_in.go index ef9e055..eafc2a0 100644 --- a/baked_in.go +++ b/baked_in.go @@ -123,6 +123,7 @@ var ( "e164": isE164, "email": isEmail, "url": isURL, + "http_url": isHttpURL, "uri": isURI, "urn_rfc2141": isUrnRFC2141, // RFC 2141 "file": isFile, @@ -1403,6 +1404,23 @@ func isURL(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL. +func isHttpURL(fl FieldLevel) bool { + if !isURL(fl) { + return false + } + + field := fl.Field() + switch field.Kind() { + case reflect.String: + + s := strings.ToLower(field.String()) + return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141. func isUrnRFC2141(fl FieldLevel) bool { field := fl.Field() diff --git a/validator_test.go b/validator_test.go index e2a608f..79cfb55 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7878,6 +7878,77 @@ func TestUrl(t *testing.T) { PanicMatches(t, func() { _ = validate.Var(i, "url") }, "Bad field type int") } +func TestHttpUrl(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"http://foo.bar#com", true}, + {"http://foobar.com", true}, + {"HTTP://foobar.com", true}, + {"https://foobar.com", true}, + {"foobar.com", false}, + {"http://foobar.coffee/", true}, + {"http://foobar.中文网/", true}, + {"http://foobar.org/", true}, + {"http://foobar.org:8080/", true}, + {"ftp://foobar.ru/", false}, + {"file:///etc/passwd", false}, + {"file://C:/windows/win.ini", false}, + {"http://user:pass@www.foobar.com/", true}, + {"http://127.0.0.1/", true}, + {"http://duckduckgo.com/?q=%2F", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/?foo=bar#baz=qux", true}, + {"http://foobar.com?foo=bar", true}, + {"http://www.xn--froschgrn-x9a.net/", true}, + {"", false}, + {"a://b", false}, + {"xyz://foobar.com", false}, + {"invalid.", false}, + {".com", false}, + {"rtmp://foobar.com", false}, + {"http://www.foo_bar.com/", true}, + {"http://localhost:3000/", true}, + {"http://foobar.com/#baz", true}, + {"http://foobar.com#baz=qux", true}, + {"http://foobar.com/t$-_.+!*\\'(),", true}, + {"http://www.foobar.com/~foobar", true}, + {"http://www.-foobar.com/", true}, + {"http://www.foo---bar.com/", true}, + {"mailto:someone@example.com", false}, + {"irc://irc.server.org/channel", false}, + {"irc://#channel@network", false}, + {"/abs/test/dir", false}, + {"./rel/test/dir", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "http_url") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "http_url" { + t.Fatalf("Index: %d HTTP URL failed Error: %s", i, errs) + } + } + } + } + + i := 1 + PanicMatches(t, func() { _ = validate.Var(i, "http_url") }, "Bad field type int") +} + func TestUri(t *testing.T) { tests := []struct { param string