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