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