Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre-conditions to reject invalid URL inputs #28

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/ring/util/http_response.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,21 @@
:headers {}
:body body}))

(defn ^{:private true}
url-like?
"Is x a URL-like parameter?"
[x]
(or (string? x)
(instance? java.net.URI x)
(instance? java.net.URL x)))

(defn created
"201 Created (Success)
The request has been fulfilled and resulted in a new resource being created."
([] (created nil))
([url] (created url nil))
([url body]
{:pre [(or (nil? url) (url-like? url))]}
{:status 201
:headers {"Location" url}
:body body}))
Expand Down Expand Up @@ -127,6 +136,7 @@
"300 Multiple Choices (Redirection)
There are multiple options for the resource that the client may follow."
([url]
{:pre [(url-like? url)]}
{:status 300
:headers {"Location" url}
:body ""}))
Expand All @@ -135,6 +145,7 @@
"301 Moved Permanently (Redirection)
This and all future requests should be directed to the given URI."
([url]
{:pre [(url-like? url)]}
{:status 301
:headers {"Location" url}
:body ""}))
Expand All @@ -143,6 +154,7 @@
"302 Found (Redirection)
The resource was found but at a different URI."
([url]
{:pre [(url-like? url)]}
{:status 302
:headers {"Location" url}
:body ""}))
Expand All @@ -151,6 +163,7 @@
"303 See Other (Redirection)
The response to the request can be found under another URI using a GET method."
([url]
{:pre [(url-like? url)]}
{:status 303
:headers {"Location" url}
:body ""}))
Expand All @@ -167,6 +180,7 @@
"305 Use Proxy (Redirection)
This single request is to be repeated via the proxy given by the Location field."
([url]
{:pre [(url-like? url)]}
{:status 305
:headers {"Location" url}
:body ""}))
Expand All @@ -175,6 +189,7 @@
"307 Temporary Redirect (Redirection)
The request should be repeated with another URI but future requests can still use the original URI."
([url]
{:pre [(url-like? url)]}
{:status 307
:headers {"Location" url}
:body ""}))
Expand All @@ -183,6 +198,7 @@
"308 Permanent Redirect (Redirection)
The request and all future requests should be repeated using another URI."
([url]
{:pre [(url-like? url)]}
{:status 308
:headers {"Location" url}
:body ""}))
Expand Down
112 changes: 64 additions & 48 deletions test/ring/util/http_response_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
(is (= {:status 206 :headers {} :body "body"} (partial-content "body")))
(is (= {:status 207 :headers {} :body "body"} (multi-status "body")))
(is (= {:status 208 :headers {} :body "body"} (already-reported "body")))
(is (= {:status 226 :headers {} :body "body"} (im-used "body"))))
(is (= {:status 226 :headers {} :body "body"} (im-used "body")))
(testing "Must reject invalid inputs"
(is (thrown? AssertionError (created 1) "Non-nil URL must be a URL-like type"))
(is (thrown? AssertionError (created {}) "Non-nil URL must be a URL-like type"))))

(testing "Redirection"
(is (= {:status 300 :headers {"Location" "/url"} :body ""} (multiple-choices "/url")))
Expand All @@ -30,55 +33,68 @@
(is (= {:status 304 :headers {} :body ""} (not-modified)))
(is (= {:status 305 :headers {"Location" "/url"} :body ""} (use-proxy "/url")))
(is (= {:status 307 :headers {"Location" "/url"} :body ""} (temporary-redirect "/url")))
(is (= {:status 308 :headers {"Location" "/url"} :body ""} (permanent-redirect "/url"))))
(is (= {:status 308 :headers {"Location" "/url"} :body ""} (permanent-redirect "/url")))
(testing "Must reject invalid inputs"
(is (thrown? AssertionError (multiple-choices nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (multiple-choices 1) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (moved-permanently nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (moved-permanently {}) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (found nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (found ::not-a-url) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (use-proxy nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (use-proxy ::not-a-url) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (temporary-redirect nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (temporary-redirect ::not-a-url) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (permanent-redirect nil) "URL must be a non-nil URL-like type"))
(is (thrown? AssertionError (permanent-redirect ::not-a-url) "URL must be a non-nil URL-like type"))))

(testing "ClientError"
(is (= {:status 400 :headers {} :body "body"} (bad-request "body")))
(is (= {:status 401 :headers {} :body "body"} (unauthorized "body")))
(is (= {:status 402 :headers {} :body "body"} (payment-required "body")))
(is (= {:status 403 :headers {} :body "body"} (forbidden "body")))
(is (= {:status 404 :headers {} :body "body"} (not-found "body")))
(is (= {:status 405 :headers {} :body "body"} (method-not-allowed "body")))
(is (= {:status 406 :headers {} :body "body"} (not-acceptable "body")))
(is (= {:status 407 :headers {} :body "body"} (proxy-authentication-required "body")))
(is (= {:status 408 :headers {} :body "body"} (request-timeout "body")))
(is (= {:status 409 :headers {} :body "body"} (conflict "body")))
(is (= {:status 410 :headers {} :body "body"} (gone "body")))
(is (= {:status 411 :headers {} :body "body"} (length-required "body")))
(is (= {:status 412 :headers {} :body "body"} (precondition-failed "body")))
(is (= {:status 413 :headers {} :body "body"} (request-entity-too-large "body")))
(is (= {:status 414 :headers {} :body "body"} (request-uri-too-long "body")))
(is (= {:status 415 :headers {} :body "body"} (unsupported-media-type "body")))
(is (= {:status 416 :headers {} :body "body"} (requested-range-not-satisfiable "body")))
(is (= {:status 417 :headers {} :body "body"} (expectation-failed "body")))
(is (= {:status 420 :headers {} :body "body"} (enhance-your-calm "body")))
(is (= {:status 422 :headers {} :body "body"} (unprocessable-entity "body")))
(is (= {:status 423 :headers {} :body "body"} (locked "body")))
(is (= {:status 424 :headers {} :body "body"} (failed-dependency "body")))
(is (= {:status 425 :headers {} :body "body"} (unordered-collection "body")))
(is (= {:status 426 :headers {} :body "body"} (upgrade-required "body")))
(is (= {:status 428 :headers {} :body "body"} (precondition-required "body")))
(is (= {:status 429 :headers {} :body "body"} (too-many-requests "body")))
(is (= {:status 431 :headers {} :body "body"} (request-header-fields-too-large "body")))
(is (= {:status 449 :headers {} :body "body"} (retry-with "body")))
(is (= {:status 450 :headers {} :body "body"} (blocked-by-windows-parental-controls "body")))
(is (= {:status 451 :headers {} :body "body"} (unavailable-for-legal-reasons "body"))))
(testing "ClientError"
(is (= {:status 400 :headers {} :body "body"} (bad-request "body")))
(is (= {:status 401 :headers {} :body "body"} (unauthorized "body")))
(is (= {:status 402 :headers {} :body "body"} (payment-required "body")))
(is (= {:status 403 :headers {} :body "body"} (forbidden "body")))
(is (= {:status 404 :headers {} :body "body"} (not-found "body")))
(is (= {:status 405 :headers {} :body "body"} (method-not-allowed "body")))
(is (= {:status 406 :headers {} :body "body"} (not-acceptable "body")))
(is (= {:status 407 :headers {} :body "body"} (proxy-authentication-required "body")))
(is (= {:status 408 :headers {} :body "body"} (request-timeout "body")))
(is (= {:status 409 :headers {} :body "body"} (conflict "body")))
(is (= {:status 410 :headers {} :body "body"} (gone "body")))
(is (= {:status 411 :headers {} :body "body"} (length-required "body")))
(is (= {:status 412 :headers {} :body "body"} (precondition-failed "body")))
(is (= {:status 413 :headers {} :body "body"} (request-entity-too-large "body")))
(is (= {:status 414 :headers {} :body "body"} (request-uri-too-long "body")))
(is (= {:status 415 :headers {} :body "body"} (unsupported-media-type "body")))
(is (= {:status 416 :headers {} :body "body"} (requested-range-not-satisfiable "body")))
(is (= {:status 417 :headers {} :body "body"} (expectation-failed "body")))
(is (= {:status 420 :headers {} :body "body"} (enhance-your-calm "body")))
(is (= {:status 422 :headers {} :body "body"} (unprocessable-entity "body")))
(is (= {:status 423 :headers {} :body "body"} (locked "body")))
(is (= {:status 424 :headers {} :body "body"} (failed-dependency "body")))
(is (= {:status 425 :headers {} :body "body"} (unordered-collection "body")))
(is (= {:status 426 :headers {} :body "body"} (upgrade-required "body")))
(is (= {:status 428 :headers {} :body "body"} (precondition-required "body")))
(is (= {:status 429 :headers {} :body "body"} (too-many-requests "body")))
(is (= {:status 431 :headers {} :body "body"} (request-header-fields-too-large "body")))
(is (= {:status 449 :headers {} :body "body"} (retry-with "body")))
(is (= {:status 450 :headers {} :body "body"} (blocked-by-windows-parental-controls "body")))
(is (= {:status 451 :headers {} :body "body"} (unavailable-for-legal-reasons "body"))))

(testing "ServerError"
(is (= {:status 500 :headers {} :body "body"} (internal-server-error "body")))
(is (= {:status 501 :headers {} :body "body"} (not-implemented "body")))
(is (= {:status 502 :headers {} :body "body"} (bad-gateway "body")))
(is (= {:status 503 :headers {} :body "body"} (service-unavailable "body")))
(is (= {:status 504 :headers {} :body "body"} (gateway-timeout "body")))
(is (= {:status 505 :headers {} :body "body"} (http-version-not-supported "body")))
(is (= {:status 506 :headers {} :body "body"} (variant-also-negotiates "body")))
(is (= {:status 507 :headers {} :body "body"} (insufficient-storage "body")))
(is (= {:status 508 :headers {} :body "body"} (loop-detected "body")))
(is (= {:status 509 :headers {} :body "body"} (bandwidth-limit-exceeded "body")))
(is (= {:status 510 :headers {} :body "body"} (not-extended "body")))
(is (= {:status 511 :headers {} :body "body"} (network-authentication-required "body")))
(is (= {:status 598 :headers {} :body "body"} (network-read-timeout "body")))
(is (= {:status 599 :headers {} :body "body"} (network-connect-timeout "body")))))
(testing "ServerError"
(is (= {:status 500 :headers {} :body "body"} (internal-server-error "body")))
(is (= {:status 501 :headers {} :body "body"} (not-implemented "body")))
(is (= {:status 502 :headers {} :body "body"} (bad-gateway "body")))
(is (= {:status 503 :headers {} :body "body"} (service-unavailable "body")))
(is (= {:status 504 :headers {} :body "body"} (gateway-timeout "body")))
(is (= {:status 505 :headers {} :body "body"} (http-version-not-supported "body")))
(is (= {:status 506 :headers {} :body "body"} (variant-also-negotiates "body")))
(is (= {:status 507 :headers {} :body "body"} (insufficient-storage "body")))
(is (= {:status 508 :headers {} :body "body"} (loop-detected "body")))
(is (= {:status 509 :headers {} :body "body"} (bandwidth-limit-exceeded "body")))
(is (= {:status 510 :headers {} :body "body"} (not-extended "body")))
(is (= {:status 511 :headers {} :body "body"} (network-authentication-required "body")))
(is (= {:status 598 :headers {} :body "body"} (network-read-timeout "body")))
(is (= {:status 599 :headers {} :body "body"} (network-connect-timeout "body")))))

(declare slingshots?)
(defmethod assert-expr 'slingshots? [msg form]
Expand Down