]> git.tdb.fi Git - libs/net.git/blob - source/http/utils.cpp
8662c2ec4176c452472d1c6984fd13f489b70d1f
[libs/net.git] / source / http / utils.cpp
1 #include <algorithm>
2 #include <msp/strings/format.h>
3 #include <msp/strings/regex.h>
4 #include <msp/strings/utils.h>
5 #include "utils.h"
6
7 using namespace std;
8
9 namespace {
10
11 const char *reserved[]=
12 {
13         " #%&+=?",
14         " #%&*+:;=?@[]",
15         " !#$%&'()*+,/:;=?@[]",
16 };
17
18 bool is_reserved(char c, unsigned level)
19 {
20         for(const char *r=reserved[level]; *r; ++r)
21                 if(c==*r)
22                         return true;
23         return false;
24 }
25
26 }
27
28 namespace Msp {
29 namespace Http {
30
31 string urlencode(const string &str, EncodeLevel level)
32 {
33         string result;
34         result.reserve(str.size());
35         for(char c: str)
36         {
37                 if(is_reserved(c, level))
38                         result += format("%%%02X", c);
39                 else
40                         result += c;
41         }
42         return result;
43 }
44
45 string urlencode_plus(const string &str, EncodeLevel level)
46 {
47         string result;
48         result.reserve(str.size());
49         for(char c: str)
50         {
51                 if(c==' ')
52                         result += '+';
53                 else if(is_reserved(c, level))
54                         result += format("%%%02X", c);
55                 else
56                         result += c;
57         }
58         return result;
59 }
60
61 string urldecode(const string &str)
62 {
63         string result;
64         for(unsigned i=0; i<str.size(); ++i)
65         {
66                 char c = str[i];
67                 if(c=='%')
68                 {
69                         if(i+3>str.size())
70                                 throw invalid_argument("urldecode");
71                         result += lexical_cast<unsigned char>(str.substr(i+1, 2), "x");
72                         i += 2;
73                 }
74                 else if(c=='+')
75                         result += ' ';
76                 else
77                         result += c;
78         }
79         return result;
80 }
81
82 Url parse_url(const string &str)
83 {
84         static Regex r_url("^(([a-z]+)://)?([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(:[0-9]+)?)?(/[^?#]*)?(\\?([^#]*))?(#(.*))?$");
85         if(RegMatch m = r_url.match(str))
86         {
87                 Url url;
88                 url.scheme = m[2].str;
89                 url.host = m[3].str;
90                 url.path = urldecode(m[6].str);
91                 url.query = m[8].str;
92                 url.fragment = m[10].str;
93                 return url;
94         }
95         else
96                 throw invalid_argument("parse_url");
97 }
98
99 string build_url(const Url &url)
100 {
101         if(!url.path.empty() && url.path[0]!='/')
102                 throw invalid_argument("build_url");
103
104         string str;
105         if(!url.scheme.empty())
106                 str += url.scheme+"://";
107         str += url.host;
108         str += urlencode(url.path);
109         if(!url.query.empty())
110         {
111                 str += '?';
112                 str += url.query;
113         }
114         if(!url.fragment.empty())
115         {
116                 str += '#';
117                 str += url.fragment;
118         }
119         return str;
120 }
121
122 Query parse_query(const std::string &str)
123 {
124         Query query;
125         for(const string &p: split(str, '&'))
126         {
127                 string::size_type equals = p.find('=');
128                 string &value = query[urldecode(p.substr(0, equals))];
129                 if(equals!=string::npos)
130                         value = urldecode(p.substr(equals+1));
131         }
132         return query;
133 }
134
135 string build_query(const Query &query)
136 {
137         string str;
138         for(const auto &kvp: query)
139         {
140                 if(!str.empty())
141                         str += '&';
142                 str += urlencode_plus(kvp.first);
143                 str += '=';
144                 str += urlencode_plus(kvp.second);
145         }
146         return str;
147 }
148
149 } // namespace Http
150 } // namespace Msp