pax_global_header00006660000000000000000000000064141750661310014516gustar00rootroot0000000000000052 comment=9a9dc4bd90973f8907b68fe5d14c673ed11c1b47 lambdaisland-uri-clojure-1.13.95/000077500000000000000000000000001417506613100165475ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/.VERSION_PREFIX000066400000000000000000000000041417506613100207440ustar00rootroot000000000000001.13lambdaisland-uri-clojure-1.13.95/.circleci/000077500000000000000000000000001417506613100204025ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/.circleci/config.yml000066400000000000000000000027701417506613100224000ustar00rootroot00000000000000version: 2.1 orbs: kaocha: lambdaisland/kaocha@0.0.1 clojure: lambdaisland/clojure@0.0.2 commands: checkout_and_run: parameters: clojure_version: type: string steps: - checkout - clojure/with_cache: cache_version: << parameters.clojure_version >> steps: - run: clojure -e '(println (System/getProperty "java.runtime.name") (System/getProperty "java.runtime.version") "\nClojure" (clojure-version))' - run: npm install ws - kaocha/execute: args: "clj --reporter documentation --plugin cloverage --codecov" clojure_version: << parameters.clojure_version >> - kaocha/execute: args: "cljs --reporter documentation" clojure_version: << parameters.clojure_version >> - kaocha/upload_codecov: flags: clj jobs: java-15-clojure-1_10: executor: clojure/openjdk15 steps: [{checkout_and_run: {clojure_version: "1.10.1"}}] java-11-clojure-1_10: executor: clojure/openjdk11 steps: [{checkout_and_run: {clojure_version: "1.10.1"}}] java-9-clojure-1_10: executor: clojure/openjdk9 steps: [{checkout_and_run: {clojure_version: "1.10.1"}}] java-8-clojure-1_10: executor: clojure/openjdk8 steps: [{checkout_and_run: {clojure_version: "1.10.1"}}] workflows: kaocha_test: jobs: - java-15-clojure-1_10 - java-11-clojure-1_10 - java-9-clojure-1_10 - java-8-clojure-1_10 lambdaisland-uri-clojure-1.13.95/.dir-locals.el000066400000000000000000000000771417506613100212040ustar00rootroot00000000000000((nil . ((cider-clojure-cli-global-options . "-A:dev:test")))) lambdaisland-uri-clojure-1.13.95/.gitignore000066400000000000000000000002701417506613100205360ustar00rootroot00000000000000/target /classes /checkouts pom.xml.asc *.jar *.class /.lein-* /.nrepl-port .hgignore .hg/ gh-pages .cljs_rhino_repl .cljs_node_repl out node_modules .cpcache package-lock.json .store lambdaisland-uri-clojure-1.13.95/.travis.yml000066400000000000000000000003431417506613100206600ustar00rootroot00000000000000language: clojure install: - curl https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein > my-lein - chmod +x my-lein - ./my-lein deps - ./my-lein --version script: ./my-lein test-all jdk: - oraclejdk8 lambdaisland-uri-clojure-1.13.95/CHANGELOG.md000066400000000000000000000046221417506613100203640ustar00rootroot00000000000000# 1.13.95 (2022-01-28 / a9cbeff) ## Fixed - Fix a stack overflow in `normalize/char-seq` for really large query parameter values # 1.12.89 (2021-11-29 / 2118a75) ## Changed - Support `toString` on Babashka (requires recent `bb`) # 1.11.86 (2021-10-28 / 22c27af) ## Fixed - Fixed an issue in `lambdaisland.uri.normalize/normalize-query` which did not take into account utf-16 encoding. # 1.10.79 (2021-10-12 / d90c6a8) ## Changed - `lambdaisland.uri.normalize/normalize` now also normalizes the fragment. # 1.4.74 (2021-09-06 / e07f9fd) ## Added - `uri-str` as an explicit `lambdaisland.uri.URI` to string conversion ## Fixed - Fixed compatibility with Babashka/SCI. Note that on babashka we can't implement IFn or toString, so converting a `URI` back to a string needs to be done explicitly with `uri-str`, and it is not possible to use a URI as a function. (`(:path uri)` is ok, `(uri :path)` is not). # 1.4.70 (2021-05-31 / 76999dc) ## Added - Add `uri?` predicate. # 1.4.54 (2020-06-16 / 05a8a19) ## Fixed - Make query decoding handle `+` as space, so the conversion between maps and query strings correctly round trips. - Handle percent encoding of control characters (codepoints < 16) - make `lambdaisland.uri.platform/string->byte-seq` return unsigned bytes on both plaforms (clj/cljs) # 1.4.49 (2020-06-11 / ee48e58) ## Changed - Make `assoc-query` / `query-encode` encode spaces as "+" rather than "%20", which brings it in line to how most languages/libraries do it. # 1.3.45 (2020-05-01 / a04368b) ## Added - Added function for dealing with query strings as maps: `query-string->map`, `map->query-string`, `query-map`, `query-encode`, `assoc-query`, `assoc-query*`. ## Fixed - Fix query string normalization, for delimiter characters like `=` and `+` there is a semantic difference between the encoded and decoded form, when they are encoded in the input normalization should not decode them and vice versa # 1.2.1 (2020-02-23 / a992787) ## Changed - Remove dependencies on ClojureScript and data.json. # 1.2.0 (2020-02-17 / c0e1f1a) ## Added - `lambdaisland.uri.normalize/normalize`, for normalizing URI instances. ## Changed - Added type hints to avoid reflection (thanks @totakke!) # 1.1.0 (2017-04-25) ## Added - Predicate functions `absolute?` and `relative?` # 1.0.0 (2017-02-23) ## Added - Initial release, public vars: `uri`, `join`, `coerce`, `parse`, `edn-readers`lambdaisland-uri-clojure-1.13.95/LICENSE000066400000000000000000000405251417506613100175620ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. lambdaisland-uri-clojure-1.13.95/README.md000066400000000000000000000147411417506613100200350ustar00rootroot00000000000000# lambdaisland/uri [![CircleCI](https://circleci.com/gh/lambdaisland/uri.svg?style=svg)](https://circleci.com/gh/lambdaisland/uri) [![cljdoc badge](https://cljdoc.org/badge/lambdaisland/uri)](https://cljdoc.org/d/lambdaisland/uri) [![Clojars Project](https://img.shields.io/clojars/v/lambdaisland/uri.svg)](https://clojars.org/lambdaisland/uri) A pure Clojure/ClojureScript URI library. Key features - 100% cross-platform `.cljc` - RFC compliant joining of URIs - relative URIs as first class citizens ## Lambda Island Open Source   uri is part of a growing collection of quality Clojure libraries created and maintained by the fine folks at [Gaiwan](https://gaiwan.co). Pay it forward by [becoming a backer on our Open Collective](http://opencollective.com/lambda-island), so that we may continue to enjoy a thriving Clojure ecosystem. You can find an overview of our projects at [lambdaisland/open-source](https://github.com/lambdaisland/open-source).     ## Rationale There are a number of Clojure libraries for working with URI/URLs (see [Similar projects](#similar-projects) below). They all rely to some degree on `java.net.URI` or `java.net.URL`. This lib provides a pure-Clojure/ClojureScript alternative. See the [announcement blog post](https://lambdaisland.com/blog/27-02-2017-announcing-lambdaisland-uri) ## Installation To install, add the following dependency to your project or build file: deps.edn: ``` clojure lambdaisland/uri {:mvn/version "1.12.95"} ``` project.clj ``` clojure [lambdaisland/uri "1.12.95"] ``` ## Usage ``` clojure (require '[lambdaisland.uri :refer [uri join]]) ;; uri :: String -> lambdaisland.uri.URI (uri "//example.com/foo/bar") ;;=> #lambdaisland/uri "//example.com/foo/bar" ;; A URI is a record, use assoc to update specific parts ;; Use `str` if you want the URI back as a string (str (assoc (uri "//example.com/foo/bar") :scheme "https" :user "arne" :password "supersecret" :host "lambdaisland.com" :port "3333" :path "/hello/world" :query "q=5" :fragment "section1")) ;;=> "https://arne:supersecret@lambdaisland.com:3333/hello/world?q=5#section1" ;; RFC compliant joining of relative URIs (join "//example.com/foo/bar" "./~arne/site/" "../foo.png") ;;=> #lambdaisland/uri "//example.com/foo/~arne/foo.png" ;; Arguments to `join` are coerced, you can pass strings, java.net.URI, or any x ;; for which `(str x)` returns a URI string. (join (java.net.URI. "http://example.com/foo/bar") (uri "./~arne/site/") "../foo.png") ;;=> #lambdaisland/uri "http://example.com/foo/~arne/foo.png" ;; URI implements IFn for keyword based lookup, so it's fully ;; interface-compatible with Clojure maps. (:path (uri "http://example.com/foo/bar")) ;; Instances of URI are printed with a #lambdaisland/uri reader tag. To read ;; them back from EDN, use the provided readers. (require '[clojure.edn :as edn]) (edn/read-string {:readers lambdaisland.uri/edn-readers} "#lambdaisland/uri \"http://example.com/foo/~arne/foo.png\"") ``` [Full API docs are on Cljdoc](https://cljdoc.org/d/lambdaisland/uri) ## Babashka-specific caveats (also applies to SCI) Instances of URI implement the `toString` method, so calling `(str uri)` gives you the URI back as a string. They also implement the `IFn` interfaces so they are callable the way maps are. On babashka implementing interfaces or overriding Object methods is not supported. As an alternative to `clojure.core/str` you can use `lambdaisland.uri/uri-str`. As an alternative to using the URI as a function, use the keyword as a function, or use `clojure.core/get` ``` clojure ;; clojure / clojurescript (str uri) ;; "https://example.com" (uri :domain) ;; "example.com" ;; bb (str uri) ;; "{:scheme "https", :domain "example.com", :path ...}" (uri :domain) ;; nil (uri/uri-str uri) ;; "https://example.com" (:domain uri) ;; "example.com" (get uri :domain) ;; "example.com" ``` ## Similar projects * [exploding-fish](https://github.com/wtetzner/exploding-fish) I was not aware at the time of creating lambdaisland/uri of exploding fish. It is the most mature pure-Clojure URI lib out there. It does not provide ClojureScript support. * [cemerick/url](https://github.com/cemerick/url) Cross platform (cljx), Clojure version uses `java.net.URL`. * [michaelklishin/urly](https://github.com/michaelklishin/urly) Based on `java.net.URI`. * [codonnell/uri](https://github.com/codonnell/uri) Based on `java.net.URI`. ## Further reading [RFC3986 Uniform Resource Identifier (URI): Generic Syntax](https://www.ietf.org/rfc/rfc3986.txt) This library implements the algorithm specified in [Section 5.2](https://tools.ietf.org/html/rfc3986#section-5.2) of that RFC. It has been tested against [this list of test cases compiled by the W3C](https://www.w3.org/2004/04/uri-rel-test.html). ## Contributing Everyone has a right to submit patches to uri, and thus become a contributor. Contributors MUST - adhere to the [LambdaIsland Clojure Style Guide](https://nextjournal.com/lambdaisland/clojure-style-guide) - write patches that solve a problem. Start by stating the problem, then supply a minimal solution. `*` - agree to license their contributions as MPL 2.0. - not break the contract with downstream consumers. `**` - not break the tests. Contributors SHOULD - update the CHANGELOG and README. - add tests for new functionality. If you submit a pull request that adheres to these rules, then it will almost certainly be merged immediately. However some things may require more consideration. If you add new dependencies, or significantly increase the API surface, then we need to decide if these changes are in line with the project's goals. In this case you can start by [writing a pitch](https://nextjournal.com/lambdaisland/pitch-template), and collecting feedback on it. `*` This goes for features too, a feature needs to solve a problem. State the problem it solves, then supply a minimal solution. `**` As long as this project has not seen a public release (i.e. is not on Clojars) we may still consider making breaking changes, if there is consensus that the changes are justified. ## License Copyright © 2017-2021 Arne Brasseur and Contributors Licensed under the term of the Mozilla Public License 2.0, see LICENSE. lambdaisland-uri-clojure-1.13.95/bb.edn000066400000000000000000000002551417506613100176240ustar00rootroot00000000000000{:deps {lambdaisland/open-source {:git/url "https://github.com/lambdaisland/open-source" :git/sha "835e8f001659ed07e5835a554b99828331a88e8d"}}} lambdaisland-uri-clojure-1.13.95/bin/000077500000000000000000000000001417506613100173175ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/bin/kaocha000077500000000000000000000001551417506613100204740ustar00rootroot00000000000000#!/usr/bin/env bash [[ -d "node_modules/ws" ]] || npm install ws clojure -A:dev:test -m kaocha.runner "$@" lambdaisland-uri-clojure-1.13.95/bin/proj000077500000000000000000000004111417506613100202130ustar00rootroot00000000000000#!/usr/bin/env bb (ns proj (:require [lioss.main :as lioss])) (lioss/main {:license :mpl :group-id "lambdaisland" :inception-year 2017 :description "A pure Clojure/ClojureScript URI library."}) ;; Local Variables: ;; mode:clojure ;; End: lambdaisland-uri-clojure-1.13.95/deps.edn000066400000000000000000000006711417506613100201760ustar00rootroot00000000000000{:paths ["src"] :deps {org.clojure/clojure {:mvn/version "1.10.1"}} :aliases {:dev {} :test {:extra-paths ["test"] :extra-deps {org.clojure/clojurescript {:mvn/version "1.10.597"} lambdaisland/kaocha {:mvn/version "RELEASE"} com.lambdaisland/kaocha-cljs {:mvn/version "1.0.113"} lambdaisland/kaocha-junit-xml {:mvn/version "RELEASE"} org.clojure/test.check {:mvn/version "1.0.0"}}}}} lambdaisland-uri-clojure-1.13.95/generate_docs000077500000000000000000000002271417506613100213000ustar00rootroot00000000000000#!/bin/bash export AUTODOC_CMD="lein with-profile +codox codox" \curl -sSL https://raw.githubusercontent.com/plexus/autodoc/master/autodoc.sh | bash lambdaisland-uri-clojure-1.13.95/package.json000066400000000000000000000000571417506613100210370ustar00rootroot00000000000000{ "dependencies": { "ws": "^8.4.2" } } lambdaisland-uri-clojure-1.13.95/pom.xml000066400000000000000000000053361417506613100200730ustar00rootroot00000000000000 4.0.0 lambdaisland uri 1.12.89 uri A pure Clojure/ClojureScript URI library. https://github.com/lambdaisland/uri 2017 Lambda Island https://lambdaisland.com MPL-2.0 https://www.mozilla.org/media/MPL/2.0/index.txt https://github.com/lambdaisland/uri scm:git:git://github.com/lambdaisland/uri.git scm:git:ssh://git@github.com/lambdaisland/uri.git 46735a61f5e0f758bfb914e65e7bd843496ca842 org.clojure clojure 1.10.1 src src org.apache.maven.plugins maven-compiler-plugin 3.8.1 org.apache.maven.plugins maven-jar-plugin 3.2.0 46735a61f5e0f758bfb914e65e7bd843496ca842 org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign clojars https://repo.clojars.org/ clojars Clojars repository https://clojars.org/repo lambdaisland-uri-clojure-1.13.95/repl_sessions/000077500000000000000000000000001417506613100214375ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/repl_sessions/encode.clj000066400000000000000000000074241417506613100233750ustar00rootroot00000000000000(ns encode (:require [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as normalize])) (-> (uri/uri "https://example.com/") (assoc :query (str "src=" (normalize/percent-encode "/foo/bar" :query))) (uri/assoc-query :frame-id "frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f" :kind "iframe") str) "https://example.com/?src=/foo/bar&kind=iframe&frame-id=frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f" "https://example.com/?src=/foo/bar&kind=iframe&frame-id=frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f" (-> (uri/uri "https://example.com/") (assoc :query (str "src=" (normalize/percent-encode "/foo/bar" :query))) str) ;;=> "https://example.com/?src=/foo/bar" (str domain "/a/iframe-viewer?src=" (url/url-encode src) "&frame-id=" frame-id "&kind=" (name kind)) (-> (uri/uri domain) (assoc :path "/a/iframe-viewer") (assoc-query :src src :frame-id frame-id :kind (name kind)) str) (normalize/normalize (uri/uri "https://dev.nextjournalusercontent.com:8888/a/iframe-viewer?src=%2Fdata%2FQmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication%252Fvnd.nextjournal.html%252Bhtml&frame-id=frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f&kind=iframe")) #lambdaisland/uri "https://dev.nextjournalusercontent.com:8888/a/iframe-viewer?src=/data/QmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V?content-type=application%252Fvnd.nextjournal.html%252Bhtml&frame-id=frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f&kind=iframe" (-> (uri/uri "https://example.com/") (uri/assoc-query :xxx "?#") (uri/assoc-query :yyy "?#") (uri/assoc-query :zzz "?#&=") str) "https://example.com/?src=/foo/bar&kind=iframe&frame-id=frame-1d3ba257-4c55-48a5-8011-5d1aba5c240f" (-> (uri/uri "https://example.com/") (assoc :query (str "src=" (normalize/percent-encode "/foo/bar" :query))) str) "https://example.com/?src=/foo/bar" "https://example.com/?src=%2Ffoo%2Fbar" (uri/query-map (uri/uri "?foo=%20%2B%26xxx%3D123")) ;;=> {:foo " +&xxx=123"} (uri/query-map (normalize/normalize (uri/uri "?foo=%20%2B%26xxx%3D123"))) ;;=> {:foo " +", :xxx "123"} (-> (uri/uri "?foo=%20%2B%26xxx%3D123") normalize/normalize) #lambdaisland/uri "?foo=%20+&xxx=123" (normalize/percent-encode "+" :query) ;; => {:scheme "https" ;; :user nil ;; :password nil ;; :host "example.com" ;; :port nil ;; :path "/" ;; :query "" ;; :fragment "hello"} ;; => {:scheme "https" ;; :user nil ;; :password nil ;; :host "example.com" ;; :port nil ;; :path "/" ;; :query "#hello" ;; :fragment nil} Parameter id must conform to #"^\d+$", but got "%2Fdata%2FQmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication%2Fvnd.nextjournal.html%2Bhtml" . Parameter id must conform to clojure.core/uuid? , but got "%2Fdata%2FQmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication%2Fvnd.nextjournal.html%2Bhtml" . Parameter id must conform to com.nextjournal.journal.params/matches-base58? , but got "%2Fdata%2FQmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication%2Fvnd.nextjournal.html%2Bhtml" . Parameter id must conform to com.nextjournal.journal.params/matches-uuid? , but got "%2Fdata%2FQmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication%2Fvnd.nextjournal.html%2Bhtml" . https://dev.nextjournalusercontent.com:8888/a/ (-> (uri/uri "https://example.com") (assoc :path "/a/iframe-viewer") (uri/assoc-query :frame-id 123 :kind "iframe" :src "/data/QmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V?content-type=application/vnd.nextjournal.html+html") ) #lambdaisland/uri "https://example.com/a/iframe-viewer?src=/data/QmfFEyfAfGq3siNJyVxMcddyxtHtRjFKqfm81Nw5wiML3V%3Fcontent-type%3Dapplication/vnd.nextjournal.html+html&kind=iframe&frame-id=123" lambdaisland-uri-clojure-1.13.95/repl_sessions/merge_join.clj000066400000000000000000000015441417506613100242530ustar00rootroot00000000000000(require '[lambdaisland.uri :as uri]) (-> (uri/uri "https://my.service/some-context/?api_key=123") (update :path str "some-resource")) (defn uri-merge [& uris] (let [uri-maps (->> uris (map uri/uri) (map #(into {} (filter (comp some? val)) %))) merged-path (some->> uris (keep :path) seq (apply uri/join) :path)] (-> (apply merge uri-maps) (assoc :path merged-path) uri/map->URI))) (str (uri-merge "http://localhost:3303/hello-world" "https://en.wikipedia.org/wiki/VT100#Variants")) ;; => "https://en.wikipedia.org:3303#Variants" (def endpoint "https://my.service/some-context/") (def api-key "123") (defn api-uri [& segments] (-> (apply uri/join endpoint segments) (assoc :query (str "api-key=123")))) (api-uri endpoint "some-resource") ;; => "https://my.service/some-context/some-resource?api-key=123" lambdaisland-uri-clojure-1.13.95/repl_sessions/poke.clj000066400000000000000000000003621417506613100230700ustar00rootroot00000000000000(ns poke (:require [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as norm])) (:fragment (norm/normalize (uri/map->URI {:fragment "'#'schön"}))) "'%23'sch%C3%B6n" (norm/normalize-fragment "schön") ;; => "sch%C3%B6n" lambdaisland-uri-clojure-1.13.95/repl_sessions/round_trip_query_map.clj000066400000000000000000000047111417506613100264030ustar00rootroot00000000000000(ns round-trip-query-map (:require [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as norm] [lambdaisland.uri.platform :as platform] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] [clojure.test.check :as check] [clojure.string :as str])) (def query-map-gen (gen/map gen/keyword gen/string)) (check/quick-check 10000 (prop/for-all [q query-map-gen] (let [res (-> q uri/map->query-string uri/query-string->map)] (or (and (empty? q) (empty? res)) ;; (= nil {}) (= q res))))) (def query-string-part-gen (gen/such-that (complement (partial some #{\& \= \% \space})) gen/string)) (def query-string-gen (gen/fmap (fn [parts] (str/join "&" (map (fn [[k v]] (str k "=" v)) parts))) (gen/list (gen/tuple (gen/such-that seq query-string-part-gen) (gen/such-that seq query-string-part-gen))))) (check/quick-check 100 (prop/for-all [q query-string-gen] (let [res (-> q uri/query-string->map uri/map->query-string norm/percent-decode)] (or (and (empty? q) (empty? res)) ;; (= nil {}) (= q res))))) {:shrunk {:total-nodes-visited 327, :depth 36, :pass? false, :result false, :result-data nil, :time-shrinking-ms 65, :smallest ["=&=&="]}, :failed-after-ms 3, :num-tests 13, :seed 1592289824964, :fail ["£pÓn=Szþ«Ÿs&\f“ɽ¼=wÅýˆ¤‘Ôµ-&È-¼\t½?óø=µ\r÷2¶+’T&-šô•¤+=`MZ—R1-àò!&ÎÉڍÝ=©×DžÂ&¥=Ø#$‡/\\j‡Q&å=¯]3³­ñ˜&Û¼Ô=ãi™–´p‰e&‚Í=Päzi#V*Wäã"], :result false, :result-data nil, :failing-size 12, :pass? false} (-> x uri/map->query-string uri/query-string->map) (seq (platform/string->byte-seq "\u0000")) (map long (.getBytes "\b")) (-> "\u0000=\u0000&\u0001=\u0000&\u0000=\u0000" uri/query-string->map uri/map->query-string norm/percent-decode ) (norm/percent-encode "\b") (platform/byte->hex 16) (uri/map->query-string (uri/query-string->map "x=a+b")) lambdaisland-uri-clojure-1.13.95/repl_sessions/walkthrough.clj000066400000000000000000000022271417506613100244730ustar00rootroot00000000000000(ns repl-sessions.walkthrough (:require [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as normalize])) (def u (uri/uri "https://lambdaisland.com/episodes/all")) (type u) (record? u) (str u) (into {} (uri/uri "https://lambdaisland.com/episodes/all")) ;; => {:scheme "https", ;; :user nil, ;; :password nil, ;; :host "lambdaisland.com", ;; :port nil, ;; :path "/episodes/all", ;; :query nil, ;; :fragment nil} (:host u) (:path u) (assoc u :fragment "hello") (assoc u :path "") (str u) (uri/uri u) (uri/uri (java.net.URI. "http://example.com")) (def base "http://example.com/api/v1/") (uri/join base "hello/world") (uri/join base "/hello/world") (uri/join base "./hello/../../world") (uri/query-map "http://example.com/?foo=bar&hello=world") (-> "http://example.com/?foo=bar&hello=world" (uri/assoc-query :foo 123)) (-> "http://example.com/?foo=bar&hello=world" (uri/assoc-query :foo [123 456])) (-> "http://example.com/?foo=bar&hello=world" (uri/assoc-query :foo "foo bar")) (normalize/normalize (uri/uri "http://example.com/🙈🙉")) (normalize/normalize (uri/uri "http://example.com/%61%62%63")) lambdaisland-uri-clojure-1.13.95/src/000077500000000000000000000000001417506613100173365ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/src/lambdaisland/000077500000000000000000000000001417506613100217515ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/src/lambdaisland/uri.cljc000066400000000000000000000217471417506613100234200ustar00rootroot00000000000000(ns lambdaisland.uri (:refer-clojure :exclude [uri?]) (:require [clojure.string :as str] [lambdaisland.uri.normalize :as normalize]) #?(:clj (:import clojure.lang.IFn))) (def uri-regex #?(:clj #"\A(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)?(\?([^#]*))?(#(.*))?\z" :cljs #"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)?(\?([^#]*))?(#(.*))?$")) (def authority-regex #?(:clj #"\A(([^:]*)(:(.*))?@)?([^:]*)(:(\d*))?\z" :cljs #"^(([^:]*)(:(.*))?@)?([^:]*)(:(\d*))?$")) (defn- authority-string [user password host port] (when host (cond-> user (and user password) (str ":" password) user (str "@") true (str host) port (str ":" port)))) (defn uri-str "Convert the URI instance back to a string" [{:keys [scheme user password host port path query fragment]}] (let [authority (authority-string user password host port)] (cond-> "" scheme (str scheme ":") authority (str "//" authority) true (str path) query (str "?" query) fragment (str "#" fragment)))) (defrecord URI [scheme user password host port path query fragment] #?@(:bb [] :default [IFn (#?(:clj invoke :cljs -invoke) [this kw] (get this kw))]) Object (toString [this] (uri-str this))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; parse (defn- match-uri [uri] (let [matches (re-matches uri-regex uri) [_ _ scheme _ authority path _ query _ fragment] matches] [scheme authority (when (seq path) path) query fragment])) (defn- match-authority [authority] (let [matches (re-matches authority-regex authority) [_ _ user _ password host _ port] matches] [user password host port])) (defn parse "Parse a URI string into a lambadisland.uri.URI record." [uri] (let [[scheme authority path query fragment] (match-uri uri)] (if authority (let [[user password host port] (match-authority authority)] (URI. scheme user password host port path query fragment)) (URI. scheme nil nil nil nil path query fragment)))) (defn uri "Turn the given value into a lambdaisland.uri.URI record, if it isn't one already. Supports String, java.net.URI, and other URI-like objects that return a valid URI string with `str`." [uri-like] (if (instance? URI uri-like) uri-like (parse (str uri-like)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; join / resolve ;; This section is based on RFC 3986 (defn- absolute-path? [path] (= (first path) \/)) (defn- remove-dot-segments "As per RFC 3986 section 5.2.4" [path] (when path (loop [in (str/split path #"(?=/)") out []] (case (first in) "/." (if (next in) (recur (next in) out) (recur nil (conj out "/"))) "/.." (if (next in) (recur (next in) (vec (butlast out))) (recur nil (conj (vec (butlast out)) "/"))) nil (str/join out) (recur (next in) (conj out (first in))))))) (defn- merge-paths [a b] (if (some #{\/} a) (str (re-find #?(:clj #"\A.*/" :cljs #"^.*/") a) b) (if (absolute-path? b) b (str "/" b)))) (defn join* "Join two URI records as per RFC 3986. Handles relative URIs." [base ref] (if (:scheme ref) (update ref :path remove-dot-segments) (-> (if (:host ref) (assoc ref :scheme (:scheme base) :query (:query ref)) (if (nil? (:path ref)) (assoc base :query (some :query [ref base])) (assoc base :path (remove-dot-segments (if (absolute-path? (:path ref)) (:path ref) (merge-paths (:path base) (:path ref)))) :query (:query ref)))) (assoc :fragment (:fragment ref))))) (defn join "Joins any number of URIs as per RFC3986. Arguments can be strings, they will be coerced to URI records." [& uris] (reduce join* (map uri uris))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Query strings (defn- decode-param-pair [param] (let [[k v] (str/split param #"=")] [(if k (normalize/percent-decode k) "") (if v (normalize/percent-decode (str/replace v #"\+" " ")) "")])) (defn query-string->map "Parse a query string, consisting of key=value pairs, separated by \"&\". Takes the following options: - `:keywordize?` whether to turn return keys as keywords. Defaults to `true`. - `:multikeys` how to handle the same key occuring multiple times, defaults to `:duplicates` The possible values for `:multikeys` are - `:never` always return a single value for a key. The rightmost value \"wins\" - `:always` return a map with vectors as values, with successive values of the same key in order - `:duplicates` return a vector for keys that occur multiple times, or a string otherwise" ([q] (query-string->map q nil)) ([q {:keys [multikeys keywordize?] :or {multikeys :duplicates keywordize? true}}] (when (not (str/blank? q)) (->> (str/split q #"&") (map decode-param-pair) (reduce (fn [m [k v]] (let [k (if keywordize? (keyword k) k)] (case multikeys :never (assoc m k v) :always (if (contains? m k) (update m k conj v) (assoc m k [v])) :duplicates (if (contains? m k) (if (vector? (m k)) (update m k conj v) (assoc m k [(m k) v])) (assoc m k v))))) {}))))) (defn query-map "Return the query section of a URI as a map. Will coerce its argument with [[uri]]. Takes an options map, see [[query-string->map]] for options." ([uri] (query-map uri nil)) ([u opts] (query-string->map (:query (uri u)) opts))) (defn query-encode "Percent encoding for query strings. Will percent-encode values that are reserved in query strings only. Encodes spaces as +." [s] (let [encode-char #(cond (= " " %) "+" (re-find #"[^a-zA-Z0-9\-\._~@\/]" %) (normalize/percent-encode %) :else %)] (->> (normalize/char-seq s) (map encode-char) (apply str)))) (defn- encode-param-pair [k v] (str (query-encode (cond (simple-ident? k) (name k) (qualified-ident? k) (str (namespace k) "/" (name k)) :else (str k))) "=" (query-encode (str v)))) (defn map->query-string "Convert a map into a query string, consisting of key=value pairs separated by `&`. The result is percent-encoded so it is always safe to use. Keys can be strings or keywords. If values are collections then this results in multiple entries for the same key. `nil` values are ignored. Values are stringified." [m] (when (seq m) (->> m (mapcat (fn [[k v]] (cond (nil? v) [] (coll? v) (map (partial encode-param-pair k) v) :else [(encode-param-pair k v)]))) (interpose "&") (apply str)))) (defn assoc-query* "Add additional query parameters to a URI. Takes a URI (or coercible to URI) and a map of query params." [u m] (let [u (uri u)] (assoc u :query (map->query-string (merge (query-map u) m))))) (defn assoc-query "Add additional query parameters to a URI. Takes a URI (or coercible to URI) followed key value pairs. (assoc-query \"http://example.com?id=1&name=John\" :name \"Jack\" :style \"goth\") ;;=> #lambdaisland/uri \"http://example.com?id=1&name=Jack&style=goth\" " [u & {:as kvs}] (assoc-query* u kvs)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Predicates (defn relative? "Is the URI relative? Returns true if the URI does not have a scheme (protocol)." [uri] (nil? (:scheme uri))) (def ^{:doc "Is the URI absolute? Returns true if the URI has a scheme (protocol), and hence also an origin."} absolute? (complement relative?)) (defn uri? "Check if `o` is URI instance." [o] (instance? URI o)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; EDN (def edn-tag 'lambdaisland/uri) #?(:clj (defmethod print-method URI [^URI this ^java.io.Writer writer] (.write writer "#") (.write writer (str edn-tag)) (.write writer " ") (.write writer (prn-str (.toString this)))) :cljs (extend-type URI IPrintWithWriter (-pr-writer [this writer _opts] (write-all writer "#" (str edn-tag) " " (prn-str (.toString this)))))) (def ^{:doc "A map that can be passed to clojure.edn/read, so tagged URI literals are read back correctly."} edn-readers {edn-tag parse}) lambdaisland-uri-clojure-1.13.95/src/lambdaisland/uri/000077500000000000000000000000001417506613100225505ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/src/lambdaisland/uri/normalize.cljc000066400000000000000000000141121417506613100254040ustar00rootroot00000000000000(ns lambdaisland.uri.normalize (:require [clojure.string :as str] [lambdaisland.uri.platform :refer [byte-seq->string string->byte-seq byte->hex hex->byte char-code-at str-len]])) ;; TODO we might be better off having these just be sets (def ^{:doc "Which characters should be percent-encoded depends on which section of the URI is being normalized. This map contains regexes that for each case match the characters that need encoding."} character-classes (let [alpha "a-zA-Z" digit "0-9" gen-delims ":\\/\\?#\\[\\]@" sub-delims "!\\$&'\\(\\)\\*\\+,;=" reserved (str gen-delims sub-delims) unreserved (str alpha digit "\\-\\._~") pchar (str unreserved sub-delims ":@") scheme (str alpha digit "\\-\\+\\.") host (str unreserved sub-delims "\\[:\\]") authority pchar path (str pchar "\\/") query (str unreserved ":@\\/\\?") fragment (str pchar "\\/\\?")] {:alpha (re-pattern (str "[^" alpha "]")) :digit (re-pattern (str "[^" digit "]")) :gen-delims (re-pattern (str "[^" gen-delims "]")) :sub-delims (re-pattern (str "[^" sub-delims "]")) :reserved (re-pattern (str "[^" reserved "]")) :unreserved (re-pattern (str "[^" unreserved "]")) :pchar (re-pattern (str "[^" pchar "]")) :scheme (re-pattern (str "[^" scheme "]")) :host (re-pattern (str "[^" host "]")) :authority (re-pattern (str "[^" authority "]")) :path (re-pattern (str "[^" path "]")) :query (re-pattern (str "[^" query "]")) :fragment (re-pattern (str "[^" fragment "]"))})) (defn high-surrogate? [char-code] (<= 0xD800 char-code 0xDBFF)) (defn char-seq "Return a seq of the characters in a string, making sure not to split up UCS-2 (or is it UTF-16?) surrogate pairs. Because JavaScript. And Java." ([str] (char-seq str 0)) ([str offset] (loop [offset offset res []] (if (>= offset (str-len str)) res (let [code (char-code-at str offset) width (if (high-surrogate? code) 2 1) next-offset (+ offset width) cur-char (subs str offset next-offset)] (recur next-offset (conj res cur-char))))))) (defn percent-encode "Convert characters in their percent encoded form. e.g. `(percent_encode \"a\") #_=> \"%61\"`. When given a second argument, then only characters of the given character class are encoded, see `character-class`. Characters are encoded as UTF-8. To use a different encoding. re-bind `*character-encoding*`" ([component] (->> (string->byte-seq component) (map #(str "%" (byte->hex %))) (apply str))) ([component type] (let [char-class (get character-classes type) encode-char #(cond-> % (re-find char-class %) percent-encode)] (->> (char-seq component) (map encode-char) (apply str))))) (defn percent-decode "The inverse of `percent-encode`, convert any %XX sequences in a string to characters. Byte sequences are interpreted as UTF-8. To use a different encoding. re-bind `*character-encoding*`" [s] (when s (str/replace s #"(%[0-9A-Fa-f]{2})+" (fn [[x _]] (byte-seq->string (->> (str/split x #"%") (drop 1) (map hex->byte))))))) (defn normalize-path [path] (when-not (nil? path) (percent-encode (percent-decode path) :path))) (defn normalize-fragment [fragment] (when-not (nil? fragment) (percent-encode (percent-decode fragment) :fragment))) (defn hex-code-point? [cp] (or (<= #_(long \0) 48 cp #_(long \9) 57) (<= #_(long \A) 65 cp #_(long \F) 70) (<= #_(long \a) 97 cp #_(long \f) 102))) (def sub-delims "RFC3986 section 2.2 The purpose of reserved characters is to provide a set of delimiting characters that are distinguishable from other data within a URI. URIs that differ in the replacement of a reserved character with its corresponding percent-encoded octet are not equivalent. Percent- encoding a reserved character, or decoding a percent-encoded octet that corresponds to a reserved character, will change how the URI is interpreted by most applications. Thus, characters in the reserved set are protected from normalization and are therefore safe to be used by scheme-specific and producer-specific algorithms for delimiting data subcomponents within a URI. " #{"!" "$" "&" "'" "(" ")" "*" "+" "," ";" "="}) (defn normalize-query "Normalize the query section of a URI - sub-delimiters that are not percent encoded are left unencoded - sub-delimiters and other reserved characters are always percent encoded - non-reserved characters that are percent-encoded are decoded " [s] (when s (let [len (str-len s)] (loop [i 0 res []] (cond (= i len) (apply str res) (and (< i (- len 2)) (= 37 (char-code-at s i)) (hex-code-point? (char-code-at s (inc i))) (hex-code-point? (char-code-at s (+ i 2)))) (recur (+ i 3) (conj res (percent-encode (percent-decode (subs s i (+ i 3))) :query))) (contains? sub-delims (subs s i (inc i))) (recur (inc i) (conj res (subs s i (inc i)))) :else (let [increment (if (high-surrogate? (char-code-at s i)) 2 1)] (recur (+ i increment) (conj res (percent-encode (subs s i (+ i increment)) :query))))))))) (defn normalize "Normalize a lambdaisland.uri.URI. Currently normalizes (percent-encodes) the path, query, and fragment." [uri] (-> uri (update :path normalize-path) (update :query normalize-query) (update :fragment normalize-fragment))) lambdaisland-uri-clojure-1.13.95/src/lambdaisland/uri/platform.clj000066400000000000000000000006741417506613100250750ustar00rootroot00000000000000(ns lambdaisland.uri.platform) ;; clj (defn string->byte-seq [^String s] (map #(if (< % 0) (+ % 256) %) (.getBytes s "UTF8"))) (defn byte-seq->string [arr] (String. (byte-array arr) "UTF8")) (defn hex->byte [hex] (Integer/parseInt hex 16)) (defn byte->hex [byte] {:pre [(<= 0 byte 255)]} (format "%02X" byte)) (defn char-code-at [^String str pos] (long ^Character (.charAt str pos))) (defn str-len [^String s] (.length s)) lambdaisland-uri-clojure-1.13.95/src/lambdaisland/uri/platform.cljs000066400000000000000000000013111417506613100252450ustar00rootroot00000000000000(ns lambdaisland.uri.platform ;; cljs (:require [goog.crypt :as c])) (defn string->byte-seq [s] (c/stringToUtf8ByteArray s)) (defn byte-seq->string [arr] (c/utf8ByteArrayToString (apply array arr))) (defn hex->byte [hex] (js/parseInt hex 16)) (def hex-digit {0 "0" 1 "1" 2 "2" 3 "3" 4 "4" 5 "5" 6 "6" 7 "7" 8 "8" 9 "9" 10 "A" 11 "B" 12 "C" 13 "D" 14 "E" 15 "F"}) (defn byte->hex [byte] (let [byte (bit-and 0xFF byte) low-nibble (bit-and 0xF byte) high-nibble (bit-shift-right byte 4)] (str (hex-digit high-nibble) (hex-digit low-nibble)))) (defn char-code-at [str pos] (.charCodeAt str pos)) (defn str-len [s] (.-length s)) lambdaisland-uri-clojure-1.13.95/test/000077500000000000000000000000001417506613100175265ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/test/lambdaisland/000077500000000000000000000000001417506613100221415ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/test/lambdaisland/test_runner.cljs000066400000000000000000000003641417506613100253710ustar00rootroot00000000000000(ns lambdaisland.test-runner (:require [doo.runner :refer-macros [doo-tests]] [lambdaisland.uri-test] [lambdaisland.uri.normalize-test])) (doo-tests 'lambdaisland.uri-test 'lambdaisland.uri.normalize-test) lambdaisland-uri-clojure-1.13.95/test/lambdaisland/uri/000077500000000000000000000000001417506613100227405ustar00rootroot00000000000000lambdaisland-uri-clojure-1.13.95/test/lambdaisland/uri/normalize_test.cljc000066400000000000000000000037061417506613100266420ustar00rootroot00000000000000(ns lambdaisland.uri.normalize-test (:require [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as n] [clojure.test :refer [deftest testing is are]])) (deftest normalize-test (are [x y] (= (-> x uri/parse n/normalize str) y) "http://example.com/a b c" "http://example.com/a%20b%20c" "http://example.com/a%20b%20c" "http://example.com/a%20b%20c" "/𝍖" "/%F0%9D%8D%96" "http://foo.bar/?x=%20" "http://foo.bar/?x=%20" "https://example.com?text=You are welcome 🙂" "https://example.com?text=You%20are%20welcome%20%F0%9F%99%82" ) (are [x y] (= (-> x n/normalize str) y) (uri/map->URI {:query "x=y"}) "?x=y" (uri/map->URI {:query "x=?y#"}) "?x=?y%23" (uri/map->URI {:query "foo=bar"}) "?foo=bar" (uri/map->URI {:query "foo=b%61r"}) "?foo=bar" (uri/map->URI {:query "foo=bar%3Dbaz"}) "?foo=bar%3Dbaz" (uri/map->URI {:query "foo=%20%2B%26xxx%3D123"}) "?foo=%20%2B%26xxx%3D123" (uri/map->URI {:query "text=You are welcome 🙂"}) "?text=You%20are%20welcome%20%F0%9F%99%82" )) (deftest char-seq-test (let [long-string (->> "s" ;; Long enough to trigger StackOverflow in non-tail recursive cases. (repeat 5000) (apply str)) long-string-len (count long-string) cs (n/char-seq long-string)] (is (= long-string-len (count cs))) (is (every? #{"s"} cs)))) (deftest normalize-path-test (are [x y] (= (n/normalize-path x) y) "/abc" "/abc" "𝍖" "%F0%9D%8D%96")) (deftest percent-encode-test (are [class comp result] (= (n/percent-encode comp class) result) :alpha "abcAbc" "abcAbc" :alpha "abc123" "abc%31%32%33" :path "abc/123" "abc/123" :path "abc/123:/#" "abc/123:/%23" :path "𝍖" "%F0%9D%8D%96")) (deftest percent-decode-test (are [in out] (= (n/percent-decode in) out) "%61%62%63" "abc" "%F0%9F%99%88%F0%9F%99%89" "🙈🙉")) lambdaisland-uri-clojure-1.13.95/test/lambdaisland/uri_test.cljc000066400000000000000000000150141417506613100246350ustar00rootroot00000000000000(ns lambdaisland.uri-test (:require [clojure.test :as t :refer [are deftest is testing]] [lambdaisland.uri :as uri] [lambdaisland.uri.normalize :as norm] [lambdaisland.uri.platform :as platform] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] [clojure.test.check.clojure-test :as tc] [clojure.string :as str]) #?(:clj (:import lambdaisland.uri.URI))) (deftest parsing (testing "happy path" (are [x y] (= y (uri/parse x)) "http://user:password@example.com:8080/path?query=value#fragment" (uri/URI. "http" "user" "password" "example.com" "8080" "/path" "query=value" "fragment") "/happy/path" (uri/URI. nil nil nil nil nil "/happy/path" nil nil) "relative/path" (uri/URI. nil nil nil nil nil "relative/path" nil nil) "http://example.com" (uri/URI. "http" nil nil "example.com" nil nil nil nil) ))) (deftest joining (are [x y] (= (uri/parse y) (apply uri/join (map uri/parse x))) ["http://foo.bar" "https://baz.com"] "https://baz.com" ["http://example.com" "/a/path"] "http://example.com/a/path" ["http://example.com/foo/bar" "/a/path"] "http://example.com/a/path" ["http://example.com/foo/bar" "a/relative/path"] "http://example.com/foo/a/relative/path" ["http://example.com/foo/bar/" "a/relative/path"] "http://example.com/foo/bar/a/relative/path" ["/foo/bar/" "a/relative/path"] "/foo/bar/a/relative/path" ["http://example.com" "a/relative/path"] "http://example.com/a/relative/path" ["http://example.com/a/b/c/d/" "../../x/y"] "http://example.com/a/b/x/y") (testing "https://www.w3.org/2004/04/uri-rel-test.html" (are [x y] (= y (str (uri/join (uri/parse "http://a/b/c/d;p?q") (uri/parse x)))) "g" "http://a/b/c/g" "./g" "http://a/b/c/g" "g/" "http://a/b/c/g/" "/g" "http://a/g" "//g" "http://g" "?y" "http://a/b/c/d;p?y" "g?y" "http://a/b/c/g?y" "#s" "http://a/b/c/d;p?q#s" "g#s" "http://a/b/c/g#s" "g?y#s" "http://a/b/c/g?y#s" ";x" "http://a/b/c/;x" "g;x" "http://a/b/c/g;x" "g;x?y#s" "http://a/b/c/g;x?y#s" "" "http://a/b/c/d;p?q" "." "http://a/b/c/" "./" "http://a/b/c/" ".." "http://a/b/" "../" "http://a/b/" "../g" "http://a/b/g" "../.." "http://a/" "../../" "http://a/" "../../g" "http://a/g" "../../../g" "http://a/g" "../../../../g" "http://a/g" "/./g" "http://a/g" "/g" "http://a/g" "g." "http://a/b/c/g." ".g" "http://a/b/c/.g" "g.." "http://a/b/c/g.." "..g" "http://a/b/c/..g" "./../g" "http://a/b/g" "./g/" "http://a/b/c/g/" "g/h" "http://a/b/c/g/h" "h" "http://a/b/c/h" "g;x=1/./y" "http://a/b/c/g;x=1/y" "g;x=1/../y" "http://a/b/c/y" "g?y/./x" "http://a/b/c/g?y/./x" "g?y/../x" "http://a/b/c/g?y/../x" "g#s/./x" "http://a/b/c/g#s/./x" "g#s/../x" "http://a/b/c/g#s/../x" "http:g" "http:g")) (testing "coerces its arguments" (is (= (uri/join "http://x/y/z" "/a/b/c") (uri/parse "http://x/a/b/c"))) #?(:clj (is (= (uri/join (java.net.URI. "http://x/y/z") "/a/b/c") (uri/parse "http://x/a/b/c")))))) (deftest lambdaisland-uri-URI (let [example "http://usr:pwd@example.com:8080/path?query=value#fragment" parsed (uri/uri example)] (testing "it allows keyword based access" (is (= (:scheme parsed) "http")) (is (= (:user parsed) "usr")) (is (= (:password parsed) "pwd")) (is (= (:host parsed) "example.com")) (is (= (:port parsed) "8080")) (is (= (:path parsed) "/path")) (is (= (:query parsed) "query=value")) (is (= (:fragment parsed) "fragment"))) (testing "it allows map-style access" (is (= (parsed :scheme) "http")) (is (= (parsed :user) "usr")) (is (= (parsed :password) "pwd")) (is (= (parsed :host) "example.com")) (is (= (parsed :port) "8080")) (is (= (parsed :path) "/path")) (is (= (parsed :query) "query=value")) (is (= (parsed :fragment) "fragment"))) (testing "it converts correctly to string" (is (= (str parsed) example))))) (deftest lambdaisland-uri-relative? (are [x] (uri/relative? (uri/parse x)) "//example.com" "/some/path" "?only=a-query" "#only-a-fragment" "//example.com:8080/foo/bar?baz#baq") (are [x] (uri/absolute? (uri/parse x)) "http://example.com" "https://example.com:8080/foo/bar?baz#baq")) (deftest query-map-test (is (= {:foo "bar", :aaa "bbb"} (uri/query-map "http://example.com?foo=bar&aaa=bbb"))) (is (= {"foo" "bar", "aaa" "bbb"} (uri/query-map "http://example.com?foo=bar&aaa=bbb" {:keywordize? false}))) (is (= {:id ["1" "2"]} (uri/query-map "?id=1&id=2"))) (is (= {:id "2"} (uri/query-map "?id=1&id=2" {:multikeys :never}))) (is (= {:foo ["bar"], :id ["2"]} (uri/query-map "?foo=bar&id=2" {:multikeys :always}))) (is (= {:foo " +&xxx=123"} (uri/query-map "?foo=%20%2B%26xxx%3D123")))) (deftest assoc-query-test (is (= (uri/uri "http://example.com?foo=baq&aaa=bbb&hello=world") (uri/assoc-query "http://example.com?foo=bar&aaa=bbb" :foo "baq" :hello "world"))) (is (= (uri/uri "http://example.com?foo=baq&aaa=bbb&hello=world") (uri/assoc-query* "http://example.com?foo=bar&aaa=bbb" {:foo "baq" :hello "world"}))) (is (= (uri/uri "?id=1&id=2") (uri/assoc-query* "" (uri/query-map "?id=1&id=2")))) (is (= (uri/uri "?id=1") (uri/assoc-query "?id=1&name=jack" :name nil))) (is (= (uri/uri "?foo=+%2B%26%3D") (uri/assoc-query "" :foo " +&="))) (is (= "a=a+b&b=b+c" (-> "/foo" (uri/assoc-query* {:a "a b"}) (uri/assoc-query* {:b "b c"}) :query))) (is (= {:a "a b"} (-> "/foo" (uri/assoc-query* {:a "a b"}) uri/query-map)))) (deftest uri-predicate-test (is (true? (uri/uri? (uri/uri "/foo"))))) (def query-map-gen (gen/map (gen/such-that #(not= ":/" (str %)) gen/keyword) gen/string)) (tc/defspec query-string-round-trips 100 (prop/for-all [q query-map-gen] (let [res (-> q uri/map->query-string uri/query-string->map)] (or (and (empty? q) (empty? res)) ;; (= nil {}) (= q res))))) lambdaisland-uri-clojure-1.13.95/tests.edn000066400000000000000000000001301417506613100203730ustar00rootroot00000000000000#kaocha/v1 {:tests [{:id :clj} {:id :cljs :type :kaocha.type/cljs}]}