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