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