]> git.tdb.fi Git - libs/net.git/blob - source/http/message.cpp
Add a dynamic receiver class for more flexible packet handling
[libs/net.git] / source / http / message.cpp
1 #include "message.h"
2 #include <cstdlib>
3 #include <msp/core/maputils.h>
4 #include <msp/strings/format.h>
5 #include <msp/strings/utils.h>
6
7 using namespace std;
8
9 namespace Msp {
10 namespace Http {
11
12 void Message::set_header(const string &hdr, const string &val)
13 {
14         headers[normalize_header_name(hdr)] = val;
15 }
16
17 bool Message::has_header(const string &hdr) const
18 {
19         return headers.count(normalize_header_name(hdr));
20 }
21
22 const string &Message::get_header(const string &hdr) const
23 {
24         return get_item(headers, normalize_header_name(hdr));
25 }
26
27 void Message::add_content(const string &d)
28 {
29         content += d;
30         if(headers.count("Content-Type")==0)
31                 set_header("Content-Type", "text/plain");
32         set_header("Content-Length", lexical_cast<string>(content.size()));
33 }
34
35 void Message::set_user_data(const Variant &d)
36 {
37         user_data = d;
38 }
39
40 unsigned Message::parse_content(const string &d)
41 {
42         if(complete)
43                 return 0;
44
45         auto i = headers.find("Content-Length");
46         if(i!=headers.end())
47         {
48                 string::size_type needed = lexical_cast<string::size_type>(i->second)-content.size();
49                 string::size_type len = min(needed, d.size());
50                 
51                 content.append(d, 0, len);
52
53                 if(len==needed)
54                         complete = true;
55                 
56                 return len;
57         }
58
59         i = headers.find("Transfer-Encoding");
60         if(i!=headers.end() && strcasecmp(i->second, "chunked")==0)
61         {
62                 string::size_type pos = 0;
63                 while(!complete && pos<d.size())
64                 {
65                         if(chunk_length==0)
66                         {
67                                 string::size_type lf = d.find('\n', pos);
68                                 if(lf==string::npos)
69                                         return pos;
70                                 chunk_length = lexical_cast<unsigned>(strip(d.substr(pos, lf-pos)), "x");
71                                 if(chunk_length==0)
72                                         complete = true;
73                                 pos = lf+1;
74                         }
75                         else
76                         {
77                                 string::size_type len = min(chunk_length, d.size()-pos);
78                                 content.append(d, pos, len);
79                                 chunk_length -= len;
80                                 if((pos = d.find('\n', pos+len))!=string::npos)
81                                         ++pos;
82                         }
83                 }
84
85                 return pos;
86         }
87
88         complete = true;
89         return 0;
90 }
91
92 unsigned Message::parse_headers(const string &d)
93 {
94         string::size_type start = 0;
95         while(1)
96         {
97                 string::size_type lf = d.find('\n', start);
98                 if(lf==string::npos)
99                         throw invalid_argument("Message::parse_headers");
100                 if(lf==start || (d[start]=='\r' && lf==start+1))
101                         return lf+1;
102
103                 string::size_type colon = d.find(':', start);
104                 if(colon>lf)
105                         throw invalid_argument("Message::parse_headers");
106
107                 set_header(d.substr(start, colon-start), strip(d.substr(colon+1, lf-colon-1)));
108
109                 start = lf+1;
110         }
111 }
112
113 string Message::str_common() const
114 {
115         string result;
116
117         for(auto &kvp: headers)
118                 if(kvp.first[0]!='-')
119                         result += format("%s: %s\r\n", kvp.first, kvp.second);
120         result += "\r\n";
121         result += content;
122
123         return result;
124 }
125
126 string Message::normalize_header_name(const string &hdr) const
127 {
128         string result = hdr;
129         bool upper = true;
130         for(char &c: result)
131         {
132                 if(c=='-')
133                         upper = true;
134                 else if(upper)
135                 {
136                         c = toupper(static_cast<unsigned char>(c));
137                         upper = false;
138                 }
139                 else
140                         c = tolower(static_cast<unsigned char>(c));
141         }
142         return result;
143 }
144
145 } // namespace Http
146 } // namespace Msp