]> git.tdb.fi Git - libs/net.git/blob - source/http/utils.cpp
Add a dynamic receiver class for more flexible packet handling
[libs/net.git] / source / http / utils.cpp
1 #include "utils.h"
2 #include <algorithm>
3 #include <msp/strings/format.h>
4 #include <msp/strings/regex.h>
5 #include <msp/strings/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         append(str, "?", url.query);
110         append(str, "#", url.fragment);
111         return str;
112 }
113
114 Query parse_query(const std::string &str)
115 {
116         Query query;
117         for(const string &p: split(str, '&'))
118         {
119                 string::size_type equals = p.find('=');
120                 string &value = query[urldecode(p.substr(0, equals))];
121                 if(equals!=string::npos)
122                         value = urldecode(p.substr(equals+1));
123         }
124         return query;
125 }
126
127 string build_query(const Query &query)
128 {
129         string str;
130         for(const auto &kvp: query)
131         {
132                 append(str, "&", urlencode_plus(kvp.first));
133                 str += '=';
134                 str += urlencode_plus(kvp.second);
135         }
136         return str;
137 }
138
139 } // namespace Http
140 } // namespace Msp