mirror of
https://github.com/correl/urilib.git
synced 2024-11-22 03:00:16 +00:00
Implement the initial API
This commit is contained in:
parent
66d066ed7e
commit
56c320572f
15 changed files with 591 additions and 415 deletions
10
.editorconfig
Normal file
10
.editorconfig
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*.{erl,hrl,src}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
54
.gitignore
vendored
54
.gitignore
vendored
|
@ -1,62 +1,12 @@
|
||||||
# Created by .ignore support plugin (hsz.mobi)
|
|
||||||
### Erlang template
|
|
||||||
.eunit
|
.eunit
|
||||||
deps
|
deps
|
||||||
*.o
|
*.o
|
||||||
*.beam
|
|
||||||
*.plt
|
*.plt
|
||||||
erl_crash.dump
|
erl_crash.dump
|
||||||
ebin
|
|
||||||
rel/example_project
|
|
||||||
.concrete/DEV_MODE
|
|
||||||
.rebar
|
.rebar
|
||||||
TEST*.xml
|
TEST*.xml
|
||||||
_build
|
_build
|
||||||
|
.idea
|
||||||
### JetBrains template
|
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
|
||||||
|
|
||||||
*.iml
|
|
||||||
|
|
||||||
## Directory-based project format:
|
|
||||||
.idea/
|
|
||||||
# if you remove the above rule, at least ignore the following:
|
|
||||||
|
|
||||||
# User-specific stuff:
|
|
||||||
# .idea/workspace.xml
|
|
||||||
# .idea/tasks.xml
|
|
||||||
# .idea/dictionaries
|
|
||||||
|
|
||||||
# Sensitive or high-churn files:
|
|
||||||
# .idea/dataSources.ids
|
|
||||||
# .idea/dataSources.xml
|
|
||||||
# .idea/sqlDataSources.xml
|
|
||||||
# .idea/dynamic.xml
|
|
||||||
# .idea/uiDesigner.xml
|
|
||||||
|
|
||||||
# Gradle:
|
|
||||||
# .idea/gradle.xml
|
|
||||||
# .idea/libraries
|
|
||||||
|
|
||||||
# Mongo Explorer plugin:
|
|
||||||
# .idea/mongoSettings.xml
|
|
||||||
|
|
||||||
## File-based project format:
|
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
rebar.lock
|
||||||
## Plugin-specific files:
|
|
||||||
|
|
||||||
# IntelliJ
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
|
||||||
.idea_modules/
|
|
||||||
|
|
||||||
# JIRA plugin
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
|
|
32
Makefile
32
Makefile
|
@ -1,32 +0,0 @@
|
||||||
PROJECT=urilib
|
|
||||||
REBAR=bin/rebar
|
|
||||||
|
|
||||||
all: get-deps compile
|
|
||||||
|
|
||||||
build-plt:
|
|
||||||
@dialyzer --build_plt --output_plt ~/.$(PROJECT).plt \
|
|
||||||
--apps kernel stdlib erts inets edoc
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@( $(REBAR) clean )
|
|
||||||
|
|
||||||
compile:
|
|
||||||
@( $(REBAR) compile )
|
|
||||||
|
|
||||||
dialyze:
|
|
||||||
@dialyzer ebin/*.beam --plt ~/.$(PROJECT).plt
|
|
||||||
|
|
||||||
doc:
|
|
||||||
@echo "Running rebar doc..."
|
|
||||||
@$(REBAR) skip_deps=true doc
|
|
||||||
|
|
||||||
eunit:
|
|
||||||
@echo "Running rebar eunit..."
|
|
||||||
@$(REBAR) skip_deps=true eunit
|
|
||||||
|
|
||||||
get-deps:
|
|
||||||
@( $(REBAR) get-deps )
|
|
||||||
|
|
||||||
test: all eunit
|
|
||||||
|
|
||||||
.PHONY: dialyze doc eunit
|
|
132
README.md
132
README.md
|
@ -9,137 +9,9 @@ Example Usage
|
||||||
```erlang
|
```erlang
|
||||||
-include_lib("urilib.h").
|
-include_lib("urilib.h").
|
||||||
|
|
||||||
URI = urilib:parse_uri("http://foo:bar@www.google.com/search?baz=qux#corgie"),
|
URI = urilib:parse("http://foo:bar@www.google.com/search?baz=qux#corgie"),
|
||||||
io:format("Parsed URI: ~p~n", [URI]).
|
io:format("Parsed URI: ~p~n", [URI]).
|
||||||
|
|
||||||
URL = urllib:build(#url{scheme=http, host="www.google.com", path="/search", query=[{"foo", "bar"}], fragment="baz"}),
|
URL = urllib:build({http, undefined, undefined, "www.google.com", undefined, "/search", [{"foo", "bar"}], "baz"}),
|
||||||
io:format("Built URL: ~s~n", [URL]).
|
io:format("Built URL: ~s~n", [URL]).
|
||||||
```
|
```
|
||||||
|
|
||||||
Records
|
|
||||||
-------
|
|
||||||
|
|
||||||
#### authority ####
|
|
||||||
```erlang
|
|
||||||
#{host :: string(), port :: integer()}).
|
|
||||||
```
|
|
||||||
|
|
||||||
#### userinfo ####
|
|
||||||
```erlang
|
|
||||||
#{username :: string(), password :: string()}).
|
|
||||||
```
|
|
||||||
|
|
||||||
#### uri ####
|
|
||||||
```erlang
|
|
||||||
#{scheme :: atom(),
|
|
||||||
userinfo :: #userinfo{},
|
|
||||||
authority :: #authority{},
|
|
||||||
path :: string(),
|
|
||||||
query :: list(),
|
|
||||||
fragment :: string()}).
|
|
||||||
```
|
|
||||||
|
|
||||||
#### url ####
|
|
||||||
```erlang
|
|
||||||
#{scheme :: atom(),
|
|
||||||
username :: string(),
|
|
||||||
password :: string(),
|
|
||||||
host :: string(),
|
|
||||||
port :: integer(),
|
|
||||||
path :: string(),
|
|
||||||
query :: list(),
|
|
||||||
fragment :: string()}).
|
|
||||||
```
|
|
||||||
|
|
||||||
API
|
|
||||||
---
|
|
||||||
|
|
||||||
<a name="build-1"></a>
|
|
||||||
|
|
||||||
### build/1 ###
|
|
||||||
```erlang
|
|
||||||
build(Uri::Value) -> URI
|
|
||||||
```
|
|
||||||
<ul class="definitions"><li><code>Value = #uri{} | #url{}</code></li><li><code>URI = string()</code></li></ul>
|
|
||||||
|
|
||||||
Returns a URI from the record passed in.
|
|
||||||
|
|
||||||
<a name="decode-1"></a>
|
|
||||||
|
|
||||||
### decode/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
decode(Value) -> DecodedValue
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>Value = string()</code></li><li><code>DecodeValue = string()</code></li></ul>
|
|
||||||
|
|
||||||
Decode a percent encoded string value.
|
|
||||||
|
|
||||||
<a name="decode_plus-1"></a>
|
|
||||||
|
|
||||||
### decode_plus/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
decode_plus(Value) -> DecodedValue
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>Value = string()</code></li><li><code>DecodeValue = string()</code></li></ul>
|
|
||||||
|
|
||||||
Decode a percent encoded string value that uses pluses for spaces.
|
|
||||||
|
|
||||||
Note: The use of plus for space is defined in RFC-1630 but does not appear
|
|
||||||
in RFC-3986.
|
|
||||||
|
|
||||||
<a name="encode-1"></a>
|
|
||||||
|
|
||||||
### encode/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
encode(Value) -> EncodedValue
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>Value = string()</code></li><li><code>EncodedValue = string()</code></li></ul>
|
|
||||||
|
|
||||||
Percent encode a string value.
|
|
||||||
|
|
||||||
<a name="encode_plus-1"></a>
|
|
||||||
|
|
||||||
### encode_plus/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
encode_plus(Value) -> EncodedValue
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>Value = string()</code></li><li><code>EncodedValue = string()</code></li></ul>
|
|
||||||
|
|
||||||
Percent encode a string value similar to encode/1, but encodes spaces with a
|
|
||||||
plus (+) instead of %20. This function can be used for encoding query arguments.
|
|
||||||
|
|
||||||
Note: The use of plus for space is defined in RFC-1630 but does not appear
|
|
||||||
in RFC-3986.
|
|
||||||
|
|
||||||
<a name="parse_uri-1"></a>
|
|
||||||
|
|
||||||
### parse_uri/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
parse_uri(URI) -> ParsedURI
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>URI = string()</code></li><li><code>ParsedURI = #uri{}</code></li></ul>
|
|
||||||
|
|
||||||
Parse a URI string returning the parsed data as a record
|
|
||||||
|
|
||||||
<a name="parse_url-1"></a>
|
|
||||||
|
|
||||||
### parse_url/1 ###
|
|
||||||
|
|
||||||
```erlang
|
|
||||||
parse_url(URL) -> ParsedURL
|
|
||||||
```
|
|
||||||
|
|
||||||
<ul class="definitions"><li><code>URI = string()</code></li><li><code>ParsedURL = #url{}</code></li></ul>
|
|
||||||
|
|
||||||
Parse a URL string returning the parsed data as a record
|
|
||||||
|
|
||||||
|
|
3
doc/edoc-info
Normal file
3
doc/edoc-info
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
%% encoding: UTF-8
|
||||||
|
{application,urilib}.
|
||||||
|
{modules,[urilib]}.
|
BIN
doc/erlang.png
Normal file
BIN
doc/erlang.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
17
doc/index.html
Normal file
17
doc/index.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>urllib</title>
|
||||||
|
</head>
|
||||||
|
<frameset cols="20%,80%">
|
||||||
|
<frame src="modules-frame.html" name="modulesFrame" title="">
|
||||||
|
|
||||||
|
<frame src="overview-summary.html" name="overviewFrame" title="">
|
||||||
|
<noframes>
|
||||||
|
<h2>This page uses frames</h2>
|
||||||
|
<p>Your browser does not accept frames.
|
||||||
|
<br>You should go to the <a href="overview-summary.html">non-frame version</a> instead.
|
||||||
|
</p>
|
||||||
|
</noframes>
|
||||||
|
</frameset>
|
||||||
|
</html>
|
12
doc/modules-frame.html
Normal file
12
doc/modules-frame.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>urllib</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
<h2 class="indextitle">Modules</h2>
|
||||||
|
<table width="100%" border="0" summary="list of modules">
|
||||||
|
<tr><td><a href="urilib.html" target="overviewFrame" class="module">urilib</a></td></tr></table>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
doc/overview-summary.html
Normal file
16
doc/overview-summary.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>urllib</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||||
|
<h1>urllib</h1>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||||
|
<p><i>Generated by EDoc, Jan 21 2016, 22:59:50.</i></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
doc/stylesheet.css
Normal file
55
doc/stylesheet.css
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/* standard EDoc style sheet */
|
||||||
|
body {
|
||||||
|
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
margin-left: .25in;
|
||||||
|
margin-right: .2in;
|
||||||
|
margin-top: 0.2in;
|
||||||
|
margin-bottom: 0.2in;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
h1,h2 {
|
||||||
|
margin-left: -0.2in;
|
||||||
|
}
|
||||||
|
div.navbar {
|
||||||
|
background-color: #add8e6;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
h2.indextitle {
|
||||||
|
padding: 0.4em;
|
||||||
|
background-color: #add8e6;
|
||||||
|
}
|
||||||
|
h3.function,h3.typedecl {
|
||||||
|
background-color: #add8e6;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
div.spec {
|
||||||
|
margin-left: 2em;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
a.module {
|
||||||
|
text-decoration:none
|
||||||
|
}
|
||||||
|
a.module:hover {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
ul.definitions {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
ul.index {
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minor style tweaks
|
||||||
|
*/
|
||||||
|
ul {
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 3
|
||||||
|
}
|
135
doc/urilib.html
Normal file
135
doc/urilib.html
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>Module urilib</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
|
||||||
|
</head>
|
||||||
|
<body bgcolor="white">
|
||||||
|
<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h1>Module urilib</h1>
|
||||||
|
<ul class="index"><li><a href="#description">Description</a></li><li><a href="#types">Data Types</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>urilib is a RFC-3986 URI Library for Erlang.
|
||||||
|
<p>Copyright © 2016</p>
|
||||||
|
|
||||||
|
<p><b>Authors:</b> Gavin M. Roy (<a href="mailto:gavinmroy@gmail.com"><tt>gavinmroy@gmail.com</tt></a>).</p>
|
||||||
|
|
||||||
|
<h2><a name="description">Description</a></h2>urilib is a RFC-3986 URI Library for Erlang
|
||||||
|
<h2><a name="types">Data Types</a></h2>
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-authority">authority()</a></h3>
|
||||||
|
<p><pre>authority() = {<a href="#type-userinfo">userinfo()</a>, <a href="#type-host">host()</a>, <a href="#type-tcp_port">tcp_port()</a>}</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-fragment">fragment()</a></h3>
|
||||||
|
<p><pre>fragment() = string() | undefined</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-host">host()</a></h3>
|
||||||
|
<p><pre>host() = string()</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-password">password()</a></h3>
|
||||||
|
<p><pre>password() = string() | undefined</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-path">path()</a></h3>
|
||||||
|
<p><pre>path() = string()</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-query">query()</a></h3>
|
||||||
|
<p><pre>query() = [tuple() | string()] | undefined</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-scheme">scheme()</a></h3>
|
||||||
|
<p><pre>scheme() = http | https | atom()</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-tcp_port">tcp_port()</a></h3>
|
||||||
|
<p><pre>tcp_port() = integer()</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-uri">uri()</a></h3>
|
||||||
|
<p><pre>uri() = {<a href="#type-scheme">scheme()</a>, <a href="#type-authority">authority()</a>, <a href="#type-path">path()</a>, <a href="#type-query">query()</a>, <a href="#type-fragment">fragment()</a>}</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-url">url()</a></h3>
|
||||||
|
<p><pre>url() =
|
||||||
|
{<a href="#type-scheme">scheme()</a>,
|
||||||
|
<a href="#type-username">username()</a>,
|
||||||
|
<a href="#type-password">password()</a>,
|
||||||
|
<a href="#type-host">host()</a>,
|
||||||
|
<a href="#type-tcp_port">tcp_port()</a>,
|
||||||
|
<a href="#type-path">path()</a>,
|
||||||
|
<a href="#type-query">query()</a>,
|
||||||
|
<a href="#type-fragment">fragment()</a>}</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-userinfo">userinfo()</a></h3>
|
||||||
|
<p><pre>userinfo() = {<a href="#type-username">username()</a>, <a href="#type-password">password()</a>} | undefined</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="typedecl"><a name="type-username">username()</a></h3>
|
||||||
|
<p><pre>username() = string() | undefined</pre></p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2><a name="index">Function Index</a></h2>
|
||||||
|
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#build-1">build/1</a></td><td>Build a URI.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#parse-1">parse/1</a></td><td>Parse a URI.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#parse-2">parse/2</a></td><td>Parse a URI, returning the result as either a <code><a href="#type-uri">uri()</a></code> or <code><a href="#type-url">url()</a></code>.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#percent_decode-1">percent_decode/1</a></td><td>Decode a percent encoded string value.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#percent_encode-1">percent_encode/1</a></td><td>Percent encode a string value.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#plus_decode-1">plus_decode/1</a></td><td>Decode a percent encoded string value that uses pluses for spaces.</td></tr>
|
||||||
|
<tr><td valign="top"><a href="#plus_encode-1">plus_encode/1</a></td><td>Percent encode a string value similar to encode/1, but encodes spaces with a
|
||||||
|
plus (<code>+</code>) instead of <code>%20</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2><a name="functions">Function Details</a></h2>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="build-1">build/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>build(Value :: <a href="#type-uri">uri()</a> | <a href="#type-url">url()</a>) -> string()</pre></p>
|
||||||
|
</div><p>Build a URI</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="parse-1">parse/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>parse(Value :: string()) -> <a href="#type-uri">uri()</a></pre></p>
|
||||||
|
</div><p>Parse a URI</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="parse-2">parse/2</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>parse(Value :: string(), Return :: uri | url) -> <a href="#type-uri">uri()</a></pre></p>
|
||||||
|
</div><p>Parse a URI, returning the result as either a <code><a href="#type-uri">uri()</a></code> or <code><a href="#type-url">url()</a></code>.</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="percent_decode-1">percent_decode/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>percent_decode(Value :: string()) -> string()</pre></p>
|
||||||
|
</div><p>Decode a percent encoded string value.</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="percent_encode-1">percent_encode/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>percent_encode(Value :: string()) -> string()</pre></p>
|
||||||
|
</div><p>Percent encode a string value.</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="plus_decode-1">plus_decode/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>plus_decode(Value :: string()) -> string()</pre></p>
|
||||||
|
</div><p><p>Decode a percent encoded string value that uses pluses for spaces.</p>
|
||||||
|
|
||||||
|
Note: The use of plus for space is defined in RFC-1630 but does not appear
|
||||||
|
in RFC-3986.</p>
|
||||||
|
|
||||||
|
<h3 class="function"><a name="plus_encode-1">plus_encode/1</a></h3>
|
||||||
|
<div class="spec">
|
||||||
|
<p><pre>plus_encode(Value :: string()) -> string()</pre></p>
|
||||||
|
</div><p><p>Percent encode a string value similar to encode/1, but encodes spaces with a
|
||||||
|
plus (<code>+</code>) instead of <code>%20</code>. This function can be used for encoding query arguments.</p>
|
||||||
|
|
||||||
|
Note: The use of plus for space is defined in RFC-1630 but does not appear in RFC-3986.</p>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
|
||||||
|
<p><i>Generated by EDoc, Jan 21 2016, 22:59:50.</i></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,6 +1,7 @@
|
||||||
%% =============================================================================
|
%% =============================================================================
|
||||||
%% @author Gavin M. Roy <gavinmroy@gmail.com>
|
%% @author Gavin M. Roy <gavinmroy@gmail.com>
|
||||||
%% @copyright 2016
|
%% @copyright 2016
|
||||||
|
%% @doc urilib is a RFC-3986 URI Library for Erlang
|
||||||
%% @end
|
%% @end
|
||||||
%% =============================================================================
|
%% =============================================================================
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
{clean_files, ["*.eunit", "ebin/*.beam"]}.
|
{clean_files, ["*.eunit", "ebin/*.beam"]}.
|
||||||
{dialyzer, [{plt_extra_apps, [kernel, stdlib, erts, inets, edoc]}]}.
|
{dialyzer, [{plt_extra_apps, [kernel, stdlib, erts, inets, edoc]}]}.
|
||||||
{erl_opts, [fail_on_warning]}.
|
{erl_opts, [fail_on_warning]}.
|
||||||
|
{edoc_opts, [{includes, "include"}, {title, "urllib"}, {pretty_printer, erl_pp}]}.
|
||||||
{eunit_exclude_deps, true}.
|
{eunit_exclude_deps, true}.
|
||||||
{eunit_opts, [verbose, {skip_deps, true}]}.
|
{eunit_opts, [verbose, {skip_deps, true}]}.
|
||||||
|
{lib_dirs,["deps"]}.
|
||||||
{minimum_otp_vsn, "17.5"}.
|
{minimum_otp_vsn, "17.5"}.
|
||||||
|
|
325
src/urilib.erl
325
src/urilib.erl
|
@ -7,12 +7,25 @@
|
||||||
-module(urilib).
|
-module(urilib).
|
||||||
|
|
||||||
-export([build/1,
|
-export([build/1,
|
||||||
parse_uri/1,
|
parse/1,
|
||||||
parse_url/1,
|
parse/2,
|
||||||
encode/1,
|
percent_decode/1,
|
||||||
encode_plus/1,
|
percent_encode/1,
|
||||||
decode/1,
|
plus_decode/1,
|
||||||
decode_plus/1]).
|
plus_encode/1]).
|
||||||
|
|
||||||
|
-export_type([scheme/0,
|
||||||
|
host/0,
|
||||||
|
tcp_port/0,
|
||||||
|
username/0,
|
||||||
|
password/0,
|
||||||
|
userinfo/0,
|
||||||
|
authority/0,
|
||||||
|
path/0,
|
||||||
|
query/0,
|
||||||
|
fragment/0,
|
||||||
|
uri/0,
|
||||||
|
url/0]).
|
||||||
|
|
||||||
-include("urilib.hrl").
|
-include("urilib.hrl").
|
||||||
|
|
||||||
|
@ -21,189 +34,253 @@
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-spec build(#uri{} | #url{}) -> string().
|
-type scheme() :: http | https | atom().
|
||||||
%% @spec build(Value) -> URI
|
-type host() :: string().
|
||||||
%% where
|
-type tcp_port() :: integer().
|
||||||
%% Value = #uri{} | #url{}
|
-type username() :: string() | undefined.
|
||||||
%% URI = string()
|
-type password() :: string() | undefined.
|
||||||
%% @doc Returns a URI from the record passed in.
|
-type userinfo() :: {username(), password()} | undefined.
|
||||||
%%
|
-type authority() :: {userinfo(), host(), tcp_port()}.
|
||||||
%% @end
|
-type path() :: string().
|
||||||
build(#uri{scheme=Scheme, userinfo=UserInfo, authority=Authority,
|
-type query() :: [tuple() | string()] | undefined.
|
||||||
path=Path, query=QArgs, fragment=Fragment}) ->
|
-type fragment() :: string() | undefined.
|
||||||
U1 = url_add_scheme(Scheme),
|
-type uri() :: {scheme(), authority(), path(), query(), fragment()}.
|
||||||
U2 = url_maybe_add_user(UserInfo, U1),
|
-type url() :: {scheme(), username(), password(), host(), tcp_port(), path(), query(), fragment()}.
|
||||||
U3 = url_add_host_and_port(Scheme,
|
|
||||||
Authority#authority.host,
|
|
||||||
Authority#authority.port, U2),
|
|
||||||
U4 = url_add_path(Path, U3),
|
|
||||||
U5 = url_maybe_add_qargs(QArgs, U4),
|
|
||||||
url_maybe_add_fragment(Fragment, U5).
|
|
||||||
|
|
||||||
|
|
||||||
-spec parse_uri(string()) -> #uri{}.
|
-spec build(Value :: uri() | url()) -> string().
|
||||||
%% @spec parse_uri(URI) -> ParsedURI
|
%% @doc Build a URI
|
||||||
%% where
|
|
||||||
%% URI = string()
|
|
||||||
%% ParsedURI = #uri{}
|
|
||||||
%% @doc Parse a URI string returning the parsed data as a record
|
|
||||||
%% @end
|
%% @end
|
||||||
parse_uri(URI) ->
|
build({Scheme, {undefined, Host, Port}, Path, Query, Fragment}) ->
|
||||||
case http_uri:parse(URI, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
|
build({Scheme, {{undefined, undefined}, Host, Port}, Path, Query, Fragment});
|
||||||
|
|
||||||
|
build({Scheme, {{Username, Password}, Host, Port}, Path, Query, Fragment}) ->
|
||||||
|
U1 = url_add_scheme(Scheme),
|
||||||
|
U2 = url_maybe_add_userinfo(Username, Password, U1),
|
||||||
|
U3 = url_add_host_and_port(Scheme, Host, Port, U2),
|
||||||
|
U4 = url_add_path(Path, U3),
|
||||||
|
U5 = url_maybe_add_qargs(Query, U4),
|
||||||
|
url_maybe_add_fragment(Fragment, U5);
|
||||||
|
|
||||||
|
build({Scheme, undefined, Host, Port, Path, Query, Fragment}) ->
|
||||||
|
build({Scheme, undefined, undefined, Host, Port, Path, Query, Fragment});
|
||||||
|
|
||||||
|
build({Scheme, Username, Password, Host, Port, Path, Query, Fragment}) ->
|
||||||
|
U1 = url_add_scheme(Scheme),
|
||||||
|
U2 = url_maybe_add_userinfo(Username, Password, U1),
|
||||||
|
U3 = url_add_host_and_port(Scheme, Host, Port, U2),
|
||||||
|
U4 = url_add_path(Path, U3),
|
||||||
|
U5 = url_maybe_add_qargs(Query, U4),
|
||||||
|
url_maybe_add_fragment(Fragment, U5).
|
||||||
|
|
||||||
|
|
||||||
|
-spec parse(string()) -> uri().
|
||||||
|
%% @doc Parse a URI
|
||||||
|
%% @end
|
||||||
|
parse(Value) ->
|
||||||
|
case http_uri:parse(Value, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
|
||||||
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
|
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
|
||||||
#uri{scheme=Scheme,
|
{Scheme, {parse_userinfo(UserInfo), Host, Port}, Path, parse_query(Query), parse_fragment(Fragment)};
|
||||||
userinfo=parse_userinfo(UserInfo),
|
{error, Reason} -> {error, Reason}
|
||||||
authority=#authority{host=Host,
|
|
||||||
port=Port},
|
|
||||||
path=Path,
|
|
||||||
query=parse_query(Query),
|
|
||||||
fragment=Fragment};
|
|
||||||
{error, Reason} -> {error, Reason}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec parse_url(string()) -> #url{}.
|
-spec parse(string(), Return :: uri | url) -> uri().
|
||||||
%% @spec parse_url(URL) -> ParsedURL
|
%% @doc Parse a URI, returning the result as either a {@type uri()} or {@type url()}.
|
||||||
%% where
|
|
||||||
%% URI = string()
|
|
||||||
%% ParsedURL = #url{}
|
|
||||||
%% @doc Parse a URL string returning the parsed data as a record
|
|
||||||
%% @end
|
%% @end
|
||||||
parse_url(URL) ->
|
parse(Value, uri) ->
|
||||||
case http_uri:parse(URL, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
|
parse(Value);
|
||||||
|
|
||||||
|
parse(Value, url) ->
|
||||||
|
case http_uri:parse(Value, [{scheme_defaults, http_uri:scheme_defaults()}, {fragment, true}]) of
|
||||||
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
|
{ok, {Scheme, UserInfo, Host, Port, Path, Query, Fragment}} ->
|
||||||
User = parse_userinfo(UserInfo),
|
{Username, Password} = parse_userinfo(UserInfo),
|
||||||
#url{scheme=Scheme,
|
{Scheme, Username, Password, Host, Port, Path, parse_query(Query), parse_fragment(Fragment)};
|
||||||
username=User#userinfo.username,
|
{error, Reason} -> {error, Reason}
|
||||||
password=User#userinfo.password,
|
|
||||||
host=Host,
|
|
||||||
port=Port,
|
|
||||||
path=Path,
|
|
||||||
query=parse_query(Query),
|
|
||||||
fragment=Fragment};
|
|
||||||
{error, Reason} -> {error, Reason}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
-spec encode(string()) -> string().
|
-spec percent_encode(string()) -> string().
|
||||||
%% @spec encode(Value) -> EncodedValue
|
|
||||||
%% where
|
|
||||||
%% Value = string()
|
|
||||||
%% EncodedValue = string()
|
|
||||||
%% @doc Percent encode a string value.
|
%% @doc Percent encode a string value.
|
||||||
%% @end
|
%% @end
|
||||||
encode(Value) ->
|
percent_encode(Value) ->
|
||||||
edoc_lib:escape_uri(Value).
|
edoc_lib:escape_uri(Value).
|
||||||
|
|
||||||
|
|
||||||
-spec encode_plus(string()) -> string().
|
-spec percent_decode(string()) -> string().
|
||||||
%% @spec encode_plus(Value) -> EncodedValue
|
|
||||||
%% where
|
|
||||||
%% Value = string()
|
|
||||||
%% EncodedValue = string()
|
|
||||||
%% @doc Percent encode a string value similar to encode/1, but encodes spaces with a
|
|
||||||
%% plus (+) instead of %20. This function can be used for encoding query arguments.
|
|
||||||
%%
|
|
||||||
%% Note: The use of plus for space is defined in RFC-1630 but does not appear
|
|
||||||
%% in RFC-3986.
|
|
||||||
%% @end
|
|
||||||
encode_plus(Value) ->
|
|
||||||
string:join([edoc_lib:escape_uri(V) || V <- string:tokens(Value, " ")], "+").
|
|
||||||
|
|
||||||
|
|
||||||
%% @spec decode(Value) -> DecodedValue
|
|
||||||
%% where
|
|
||||||
%% Value = string()
|
|
||||||
%% DecodeValue = string()
|
|
||||||
%% @doc Decode a percent encoded string value.
|
%% @doc Decode a percent encoded string value.
|
||||||
%% @end
|
%% @end
|
||||||
-spec decode(string()) -> string().
|
percent_decode(Value) ->
|
||||||
decode(Value) ->
|
|
||||||
http_uri:decode(Value).
|
http_uri:decode(Value).
|
||||||
|
|
||||||
|
|
||||||
-spec decode_plus(string()) -> string().
|
-spec plus_encode(string()) -> string().
|
||||||
%% @spec decode_plus(Value) -> DecodedValue
|
%% @doc Percent encode a string value similar to encode/1, but encodes spaces with a
|
||||||
%% where
|
%% plus (`+') instead of `%20'. This function can be used for encoding query arguments.
|
||||||
%% Value = string()
|
%%
|
||||||
%% DecodeValue = string()
|
%% Note: The use of plus for space is defined in RFC-1630 but does not appear in RFC-3986.
|
||||||
|
%% @end
|
||||||
|
plus_encode(Value) ->
|
||||||
|
string:join([edoc_lib:escape_uri(V) || V <- string:tokens(Value, " ")], "+").
|
||||||
|
|
||||||
|
|
||||||
|
-spec plus_decode(string()) -> string().
|
||||||
%% @doc Decode a percent encoded string value that uses pluses for spaces.
|
%% @doc Decode a percent encoded string value that uses pluses for spaces.
|
||||||
%%
|
%%
|
||||||
%% Note: The use of plus for space is defined in RFC-1630 but does not appear
|
%% Note: The use of plus for space is defined in RFC-1630 but does not appear
|
||||||
%% in RFC-3986.
|
%% in RFC-3986.
|
||||||
%% @end
|
%% @end
|
||||||
decode_plus(Value) ->
|
plus_decode(Value) ->
|
||||||
string:join([http_uri:decode(V) || V <- string:tokens(Value, "+")], " ").
|
string:join([http_uri:decode(V) || V <- string:tokens(Value, "+")], " ").
|
||||||
|
|
||||||
|
|
||||||
-spec parse_query(string()) -> [].
|
%% Private Functions
|
||||||
|
|
||||||
|
-spec parse_fragment(string()) -> string() | undefined.
|
||||||
|
%% @private
|
||||||
|
parse_fragment([]) ->
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
parse_fragment(Value) ->
|
||||||
|
Value.
|
||||||
|
|
||||||
|
|
||||||
|
-spec parse_query(string()) -> [tuple() | string()] | undefined.
|
||||||
%% @private
|
%% @private
|
||||||
parse_query([]) -> [];
|
|
||||||
parse_query(Query) ->
|
parse_query(Query) ->
|
||||||
case re:split(Query, "[&|?]", [{return, list}]) of
|
QArgs = re:split(Query, "[&|?]", [{return, list}]),
|
||||||
[""] -> [];
|
parse_query_result([split_query_arg(Arg) || Arg <- QArgs, Arg =/= []]).
|
||||||
QArgs -> [split_query_arg(Arg) || Arg <- QArgs, Arg =/= []]
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec parse_userinfo(string()) -> #userinfo{}.
|
-spec parse_query_result(string()) -> [tuple() | string()] | undefined.
|
||||||
|
%% @private
|
||||||
|
parse_query_result([]) ->
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
parse_query_result(QArgs) ->
|
||||||
|
QArgs.
|
||||||
|
|
||||||
|
|
||||||
|
-spec parse_userinfo(string()) -> userinfo().
|
||||||
%% @private
|
%% @private
|
||||||
parse_userinfo([]) -> #userinfo{};
|
|
||||||
parse_userinfo(Value) ->
|
parse_userinfo(Value) ->
|
||||||
case string:tokens(Value, ":") of
|
parse_userinfo_result(string:tokens(Value, ":")).
|
||||||
[User, Password] -> #userinfo{username=User, password=Password};
|
|
||||||
[User] -> #userinfo{username=User}
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec split_query_arg(string()) -> {string(), string()}.
|
-spec parse_userinfo_result(list()) -> userinfo().
|
||||||
|
%% @private
|
||||||
|
parse_userinfo_result([User, Password]) ->
|
||||||
|
{User, Password};
|
||||||
|
|
||||||
|
parse_userinfo_result([User]) ->
|
||||||
|
{User, undefined};
|
||||||
|
|
||||||
|
parse_userinfo_result([]) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
|
||||||
|
-spec split_query_arg(string()) -> {string(), string()} | undefined.
|
||||||
%% @private
|
%% @private
|
||||||
split_query_arg(Argument) ->
|
split_query_arg(Argument) ->
|
||||||
[K, V] = string:tokens(Argument, "="),
|
case string:tokens(Argument, "=") of
|
||||||
{K, V}.
|
[K, V] -> {plus_decode(K), plus_decode(V)};
|
||||||
|
[Value] -> plus_decode(Value)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_add_scheme(atom()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
|
url_add_scheme(undefined) ->
|
||||||
|
"http://";
|
||||||
|
|
||||||
url_add_scheme(Scheme) ->
|
url_add_scheme(Scheme) ->
|
||||||
string:concat(atom_to_list(Scheme), "://").
|
string:concat(atom_to_list(Scheme), "://").
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_maybe_add_userinfo(username(), password(), string()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
url_maybe_add_user([], URL) -> URL;
|
url_maybe_add_userinfo([], [], URL) ->
|
||||||
url_maybe_add_user(User, URL) ->
|
URL;
|
||||||
string:concat(URL, string:concat(User, "@")).
|
|
||||||
|
url_maybe_add_userinfo(undefined, undefined, URL) ->
|
||||||
|
URL;
|
||||||
|
|
||||||
|
url_maybe_add_userinfo(Username, [], URL) ->
|
||||||
|
url_maybe_add_userinfo(Username, undefined, URL);
|
||||||
|
|
||||||
|
url_maybe_add_userinfo(Username, undefined, URL) ->
|
||||||
|
string:concat(URL, string:concat(Username, "@"));
|
||||||
|
|
||||||
|
url_maybe_add_userinfo(Username, Password, URL) ->
|
||||||
|
string:concat(URL, string:concat(string:join([Username, Password], ":"), "@")).
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_add_host_and_port(scheme(), host(), tcp_port(), string()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
|
url_add_host_and_port(undefined, Host, undefined, URL) ->
|
||||||
|
string:concat(URL, Host);
|
||||||
|
|
||||||
|
url_add_host_and_port(http, Host, undefined, URL) ->
|
||||||
|
string:concat(URL, Host);
|
||||||
|
|
||||||
url_add_host_and_port(http, Host, 80, URL) ->
|
url_add_host_and_port(http, Host, 80, URL) ->
|
||||||
string:concat(URL, Host);
|
string:concat(URL, Host);
|
||||||
|
|
||||||
|
url_add_host_and_port(https, Host, undefined, URL) ->
|
||||||
|
string:concat(URL, Host);
|
||||||
|
|
||||||
%% @private
|
|
||||||
url_add_host_and_port(https, Host, 443, URL) ->
|
url_add_host_and_port(https, Host, 443, URL) ->
|
||||||
string:concat(URL, Host);
|
string:concat(URL, Host);
|
||||||
|
|
||||||
url_add_host_and_port(_, Host, Port, URL) ->
|
url_add_host_and_port(_, Host, Port, URL) ->
|
||||||
string:concat(URL, string:join([Host, integer_to_list(Port)], ":")).
|
string:concat(URL, string:join([Host, integer_to_list(Port)], ":")).
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_add_path(path(), string()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
|
url_add_path(undefined, URL) ->
|
||||||
|
string:concat(URL, "/");
|
||||||
|
|
||||||
url_add_path(Path, URL) ->
|
url_add_path(Path, URL) ->
|
||||||
Escaped = string:join([edoc_lib:escape_uri(P) || P <- string:tokens(Path, "/")], "/"),
|
Escaped = string:join([url_escape_path_segment(P) || P <- string:tokens(Path, "/")], "/"),
|
||||||
string:join([URL, Escaped], "/").
|
string:join([URL, Escaped], "/").
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_escape_path_segment(string()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
url_maybe_add_qargs([], URL) -> URL;
|
url_escape_path_segment(Value) ->
|
||||||
|
edoc_lib:escape_uri(http_uri:decode(Value)).
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_maybe_add_qargs(query(), string()) -> string().
|
||||||
|
%% @private
|
||||||
|
url_maybe_add_qargs(undefined, URL) ->
|
||||||
|
URL;
|
||||||
|
|
||||||
|
url_maybe_add_qargs([], URL) ->
|
||||||
|
URL;
|
||||||
|
|
||||||
url_maybe_add_qargs(QArgs, URL) ->
|
url_maybe_add_qargs(QArgs, URL) ->
|
||||||
QStr = string:join([string:join([encode_plus(K), encode_plus(V)], "=") || {K,V} <- QArgs], "&"),
|
QStr = string:join([url_maybe_encode_query_arg(Arg) || Arg <- QArgs], "&"),
|
||||||
string:join([URL, QStr], "?").
|
string:join([URL, QStr], "?").
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_maybe_encode_query_arg(tuple() | string()) -> string().
|
||||||
%% @private
|
%% @private
|
||||||
|
url_maybe_encode_query_arg({K, V}) ->
|
||||||
|
string:join([plus_encode(K), plus_encode(V)], "=");
|
||||||
|
|
||||||
|
url_maybe_encode_query_arg(V) ->
|
||||||
|
plus_encode(V).
|
||||||
|
|
||||||
|
|
||||||
|
-spec url_maybe_add_fragment(fragment(), string()) -> string().
|
||||||
|
%% @private
|
||||||
|
url_maybe_add_fragment(undefined, URL) -> URL;
|
||||||
url_maybe_add_fragment([], URL) -> URL;
|
url_maybe_add_fragment([], URL) -> URL;
|
||||||
url_maybe_add_fragment(Value, URL) ->
|
url_maybe_add_fragment(Value, URL) ->
|
||||||
Fragment = case string:left(Value, 1) of
|
Fragment = case string:left(Value, 1) of
|
||||||
"#" -> edoc_lib:escape_uri(string:sub_string(Value, 2));
|
"#" -> edoc_lib:escape_uri(string:sub_string(Value, 2));
|
||||||
_ -> edoc_lib:escape_uri(Value)
|
_ -> edoc_lib:escape_uri(Value)
|
||||||
end,
|
end,
|
||||||
string:join([URL, Fragment], "#").
|
string:join([URL, Fragment], "#").
|
||||||
|
|
|
@ -4,94 +4,152 @@
|
||||||
|
|
||||||
-include("urilib.hrl").
|
-include("urilib.hrl").
|
||||||
|
|
||||||
decode_test() ->
|
build_variation1_test() ->
|
||||||
Value = "foo%2fbar%20baz",
|
Params = {amqp, {{"guest", "password"}, "rabbitmq", 5672}, "/%2f", [{"heartbeat", "5"}], undefined},
|
||||||
Expect = "foo/bar baz",
|
Expect = "amqp://guest:password@rabbitmq:5672/%2f?heartbeat=5",
|
||||||
?assertEqual(Expect, urilib:decode(Value)).
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
decode_plus_test() ->
|
build_variation2_test() ->
|
||||||
Value = "foo/bar+baz",
|
Params = {http, {undefined, "www.google.com", 80}, "/search", [{"foo", "bar"}], "#baz"},
|
||||||
Expect = "foo/bar baz",
|
Expect = "http://www.google.com/search?foo=bar#baz",
|
||||||
?assertEqual(Expect, urilib:decode_plus(Value)).
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
encode1_test() ->
|
build_variation3_test() ->
|
||||||
Value = "foo/bar baz",
|
Params = {https, {undefined, "www.google.com", 443}, "/search", undefined, undefined},
|
||||||
Expect = "foo%2fbar%20baz",
|
Expect = "https://www.google.com/search",
|
||||||
?assertEqual(Expect, urilib:encode(Value)).
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
encode1_unicode_test() ->
|
build_variation4_test() ->
|
||||||
Value = "foo/bar✈baz",
|
Params = {https, {undefined, "www.google.com", 443}, "/search", ["foo"], undefined},
|
||||||
Expect = "foo%2fbar%c0%88baz",
|
Expect = "https://www.google.com/search?foo",
|
||||||
?assertEqual(Expect, urilib:encode(Value)).
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
encode_plus1_test() ->
|
build_variation5_test() ->
|
||||||
Value = "foo/bar baz",
|
Params = {https, {undefined, "www.google.com", undefined}, "/search", ["foo"], undefined},
|
||||||
Expect = "foo%2fbar+baz",
|
Expect = "https://www.google.com/search?foo",
|
||||||
?assertEqual(Expect, urilib:encode_plus(Value)).
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
parse_uri_variation1_test() ->
|
build_variation6_test() ->
|
||||||
URI = "amqp://guest:rabbitmq@rabbitmq:5672/%2f?heartbeat=5",
|
Params = {http, {undefined, "www.google.com", undefined}, "/search", ["foo"], undefined},
|
||||||
Expect = #uri{scheme=amqp,
|
Expect = "http://www.google.com/search?foo",
|
||||||
userinfo=#userinfo{username="guest",
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
password="rabbitmq"},
|
|
||||||
authority=#authority{host="rabbitmq", port=5672},
|
|
||||||
path="/%2f",
|
|
||||||
query=[{"heartbeat", "5"}],
|
|
||||||
fragment=[]},
|
|
||||||
?assertEqual(Expect, urilib:parse_uri(URI)).
|
|
||||||
|
|
||||||
parse_uri_variation2_test() ->
|
build_variation7_test() ->
|
||||||
|
Params = {undefined, {undefined, "www.google.com", undefined}, "/search", ["foo"], undefined},
|
||||||
|
Expect = "http://www.google.com/search?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_variation8_test() ->
|
||||||
|
Params = {undefined, {undefined, "www.google.com", undefined}, undefined, ["foo"], undefined},
|
||||||
|
Expect = "http://www.google.com/?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_variation9_test() ->
|
||||||
|
Params = {undefined, {undefined, "www.google.com", undefined}, undefined, [], ""},
|
||||||
|
Expect = "http://www.google.com/",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_variation10_test() ->
|
||||||
|
Params = {undefined, {undefined, "www.google.com", undefined}, undefined, [], "foo"},
|
||||||
|
Expect = "http://www.google.com/#foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation1_test() ->
|
||||||
|
Params = {amqp, "guest", "password", "rabbitmq", 5672, "/%2f", [{"heartbeat", "5"}], undefined},
|
||||||
|
Expect = "amqp://guest:password@rabbitmq:5672/%2f?heartbeat=5",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation2_test() ->
|
||||||
|
Params = {http, undefined, "www.google.com", 80, "/search", [{"foo", "bar"}], "#baz"},
|
||||||
|
Expect = "http://www.google.com/search?foo=bar#baz",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation3_test() ->
|
||||||
|
Params = {https, undefined, "www.google.com", 443, "/search", undefined, undefined},
|
||||||
|
Expect = "https://www.google.com/search",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation4_test() ->
|
||||||
|
Params = {https, undefined, "www.google.com", 443, "/search", ["foo"], undefined},
|
||||||
|
Expect = "https://www.google.com/search?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation5_test() ->
|
||||||
|
Params = {https, "", "", "www.google.com", 443, "/search", ["foo"], undefined},
|
||||||
|
Expect = "https://www.google.com/search?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation6_test() ->
|
||||||
|
Params = {https, "bar", "", "www.google.com", 443, "/search", ["foo"], undefined},
|
||||||
|
Expect = "https://bar@www.google.com/search?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
build_url_variation7_test() ->
|
||||||
|
Params = {https, "bar", undefined, "www.google.com", 443, "/search", ["foo"], undefined},
|
||||||
|
Expect = "https://bar@www.google.com/search?foo",
|
||||||
|
?assertEqual(Expect, urilib:build(Params)).
|
||||||
|
|
||||||
|
parse_variation1_test() ->
|
||||||
|
URI = "amqp://guest:password@rabbitmq:5672/%2f?heartbeat=5",
|
||||||
|
Expect = {amqp, {{"guest", "password"}, "rabbitmq", 5672}, "/%2f", [{"heartbeat", "5"}], undefined},
|
||||||
|
?assertEqual(Expect, urilib:parse(URI)).
|
||||||
|
|
||||||
|
parse_variation2_test() ->
|
||||||
URI = "http://www.google.com/search?foo=bar#baz",
|
URI = "http://www.google.com/search?foo=bar#baz",
|
||||||
Expect = #uri{scheme=http,
|
Expect = {http, {undefined, "www.google.com", 80}, "/search", [{"foo", "bar"}], "#baz"},
|
||||||
userinfo=#userinfo{},
|
?assertEqual(Expect, urilib:parse(URI)).
|
||||||
authority=#authority{host="www.google.com", port=80},
|
|
||||||
path="/search",
|
|
||||||
query=[{"foo", "bar"}],
|
|
||||||
fragment="#baz"},
|
|
||||||
?assertEqual(Expect, urilib:parse_uri(URI)).
|
|
||||||
|
|
||||||
parse_uri_variation3_test() ->
|
parse_variation3_test() ->
|
||||||
URI = "https://www.google.com/search",
|
URI = "https://www.google.com/search",
|
||||||
Expect = #uri{scheme=https,
|
Expect = {https, {undefined, "www.google.com", 443}, "/search", undefined, undefined},
|
||||||
userinfo=#userinfo{},
|
?assertEqual(Expect, urilib:parse(URI)).
|
||||||
authority=#authority{host="www.google.com", port=443},
|
|
||||||
path="/search",
|
parse_variation4_test() ->
|
||||||
query=[],
|
URI = "https://www.google.com/search?foo",
|
||||||
fragment=[]},
|
Expect = {https, {undefined, "www.google.com", 443}, "/search", ["foo"], undefined},
|
||||||
?assertEqual(Expect, urilib:parse_uri(URI)).
|
?assertEqual(Expect, urilib:parse(URI)).
|
||||||
|
|
||||||
|
parse_uri_test() ->
|
||||||
|
URI = "amqp://guest:password@rabbitmq:5672/%2f?heartbeat=5",
|
||||||
|
Expect = {amqp, {{"guest", "password"}, "rabbitmq", 5672}, "/%2f",
|
||||||
|
[{"heartbeat", "5"}], undefined},
|
||||||
|
?assertEqual(Expect, urilib:parse(URI, uri)).
|
||||||
|
|
||||||
parse_url_variation1_test() ->
|
parse_url_variation1_test() ->
|
||||||
URL = "amqp://guest:rabbitmq@rabbitmq:5672/%2f?heartbeat=5",
|
URI = "amqp://guest:password@rabbitmq:5672/%2f?heartbeat=5&foo=bar&baz+corgie=qux+grault",
|
||||||
Expect = #url{scheme=amqp,
|
Expect = {amqp, "guest", "password", "rabbitmq", 5672, "/%2f",
|
||||||
username="guest",
|
[{"heartbeat", "5"}, {"foo", "bar"}, {"baz corgie", "qux grault"}],
|
||||||
password="rabbitmq",
|
undefined},
|
||||||
host="rabbitmq",
|
?assertEqual(Expect, urilib:parse(URI, url)).
|
||||||
port=5672,
|
|
||||||
path="/%2f",
|
|
||||||
query=[{"heartbeat", "5"}],
|
|
||||||
fragment=[]},
|
|
||||||
?assertEqual(Expect, urilib:parse_url(URL)).
|
|
||||||
|
|
||||||
parse_url_variation2_test() ->
|
parse_url_variation2_test() ->
|
||||||
URL = "http://www.google.com/search?foo=bar#baz",
|
URI = "amqp://guest@rabbitmq:5672/%2f?heartbeat=5&foo=bar&baz+corgie=qux+grault#foo",
|
||||||
Expect = #url{scheme=http,
|
Expect = {amqp, "guest", undefined, "rabbitmq", 5672, "/%2f",
|
||||||
username=undefined,
|
[{"heartbeat", "5"}, {"foo", "bar"}, {"baz corgie", "qux grault"}],
|
||||||
password=undefined,
|
"#foo"},
|
||||||
host="www.google.com",
|
?assertEqual(Expect, urilib:parse(URI, url)).
|
||||||
port=80,
|
|
||||||
path="/search",
|
|
||||||
query=[{"foo", "bar"}],
|
|
||||||
fragment="#baz"},
|
|
||||||
?assertEqual(Expect, urilib:parse_url(URL)).
|
|
||||||
|
|
||||||
parse_url_variation3_test() ->
|
percent_decode_test() ->
|
||||||
URL = "https://www.google.com/search",
|
Value = "foo%2fbar%20baz",
|
||||||
Expect = #url{scheme=https,
|
Expect = "foo/bar baz",
|
||||||
username=undefined,
|
?assertEqual(Expect, urilib:percent_decode(Value)).
|
||||||
password=undefined,
|
|
||||||
host="www.google.com",
|
plus_decode_test() ->
|
||||||
port=443,
|
Value = "foo/bar+baz",
|
||||||
path="/search",
|
Expect = "foo/bar baz",
|
||||||
query=[],
|
?assertEqual(Expect, urilib:plus_decode(Value)).
|
||||||
fragment=[]},
|
|
||||||
?assertEqual(Expect, urilib:parse_url(URL)).
|
percent_encode_test() ->
|
||||||
|
Value = "foo/bar baz",
|
||||||
|
Expect = "foo%2fbar%20baz",
|
||||||
|
?assertEqual(Expect, urilib:percent_encode(Value)).
|
||||||
|
|
||||||
|
percent_encode_unicode_test() ->
|
||||||
|
Value = "foo/bar✈baz",
|
||||||
|
Expect = "foo%2fbar%c0%88baz",
|
||||||
|
?assertEqual(Expect, urilib:percent_encode(Value)).
|
||||||
|
|
||||||
|
plus_encode_test() ->
|
||||||
|
Value = "foo/bar baz",
|
||||||
|
Expect = "foo%2fbar+baz",
|
||||||
|
?assertEqual(Expect, urilib:plus_encode(Value)).
|
||||||
|
|
Loading…
Reference in a new issue