httpclnt.cpp

Go to the documentation of this file.
00001 
00002  //
00003  // XMLStorage XML-RPC client extension
00004  //
00005  // Copyright (c) 2009 Martin Fuchs <martin-fuchs@gmx.net>
00006  //
00007 
00008 
00009 /*
00010 
00011   All rights reserved.
00012 
00013   Redistribution and use in source and binary forms, with or without
00014   modification, are permitted provided that the following conditions are met:
00015 
00016   * Redistributions of source code must retain the above copyright
00017     notice, this list of conditions and the following disclaimer.
00018   * Redistributions in binary form must reproduce the above copyright
00019     notice, this list of conditions and the following disclaimer in
00020     the documentation and/or other materials provided with the
00021     distribution.
00022 
00023   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00027   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00029   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00030   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00031   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00032   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033   POSSIBILITY OF SUCH DAMAGE.
00034 
00035 */
00036 
00037 #include "httpclnt.h"
00038 
00039 #pragma comment(lib, "wininet")
00040 
00041 
00042 namespace XMLRPC {
00043 
00044 
00045 XMLRPCClient::XMLRPCClient(LPCTSTR url, LPCTSTR agent)
00046  :  _extensions(false),
00047     _parsed_url(url),
00048 #ifdef USE_WININET
00049     _inet_lib(agent),
00050     _connection(_inet_lib._hinet, _parsed_url)
00051 #else
00052     _connection(agent, _parsed_url)
00053 #endif
00054 {
00055      // resolve the server and service name
00056     _addr.resolve(_parsed_url._hostname.c_str(), _parsed_url._port.c_str(), 0);
00057 }
00058 
00059 
00060 #ifdef USE_WININET
00061 
00062  // send a HTTP POST request
00063 string XMLRPCClient::request_post(const string& in)
00064 {
00065     HTTPRequest request(_connection, _parsed_url.get_path(), "POST");
00066 
00067     return request.send(in.c_str(), in.length());
00068 }
00069 
00070 string HTTPRequest::send(const void* data, size_t len)
00071 {
00072     if (!HttpSendRequest(_hRequest, NULL, 0, (void*)data, len))
00073         throw LIE();
00074 
00075     string ret;
00076 
00077      // query the content length
00078     char bufQuery[32];
00079     DWORD dwLengthBufQuery = sizeof(bufQuery);
00080 
00081     if (HttpQueryInfo(_hRequest, HTTP_QUERY_CONTENT_LENGTH, bufQuery, &dwLengthBufQuery, NULL)) {
00082          // allocate a buffer with the content length we got as ASCII string
00083         DWORD content_length = (DWORD) atol(bufQuery);
00084 
00085         ret.resize(content_length);
00086 
00087         DWORD read;
00088 
00089         if (!InternetReadFile(_hRequest, &ret.at(0), content_length, &read))
00090             throw LIE();
00091     } else {
00092          // If there is no content length available, we use a loop to fetch the response data.
00093         TCHAR buffer[BUFFER_LEN];
00094         DWORD read;
00095 
00096         for(;;) {
00097             if (!InternetReadFile(_hRequest, buffer, sizeof(buffer), &read))
00098                 throw LIE();
00099 
00100             if (!read)
00101                 break;
00102 
00103             ret.append(buffer, read);
00104         }
00105     }
00106 
00107     return ret;
00108 }
00109 
00110 #else
00111 
00112  // send a HTTP POST request
00113 string XMLRPCClient::request_post(const string& in)
00114 {
00115     return _connection.send_request(_parsed_url, _addr, "POST", in.c_str(), in.length(), !_extensions);
00116 }
00117 
00118 
00119 HTTPConnection::HTTPConnection(const char* agent, const URL& url)
00120  :  _agent(agent),
00121     _url(url),
00122     _pConnect(NULL)
00123 {
00124 }
00125 
00126 Socket& HTTPConnection::get_socket(const AddressList& addr)
00127 {
00128     if (!_pConnect)
00129         _pConnect = new INConnect(addr);
00130 
00131     return *_pConnect;
00132 }
00133 
00134 void HTTPConnection::close()
00135 {
00136     if (_pConnect) {
00137         delete _pConnect;
00138         _pConnect = NULL;
00139     }
00140 }
00141 
00142 
00143 string HTTPConnection::send_request(const URL& url, const AddressList& addr, const char* verb, const char* data, size_t len, bool close_connection)
00144 {
00145     Socket& socket = get_socket(addr);
00146 
00147      // send the HTML request header
00148     ostringstream buffer;
00149 
00150     buffer << verb << " " << url._path << " HTTP/1.1\r\n";
00151     buffer << "User-Agent: " << _agent << "\r\n";
00152     buffer << "Host: " << url._hostname << ":" << url._port << "\r\n";
00153     buffer << "Content-length: " << len << "\r\n";
00154     buffer << "Content-type: text/xml\r\n";
00155 //  buffer << "Cache-Control: no-cache\r\n";
00156     buffer << "\r\n";
00157 
00158     const string& header = buffer.str();
00159     socket.send(header.c_str(), header.length());
00160 
00161      // send the request body
00162     socket.send(data, len);
00163 
00164 
00165      // read response line
00166     SocketReader reader(socket);
00167 
00168     char line[BUFFER_LEN];
00169 
00170     if (reader.read_line(line, BUFFER_LEN) <= 0)
00171         throw Exception("missing HTML server response");
00172 
00173     char version[12];
00174     int code;
00175     char msg[BUFFER_LEN];
00176 
00177 #ifdef __STDC_WANT_SECURE_LIB__
00178     if (sscanf_s(line, "%s %d %s", version, _countof(version), &code, msg, _countof(msg)) != 3)
00179 #else
00180     if (sscanf(line, "%s %d %s", version, &code, msg) != 3)
00181 #endif
00182         throw Exception("invalid HTML server response");
00183 
00184     if (code != 200)
00185         throw Exception(line);
00186 
00187 
00188      // read the response header
00189     int content_length = 0;
00190     string content_type;
00191 
00192     for(;;) {
00193         if (reader.read_line(line, BUFFER_LEN) <= 0)
00194             break;
00195 
00196         if (!_strnicmp(line, "Content-length:", 15))
00197             content_length = atoi(line+15);
00198         else if (!_strnicmp(line, "Content-type:", 13))
00199             content_type = line+14;
00200         else if (!_stricmp(line, "Connection: close"))
00201             close_connection = true;
00202     }
00203 
00204     if (!content_type.empty() && _strnicmp(content_type.c_str(),"text/xml",8)) {
00205         char b[BUFFER_LEN];
00206         sprintf(b, "illegal content type: %s", content_type.c_str());
00207         throw Exception(b);
00208     }
00209 
00210 
00211      // read the response body
00212     string ret;
00213 
00214     if (content_length) {
00215         ret.resize(content_length);
00216         reader.read(&ret.at(0), content_length);
00217     }
00218 
00219     if (close_connection)
00220         close();
00221 
00222     return ret;
00223 }
00224 
00225 #endif
00226 
00227 
00228 } // namespace XMLRPC
00229 
00230 
00231 /*
00232 #include <iostream>
00233 
00234 int main()
00235 {
00236     try {
00237         XMLRPCClient client(TEXT("XMLStorage XMLRPC"));
00238 
00239         const char* url = "http://localhost:8080/";
00240 
00241         for(int i=0; i<10; ++i) {
00242             out = client.request_post(url, "ABC=123");
00243 
00244 #ifdef _DEBUG
00245             cout << "POST -> " << out << endl;
00246 #endif
00247         }
00248     } catch(exception& e) {
00249         cerr << e.what() << endl;
00250     }
00251 
00252     return 0;
00253 }
00254 */