1 /***************************************************************************
3 * Copyright (C) 2006 David Brodsky *
5 * This library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Library General Public *
7 * License as published by the Free Software Foundation and appearing *
8 * in the file LICENSE.LGPL included in the packaging of this file. *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * Library General Public License for more details. *
15 ***************************************************************************/
20 #include <tairon/core/log.h>
21 #include <tairon/core/thread.h>
22 #include <tairon/net/socket.h>
24 #include "httpclient.h"
26 /* {{{ static const char hexTable[] */
27 static const char hexTable
[] = {
28 '0', '1', '2', '3', '4', '5', '6', '7',
29 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
39 /* {{{ HTTPClient::HTTPClient(const String &, uint16_t */
40 HTTPClient::HTTPClient(const String
&h
, uint16_t p
) : host(h
), port(p
), socket(0)
45 /* {{{ HTTPClient::~HTTPClient() */
46 HTTPClient::~HTTPClient()
51 /* {{{ HTTPClient::clearParameters() */
52 void HTTPClient::clearParameters()
58 /* {{{ HTTPClient::close() */
59 void HTTPClient::close()
61 if (!socket
) // there is nothing to do
69 /* {{{ HTTPClient::connected(Tairon::Net::Socket *) */
70 void HTTPClient::connected(Tairon::Net::Socket
*)
72 uri
+= prepareParameters();
74 socket
->write("GET " + uri
+ " HTTP/1.0\r\nConnection: close\r\nHost: " + host
+ "\r\n\r\n");
76 dataReader
= &HTTPClient::readStatus
;
80 /* {{{ HTTPClient::delParameter(const String &) */
81 void HTTPClient::delParameter(const String
&key
)
83 parameters
.erase(key
);
87 /* {{{ HTTPClient::error(Tairon::Core::Socket *, int) */
88 void HTTPClient::error(Tairon::Net::Socket
*, int err
)
91 errorSignal
.emit(this, "Socket error", err
);
95 /* {{{ HTTPClient::escape(const String &) */
96 String
HTTPClient::escape(const String
&what
)
98 unsigned int len
= what
.length();
100 out
.reserve(len
* 3);
102 for (unsigned int i
= 0; i
< len
; ++i
)
103 if (isalnum(what
[i
]) || (what
[i
] == '-'))
106 out
+= String("%", 1) + hexTable
[(what
[i
] >> 4) & 0x0f] + hexTable
[what
[i
] & 0x0F];
112 /* {{{ HTTPClient::get(const String &) */
113 void HTTPClient::get(const String
&what
)
116 WARNING("Trying to get data when there is an active socket");
122 // clear data, it may contain something from previous request
125 Tairon::Core::Thread
*current
= Tairon::Core::Thread::current();
127 socket
= new Socket(Socket::Stream
);
128 socket
->connectedSignal
.connect(Tairon::Core::threadMethodFunctor(current
, this, &HTTPClient::connected
));
129 socket
->errorSignal
.connect(Tairon::Core::threadMethodFunctor(current
, this, &HTTPClient::error
));
130 socket
->readyReadSignal
.connect(Tairon::Core::threadMethodFunctor(current
, this, &HTTPClient::readyRead
));
131 socket
->connect(host
, port
);
135 /* {{{ HTTPClient::isActive() */
136 bool HTTPClient::isActive()
142 /* {{{ HTTPClient::prepareParameters() */
143 String
HTTPClient::prepareParameters()
146 for (std::map
<String
, String
>::const_iterator it
= parameters
.begin(); it
!= parameters
.end(); ++it
)
147 ret
+= it
->first
+ "=" + it
->second
+ "&";
149 ret
.erase(ret
.length() - 1); // remove trailing &
155 /* {{{ HTTPClient::readData() */
156 void HTTPClient::readData()
158 // just empty function, body of a message from the server is incoming and
159 // there is no need to parse it, the data is already stored
163 /* {{{ HTTPClient::readHeader() */
164 void HTTPClient::readHeader()
167 while (data
.canReadLine()) {
168 line
= data
.readLine();
171 dataReader
= &HTTPClient::readData
;
178 /* {{{ HTTPClient::readStatus() */
179 void HTTPClient::readStatus()
181 String line
= data
.readLine();
187 std::stringstream
s(line
);
194 if (protocol
.find("HTTP") != 0) // unsupported protocol
195 throw "Unsupported protocol";
197 if (code
!= 200) // invalid return code
198 throw "Invalid return code";
199 // TODO: handle redirect codes
201 dataReader
= &HTTPClient::readHeader
;
202 readHeader(); // there may be another data, process them
206 /* {{{ HTTPClient::readyRead(Tairon::Net::Socket *) */
207 void HTTPClient::readyRead(Tairon::Net::Socket
*)
210 data
+= socket
->read();
211 } catch (const Tairon::Net::SocketException
&) {
212 // connection has been closed or some other error occured
217 (this->*dataReader
)();
218 if (!socket
) // connection has been closed
219 dataReadSignal
.emit(this);
220 } catch (const char *e
) { // invalid data received
222 errorSignal
.emit(this, e
, 0);
227 /* {{{ HTTPClient::setParameter(const String &, const String &) */
228 void HTTPClient::setParameter(const String
&key
, const String
&value
)
230 parameters
[key
] = escape(value
);
236 }; // namespace Tairon
238 // vim: ai sw=4 ts=4 noet fdm=marker